diff --git a/datahub-web-react/knip.json b/datahub-web-react/knip.json new file mode 100644 index 00000000000000..7b5f4b0defdd06 --- /dev/null +++ b/datahub-web-react/knip.json @@ -0,0 +1,3 @@ +{ + "ignore": ["src/generated.ts", "src/alchemy-components/**", "functions/**", ".storybook/**"] +} diff --git a/datahub-web-react/src/CustomThemeProvider.tsx b/datahub-web-react/src/CustomThemeProvider.tsx index fc0b091b7723fe..4b547030aca41d 100644 --- a/datahub-web-react/src/CustomThemeProvider.tsx +++ b/datahub-web-react/src/CustomThemeProvider.tsx @@ -1,7 +1,6 @@ import React, { useState } from 'react'; import { ThemeProvider } from 'styled-components'; -import { useIsThemeV2 } from '@app/useIsThemeV2'; import { useCustomThemeId } from '@app/useSetAppTheme'; import themes from '@conf/theme/themes'; import { Theme } from '@conf/theme/types'; @@ -12,12 +11,9 @@ interface Props { } const CustomThemeProvider = ({ children }: Props) => { - // Note: AppConfigContext not provided yet, so both of these calls rely on the DEFAULT_APP_CONFIG - const isThemeV2 = useIsThemeV2(); const customThemeId = useCustomThemeId(); - // Note: If custom theme id is a json file, it will only be loaded later in useSetAppTheme - const defaultTheme = isThemeV2 ? themes.themeV2 : themes.themeV1; + const defaultTheme = themes.themeV2; const customTheme = customThemeId ? themes[customThemeId] : null; const [theme, setTheme] = useState(customTheme ?? defaultTheme); diff --git a/datahub-web-react/src/Mocks.tsx b/datahub-web-react/src/Mocks.tsx index 774c6cc79eabf0..c70d9f130086ed 100644 --- a/datahub-web-react/src/Mocks.tsx +++ b/datahub-web-react/src/Mocks.tsx @@ -5,7 +5,6 @@ import { VIEW_ENTITY_PAGE } from '@app/entity/shared/constants'; import { GenericEntityProperties } from '@app/entity/shared/types'; import { ViewBuilderState } from '@app/entity/view/types'; import { EntityCapabilityType } from '@app/entityV2/Entity'; -import { FetchedEntity } from '@app/lineage/types'; import { DEFAULT_APP_CONFIG } from '@src/appConfigContext'; import { AppConfigDocument, GetEntityCountsDocument } from '@graphql/app.generated'; @@ -32,8 +31,6 @@ import { GetTagDocument } from '@graphql/tag.generated'; import { GetUserDocument } from '@graphql/user.generated'; import { AppConfig, - BusinessAttribute, - Container, DataFlow, DataHubView, DataHubViewFilter, @@ -53,7 +50,6 @@ import { MlModelGroup, Owner, OwnershipType, - PlatformPrivileges, PlatformType, RecommendationRenderType, RelationshipDirection, @@ -62,7 +58,7 @@ import { SearchResult, } from '@types'; -export const entityPrivileges: EntityPrivileges = { +const entityPrivileges: EntityPrivileges = { canEditLineage: true, canEditDomains: true, canEditDataProducts: true, @@ -727,7 +723,7 @@ export const dataset3 = { versionProperties: null, } as Dataset; -export const dataset3WithSchema = { +const dataset3WithSchema = { dataset: { __typename: 'Dataset', schemaMetadata: { @@ -793,7 +789,7 @@ export const dataset3WithSchema = { }, }; -export const dataset4 = { +const dataset4 = { ...dataset3, name: 'Fourth Test Dataset', urn: 'urn:li:dataset:4', @@ -806,7 +802,7 @@ export const dataset4 = { }, }; -export const dataset5 = { +const dataset5 = { ...dataset3, name: 'Fifth Test Dataset', urn: 'urn:li:dataset:5', @@ -820,7 +816,7 @@ export const dataset5 = { }, }; -export const dataset6 = { +const dataset6 = { ...dataset3, name: 'Sixth Test Dataset', urn: 'urn:li:dataset:6', @@ -835,258 +831,6 @@ export const dataset6 = { }, }; -export const dataset7 = { - ...dataset3, - name: 'Seventh Test Dataset', - urn: 'urn:li:dataset:7', - properties: { - name: 'Seventh Test Dataset', - description: 'This and here we have yet another Dataset (YAN). Are there more?', - origin: 'PROD', - customProperties: [{ key: 'propertyAKey', value: 'propertyAValue' }], - externalUrl: 'https://data.hub', - }, -}; - -export const dataset3WithLineage = { - ...dataset3, - upstream: { - start: 0, - count: 2, - total: 2, - relationships: [ - { - type: 'DownstreamOf', - direction: RelationshipDirection.Outgoing, - entity: dataset7, - }, - { - type: 'DownstreamOf', - direction: RelationshipDirection.Outgoing, - entity: dataset4, - }, - ], - }, - downstream: { - start: 0, - count: 0, - total: 0, - relationships: [], - }, -}; - -export const dataset4WithLineage = { - ...dataset4, - upstream: { - start: 0, - count: 2, - total: 2, - relationships: [ - { - type: 'DownstreamOf', - direction: RelationshipDirection.Outgoing, - entity: dataset6, - }, - { - type: 'DownstreamOf', - direction: RelationshipDirection.Outgoing, - entity: dataset5, - }, - ], - }, - downstream: { - start: 0, - count: 1, - total: 1, - relationships: [ - { - type: 'DownstreamOf', - direction: RelationshipDirection.Incoming, - entity: dataset3, - }, - ], - }, -}; - -export const dataset5WithCyclicalLineage = { - ...dataset5, - upstream: { - start: 0, - count: 1, - total: 1, - relationships: [ - { - type: 'DownstreamOf', - direction: RelationshipDirection.Outgoing, - entity: dataset3, - }, - ], - }, - downstream: { - start: 0, - count: 1, - total: 1, - relationships: [ - { - type: 'DownstreamOf', - direction: RelationshipDirection.Incoming, - entity: dataset7, - }, - ], - }, -}; - -export const dataset5WithLineage = { - ...dataset5, - upstream: null, - downstream: { - start: 0, - count: 3, - total: 3, - relationships: [ - { - type: 'DownstreamOf', - direction: RelationshipDirection.Incoming, - entity: dataset7, - }, - { - type: 'DownstreamOf', - direction: RelationshipDirection.Incoming, - entity: dataset6, - }, - { - type: 'DownstreamOf', - direction: RelationshipDirection.Incoming, - entity: dataset4, - }, - ], - }, -}; - -export const dataset6WithLineage = { - ...dataset6, - upstream: { - start: 0, - count: 1, - total: 1, - relationships: [ - { - type: 'DownstreamOf', - direction: RelationshipDirection.Outgoing, - entity: dataset5, - }, - ], - }, - downstream: { - start: 0, - count: 1, - total: 1, - relationships: [ - { - type: 'DownstreamOf', - direction: RelationshipDirection.Incoming, - entity: dataset4, - }, - ], - }, -}; - -export const dataset7WithLineage = { - ...dataset7, - upstream: { - start: 0, - count: 1, - total: 1, - relationships: [ - { - type: 'DownstreamOf', - direction: RelationshipDirection.Outgoing, - entity: dataset5, - }, - ], - }, - downstream: { - start: 0, - count: 1, - total: 1, - relationships: [ - { - type: 'DownstreamOf', - direction: RelationshipDirection.Incoming, - entity: dataset3, - }, - ], - }, -}; - -export const dataset7WithSelfReferentialLineage = { - ...dataset7, - upstream: { - start: 0, - count: 2, - total: 2, - relationships: [ - { - type: 'DownstreamOf', - direction: RelationshipDirection.Outgoing, - entity: dataset5, - }, - { - type: 'DownstreamOf', - direction: RelationshipDirection.Outgoing, - entity: dataset7, - }, - ], - }, - downstream: { - start: 0, - count: 2, - total: 2, - relationships: [ - { - type: 'DownstreamOf', - direction: RelationshipDirection.Incoming, - entity: dataset3, - }, - { - type: 'DownstreamOf', - direction: RelationshipDirection.Incoming, - entity: dataset7, - }, - ], - }, -}; - -export const container1 = { - urn: 'urn:li:container:DATABASE', - type: EntityType.Container, - platform: dataPlatform, - lastIngested: null, - exists: true, - properties: { - name: 'database1', - externalUrl: null, - __typename: 'ContainerProperties', - }, - autoRenderAspects: [], - __typename: 'Container', -} as Container; - -export const container2 = { - urn: 'urn:li:container:SCHEMA', - type: EntityType.Container, - platform: dataPlatform, - lastIngested: null, - exists: true, - properties: { - name: 'schema1', - externalUrl: null, - __typename: 'ContainerProperties', - }, - autoRenderAspects: [], - __typename: 'Container', -} as Container; - export const glossaryTerm1 = { urn: 'urn:li:glossaryTerm:1', type: EntityType.GlossaryTerm, @@ -1211,7 +955,7 @@ const glossaryTerm2 = { __typename: 'GlossaryTerm', }; -export const glossaryTerm3 = { +const glossaryTerm3 = { urn: 'urn:li:glossaryTerm:example.glossaryterm2', type: 'GLOSSARY_TERM', name: 'glossaryterm2', @@ -1330,19 +1074,6 @@ export const glossaryNode4 = { __typename: 'GlossaryNode', } as GlossaryNode; -export const glossaryNode5 = { - urn: 'urn:li:glossaryNode:example.glossarynode5', - type: 'GLOSSARY_NODE', - properties: { - name: 'Glossary Node 5', - }, - parentNodes: { - count: 1, - nodes: [glossaryNode4], - }, - __typename: 'GlossaryNode', -} as GlossaryNode; - export const sampleTag = { urn: 'urn:li:tag:abc-sample-tag', type: EntityType.Tag, @@ -1375,7 +1106,7 @@ export const sampleTag = { autoRenderAspects: [], }; -export const dataFlow1 = { +const dataFlow1 = { __typename: 'DataFlow', urn: 'urn:li:dataFlow:1', type: EntityType.DataFlow, @@ -1545,107 +1276,6 @@ export const dataJob1 = { health: [], } as DataJob; -export const businessAttribute = { - urn: 'urn:li:businessAttribute:ba1', - type: EntityType.BusinessAttribute, - __typename: 'BusinessAttribute', - properties: { - name: 'TestBusinessAtt-2', - description: 'lorem upsum updated 12', - created: { - time: 1705857132786, - }, - lastModified: { - time: 1705857132786, - }, - glossaryTerms: { - terms: [ - { - term: { - urn: 'urn:li:glossaryTerm:1', - type: EntityType.GlossaryTerm, - hierarchicalName: 'SampleHierarchicalName', - name: 'SampleName', - }, - attribution: null, - associatedUrn: 'urn:li:businessAttribute:ba1', - }, - ], - __typename: 'GlossaryTerms', - }, - tags: { - __typename: 'GlobalTags', - tags: [ - { - tag: { - urn: 'urn:li:tag:abc-sample-tag', - __typename: 'Tag', - type: EntityType.Tag, - name: 'abc-sample-tag', - }, - __typename: 'TagAssociation', - associatedUrn: 'urn:li:businessAttribute:ba1', - attribution: null, - }, - { - tag: { - urn: 'urn:li:tag:TestTag', - __typename: 'Tag', - type: EntityType.Tag, - name: 'TestTag', - }, - __typename: 'TagAssociation', - associatedUrn: 'urn:li:businessAttribute:ba1', - attribution: null, - }, - ], - }, - customProperties: [ - { - key: 'prop2', - value: 'val2', - associatedUrn: 'urn:li:businessAttribute:ba1', - __typename: 'CustomPropertiesEntry', - }, - { - key: 'prop1', - value: 'val1', - associatedUrn: 'urn:li:businessAttribute:ba1', - __typename: 'CustomPropertiesEntry', - }, - { - key: 'prop3', - value: 'val3', - associatedUrn: 'urn:li:businessAttribute:ba1', - __typename: 'CustomPropertiesEntry', - }, - ], - }, - ownership: { - owners: [ - { - owner: { - ...user1, - }, - associatedUrn: 'urn:li:businessAttribute:ba', - type: 'DATAOWNER', - attribution: null, - }, - { - owner: { - ...user2, - }, - associatedUrn: 'urn:li:businessAttribute:ba', - type: 'DELEGATE', - attribution: null, - }, - ], - lastModified: { - time: 0, - }, - }, -} as BusinessAttribute; - export const dataJob2 = { __typename: 'DataJob', urn: 'urn:li:dataJob:2', @@ -1799,7 +1429,7 @@ export const dataJob3 = { health: [], } as DataJob; -export const mlModel = { +const mlModel = { __typename: 'MLModel', urn: 'urn:li:mlModel:(urn:li:dataPlatform:sagemaker,trustmodel,PROD)', type: EntityType.Mlmodel, @@ -1885,29 +1515,7 @@ export const mlModel = { autoRenderAspects: [], } as MlModel; -export const dataset1FetchedEntity = { - urn: dataset1.urn, - name: dataset1.name, - type: dataset1.type, - upstreamChildren: [], - downstreamChildren: [ - { type: EntityType.Dataset, entity: dataset2 }, - { type: EntityType.DataJob, entity: dataJob1 }, - ], -} as FetchedEntity; - -export const dataset2FetchedEntity = { - urn: dataset2.urn, - name: 'test name', - type: dataset2.type, - upstreamChildren: [ - { type: EntityType.Dataset, entity: dataset1 }, - { type: EntityType.DataJob, entity: dataJob1 }, - ], - downstreamChildren: [], -} as FetchedEntity; - -export const mlModelGroup = { +const mlModelGroup = { __typename: 'MLModelGroup', urn: 'urn:li:mlModelGroup:(urn:li:dataPlatform:sagemaker,another-group,PROD)', type: EntityType.MlmodelGroup, @@ -1976,7 +1584,7 @@ export const mlModelGroup = { deprecation: null, } as MlModelGroup; -export const recommendationModules = [ +const recommendationModules = [ { title: 'Most Popular', moduleId: 'MostPopular', @@ -4053,35 +3661,6 @@ export const mocksWithSearchFlagsOff = [ }, ]; -export const platformPrivileges: PlatformPrivileges = { - viewAnalytics: true, - managePolicies: true, - manageIdentities: true, - generatePersonalAccessTokens: true, - manageDomains: true, - manageIngestion: true, - manageSecrets: true, - manageTokens: true, - viewTests: false, - manageTests: true, - manageGlossaries: true, - manageUserCredentials: true, - manageTags: true, - viewManageTags: true, - createTags: true, - createDomains: true, - manageGlobalViews: true, - manageOwnershipTypes: true, - manageGlobalAnnouncements: true, - createBusinessAttributes: true, - manageBusinessAttributes: true, - manageStructuredProperties: true, - viewStructuredPropertiesPage: true, - manageApplications: true, - manageFeatures: true, - manageHomePageTemplates: true, -}; - export const DomainMock1 = { urn: 'urn:li:domain:afbdad41-c523-469f-9b62-de94f938f702', id: 'afbdad41-c523-469f-9b62-de94f938f702', @@ -4117,7 +3696,7 @@ export const DomainMock1 = { supportedCapabilities: () => new Set(), } as Entity; -export const DomainMock2 = { +const DomainMock2 = { urn: 'urn:li:domain:bebdad41-c523-469f-9b62-de94f938f603', id: 'bebdad41-c523-469f-9b62-de94f938f603', type: 'DOMAIN', @@ -4404,25 +3983,3 @@ export const mockFineGrainedLineages1: GenericEntityProperties = { }, ], }; - -export const useEntityDataFunc = () => { - const value = { - entityData: { - parentContainers: { - containers: [ - { - properties: { - name: 'name1', - }, - }, - { - properties: { - name: 'name2', - }, - }, - ], - }, - }, - }; - return value; -}; diff --git a/datahub-web-react/src/app/AdminConsole.tsx b/datahub-web-react/src/app/AdminConsole.tsx deleted file mode 100644 index fba4ea98ee3d10..00000000000000 --- a/datahub-web-react/src/app/AdminConsole.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { BankOutlined, BarChartOutlined, MenuOutlined } from '@ant-design/icons'; -import { Menu } from 'antd'; -import Sider from 'antd/lib/layout/Sider'; -import React, { useState } from 'react'; -import { Link } from 'react-router-dom'; -import styled from 'styled-components'; - -import { useUserContext } from '@app/context/useUserContext'; -import { ANTD_GRAY } from '@app/entity/shared/constants'; -import { useAppConfig } from '@app/useAppConfig'; - -const ToggleContainer = styled.div` - background-color: ${ANTD_GRAY[4]}; - border-top-right-radius: 2px; - border-bottom-right-radius: 2px; -`; - -const ControlMenu = styled(Menu)` - padding-top: 28px; - height: 100%; -`; - -const ControlSlideOut = styled(Sider)` - && { - height: 100vh; - position: fixed; - left: 0px; - z-index: 10000; - } -`; - -/** - * Container for all views behind an authentication wall. - */ -export const AdminConsole = (): JSX.Element => { - const me = useUserContext(); - - const [adminConsoleOpen, setAdminConsoleOpen] = useState(false); - const { config } = useAppConfig(); - - const isAnalyticsEnabled = config?.analyticsConfig?.enabled; - const isPoliciesEnabled = config?.policiesConfig?.enabled; - - const showAnalytics = (isAnalyticsEnabled && me && me?.platformPrivileges?.viewAnalytics) || false; - const showPolicyBuilder = (isPoliciesEnabled && me && me?.platformPrivileges?.managePolicies) || false; - const showAdminConsole = showAnalytics || showPolicyBuilder; - - const onMenuItemClick = () => { - setAdminConsoleOpen(false); - }; - - const onCollapse = (collapsed) => { - if (collapsed) { - setAdminConsoleOpen(false); - } else { - setAdminConsoleOpen(true); - } - }; - - const toggleView = ( - - - - ); - - return ( - <> - {showAdminConsole && ( - - - {showAnalytics && ( - }> - - Analytics - - - )} - {showPolicyBuilder && ( - }> - - Permissions - - - )} - - - )} - - ); -}; diff --git a/datahub-web-react/src/app/ProtectedRoutes.tsx b/datahub-web-react/src/app/ProtectedRoutes.tsx index 3cadb9c1d3e4b1..e69cd36f51021b 100644 --- a/datahub-web-react/src/app/ProtectedRoutes.tsx +++ b/datahub-web-react/src/app/ProtectedRoutes.tsx @@ -6,7 +6,6 @@ import styled from 'styled-components'; import DataHubTitle from '@app/DataHubTitle'; import EmbedRoutes from '@app/EmbedRoutes'; import { SearchRoutes } from '@app/SearchRoutes'; -import { HomePage } from '@app/home/HomePage'; import { HomePage as HomePageV2 } from '@app/homeV2/HomePage'; import { IntroduceYourself } from '@app/homeV2/introduce/IntroduceYourself'; import { useSetUserPersona } from '@app/homeV2/persona/useUserPersona'; @@ -14,7 +13,7 @@ import { HomePage as HomePageV3 } from '@app/homeV3/HomePage'; import { useShowHomePageRedesign } from '@app/homeV3/context/hooks/useShowHomePageRedesign'; import { useSetUserTitle } from '@app/identity/user/useUserTitle'; import { OnboardingContextProvider } from '@app/onboarding/OnboardingContextProvider'; -import { useIsThemeV2, useSetThemeIsV2 } from '@app/useIsThemeV2'; +import { useSetThemeIsV2 } from '@app/useIsThemeV2'; import { useSetAppTheme } from '@app/useSetAppTheme'; import { useSetNavBarRedesignEnabled } from '@app/useShowNavBarRedesign'; import { NEW_ROUTE_MAP, PageRoutes } from '@conf/Global'; @@ -34,16 +33,9 @@ export const ProtectedRoutes = (): JSX.Element => { useSetUserTitle(); useSetNavBarRedesignEnabled(); - const isThemeV2 = useIsThemeV2(); const showHomepageRedesign = useShowHomePageRedesign(); + const FinalHomePage = showHomepageRedesign ? HomePageV3 : HomePageV2; - let FinalHomePage; - - if (isThemeV2) { - FinalHomePage = showHomepageRedesign ? HomePageV3 : HomePageV2; - } else { - FinalHomePage = HomePage; - } const location = useLocation(); const history = useHistory(); @@ -57,7 +49,7 @@ export const ProtectedRoutes = (): JSX.Element => { return ( - + } /> } /> diff --git a/datahub-web-react/src/app/SearchRoutes.tsx b/datahub-web-react/src/app/SearchRoutes.tsx index dbb741782a5f93..8fc2e9c27518e7 100644 --- a/datahub-web-react/src/app/SearchRoutes.tsx +++ b/datahub-web-react/src/app/SearchRoutes.tsx @@ -6,22 +6,14 @@ import { ManageApplications } from '@app/applications/ManageApplications'; import { BrowseResultsPage } from '@app/browse/BrowseResultsPage'; import { BusinessAttributes } from '@app/businessAttribute/BusinessAttributes'; import { useUserContext } from '@app/context/useUserContext'; -import DomainRoutes from '@app/domain/DomainRoutes'; -import { ManageDomainsPage } from '@app/domain/ManageDomainsPage'; import DomainRoutesV2 from '@app/domainV2/DomainRoutes'; import { ManageDomainsPage as ManageDomainsPageV2 } from '@app/domainV2/ManageDomainsPage'; -import { EntityPage } from '@app/entity/EntityPage'; import { EntityPage as EntityPageV2 } from '@app/entityV2/EntityPage'; -import GlossaryRoutes from '@app/glossary/GlossaryRoutes'; import GlossaryRoutesV2 from '@app/glossaryV2/GlossaryRoutes'; import StructuredProperties from '@app/govern/structuredProperties/StructuredProperties'; -import { ManageIngestionPage } from '@app/ingest/ManageIngestionPage'; import { ManageIngestionPage as ManageIngestionPageV2 } from '@app/ingestV2/ManageIngestionPage'; -import { SearchPage } from '@app/search/SearchPage'; -import { SearchablePage } from '@app/search/SearchablePage'; import { SearchPage as SearchPageV2 } from '@app/searchV2/SearchPage'; import { SearchablePage as SearchablePageV2 } from '@app/searchV2/SearchablePage'; -import { SettingsPage } from '@app/settings/SettingsPage'; import { SettingsPage as SettingsPageV2 } from '@app/settingsV2/SettingsPage'; import { NoPageFound } from '@app/shared/NoPageFound'; import { ManageTags } from '@app/tags/ManageTags'; @@ -32,7 +24,6 @@ import { useIsNestedDomainsEnabled, } from '@app/useAppConfig'; import { useEntityRegistry } from '@app/useEntityRegistry'; -import { useIsThemeV2 } from '@app/useIsThemeV2'; import { PageRoutes } from '@conf/Global'; /** @@ -46,8 +37,6 @@ export const SearchRoutes = (): JSX.Element => { ? entityRegistry.getEntitiesForSearchRoutes() : entityRegistry.getNonGlossaryEntities(); const { config, loaded } = useAppConfig(); - const isThemeV2 = useIsThemeV2(); - const FinalSearchablePage = isThemeV2 ? SearchablePageV2 : SearchablePage; const businessAttributesFlag = useBusinessAttributesFlag(); const appConfigContextLoaded = useIsAppConfigContextLoaded(); @@ -64,25 +53,16 @@ export const SearchRoutes = (): JSX.Element => { const showAnalytics = (config?.analyticsConfig?.enabled && me && me?.platformPrivileges?.viewAnalytics) || false; return ( - + {entities.map((entity) => ( - isThemeV2 ? ( - - ) : ( - - ) - } + render={() => } /> ))} - (isThemeV2 ? : )} - /> + } /> } /> {showTags ? } /> : null} } /> @@ -97,27 +77,13 @@ export const SearchRoutes = (): JSX.Element => { /> } /> } /> - {isNestedDomainsEnabled && ( - (isThemeV2 ? : )} - /> - )} - {!isNestedDomainsEnabled && ( - (isThemeV2 ? : )} - /> - )} + {isNestedDomainsEnabled && } />} + {!isNestedDomainsEnabled && } />} - {!showIngestV2 && } />} {showIngestV2 && } />} - (isThemeV2 ? : )} /> - (isThemeV2 ? : )} - /> + } /> + } /> {showStructuredProperties && ( } /> )} @@ -135,6 +101,6 @@ export const SearchRoutes = (): JSX.Element => { /> {me.loaded && loaded && } - + ); }; diff --git a/datahub-web-react/src/app/analytics/event.ts b/datahub-web-react/src/app/analytics/event.ts index b69a4806c6b8f5..ddf4bf642b2d4b 100644 --- a/datahub-web-react/src/app/analytics/event.ts +++ b/datahub-web-react/src/app/analytics/event.ts @@ -34,7 +34,6 @@ export enum EventType { HomePageSearchEvent, SearchResultsViewEvent, SearchResultClickEvent, - EntitySearchResultClickEvent, SearchFiltersClearAllEvent, SearchFiltersShowMoreEvent, BrowseResultClickEvent, @@ -191,7 +190,7 @@ interface BaseEvent { /** * Viewed a page on the UI. */ -export interface PageViewEvent extends BaseEvent { +interface PageViewEvent extends BaseEvent { type: EventType.PageViewEvent; originPath: string; } @@ -199,14 +198,14 @@ export interface PageViewEvent extends BaseEvent { /** * Viewed the Introduce Yourself page on the UI. */ -export interface IntroduceYourselfViewEvent extends BaseEvent { +interface IntroduceYourselfViewEvent extends BaseEvent { type: EventType.IntroduceYourselfViewEvent; } /** * Submitted the "Introduce Yourself" page through the UI. */ -export interface IntroduceYourselfSubmitEvent extends BaseEvent { +interface IntroduceYourselfSubmitEvent extends BaseEvent { type: EventType.IntroduceYourselfSubmitEvent; role: string; platformUrns: Array; @@ -215,21 +214,21 @@ export interface IntroduceYourselfSubmitEvent extends BaseEvent { /** * Skipped the "Introduce Yourself" page through the UI. */ -export interface IntroduceYourselfSkipEvent extends BaseEvent { +interface IntroduceYourselfSkipEvent extends BaseEvent { type: EventType.IntroduceYourselfSkipEvent; } /** * Viewed the Home Page on the UI. */ -export interface HomePageViewEvent extends BaseEvent { +interface HomePageViewEvent extends BaseEvent { type: EventType.HomePageViewEvent; } /** * Logged on successful new user sign up. */ -export interface SignUpEvent extends BaseEvent { +interface SignUpEvent extends BaseEvent { type: EventType.SignUpEvent; title: string; } @@ -237,28 +236,28 @@ export interface SignUpEvent extends BaseEvent { /** * Logged on user successful login. */ -export interface LogInEvent extends BaseEvent { +interface LogInEvent extends BaseEvent { type: EventType.LogInEvent; } /** * Logged on user successful logout. */ -export interface LogOutEvent extends BaseEvent { +interface LogOutEvent extends BaseEvent { type: EventType.LogOutEvent; } /** * Logged on user resetting their credentials */ -export interface ResetCredentialsEvent extends BaseEvent { +interface ResetCredentialsEvent extends BaseEvent { type: EventType.ResetCredentialsEvent; } /** * Logged on user successful search query. */ -export interface SearchEvent extends BaseEvent { +interface SearchEvent extends BaseEvent { type: EventType.SearchEvent; query: string; entityTypeFilter?: EntityType; @@ -271,7 +270,7 @@ export interface SearchEvent extends BaseEvent { /** * Logged on user successful search query from the home page. */ -export interface HomePageSearchEvent extends BaseEvent { +interface HomePageSearchEvent extends BaseEvent { type: EventType.HomePageSearchEvent; query: string; entityTypeFilter?: EntityType; @@ -283,7 +282,7 @@ export interface HomePageSearchEvent extends BaseEvent { /** * Logged on user search result click. */ -export interface SearchResultsViewEvent extends BaseEvent { +interface SearchResultsViewEvent extends BaseEvent { type: EventType.SearchResultsViewEvent; query: string; entityTypeFilter?: EntityType; @@ -299,7 +298,7 @@ export interface SearchResultsViewEvent extends BaseEvent { /** * Logged on user search result click. */ -export interface SearchResultClickEvent extends BaseEvent { +interface SearchResultClickEvent extends BaseEvent { type: EventType.SearchResultClickEvent; query: string; entityUrn: string; @@ -310,12 +309,12 @@ export interface SearchResultClickEvent extends BaseEvent { pageNumber: number; } -export interface SearchFiltersClearAllEvent extends BaseEvent { +interface SearchFiltersClearAllEvent extends BaseEvent { type: EventType.SearchFiltersClearAllEvent; total: number; } -export interface SearchFiltersShowMoreEvent extends BaseEvent { +interface SearchFiltersShowMoreEvent extends BaseEvent { type: EventType.SearchFiltersShowMoreEvent; activeFilterCount: number; hiddenFilterCount: number; @@ -324,7 +323,7 @@ export interface SearchFiltersShowMoreEvent extends BaseEvent { /** * Logged on user browse result click. */ -export interface BrowseResultClickEvent extends BaseEvent { +interface BrowseResultClickEvent extends BaseEvent { type: EventType.BrowseResultClickEvent; browsePath: string; entityType: EntityType; @@ -336,7 +335,7 @@ export interface BrowseResultClickEvent extends BaseEvent { /** * Logged on user browse result click from the home page. */ -export interface HomePageBrowseResultClickEvent extends BaseEvent { +interface HomePageBrowseResultClickEvent extends BaseEvent { type: EventType.HomePageBrowseResultClickEvent; entityType: EntityType; } @@ -344,7 +343,7 @@ export interface HomePageBrowseResultClickEvent extends BaseEvent { /** * Logged when a user opens or closes the browse v2 sidebar */ -export interface BrowseV2ToggleSidebarEvent extends BaseEvent { +interface BrowseV2ToggleSidebarEvent extends BaseEvent { type: EventType.BrowseV2ToggleSidebarEvent; action: 'open' | 'close'; } @@ -390,7 +389,7 @@ export interface BrowseV2EntityLinkClickEvent extends BaseEvent { /** * Logged when user views an entity profile. */ -export interface EntityViewEvent extends BaseEvent { +interface EntityViewEvent extends BaseEvent { type: EventType.EntityViewEvent; entityType: EntityType; entityUrn: string; @@ -399,7 +398,7 @@ export interface EntityViewEvent extends BaseEvent { /** * Logged when user views a particular section of an entity profile. */ -export interface EntitySectionViewEvent extends BaseEvent { +interface EntitySectionViewEvent extends BaseEvent { type: EventType.EntitySectionViewEvent; entityType: EntityType; entityUrn: string; @@ -434,7 +433,7 @@ export enum ExternalLinkType { Default = 'DEFAULT_EXTERNAL_URL', } -export interface EntityActionEvent extends BaseEvent { +interface EntityActionEvent extends BaseEvent { type: EventType.EntityActionEvent; actionType: string; entityType?: EntityType; @@ -442,13 +441,13 @@ export interface EntityActionEvent extends BaseEvent { externalLinkType?: ExternalLinkType; } -export interface BatchEntityActionEvent extends BaseEvent { +interface BatchEntityActionEvent extends BaseEvent { type: EventType.BatchEntityActionEvent; actionType: string; entityUrns: string[]; } -export interface RecommendationImpressionEvent extends BaseEvent { +interface RecommendationImpressionEvent extends BaseEvent { type: EventType.RecommendationImpressionEvent; moduleId: string; renderType: RecommendationRenderType; @@ -456,7 +455,7 @@ export interface RecommendationImpressionEvent extends BaseEvent { // TODO: Determine whether we need to collect context parameters. } -export interface RecommendationClickEvent extends BaseEvent { +interface RecommendationClickEvent extends BaseEvent { type: EventType.RecommendationClickEvent; renderId: string; // TODO : Determine whether we need a render id to join with click event. moduleId: string; @@ -465,7 +464,7 @@ export interface RecommendationClickEvent extends BaseEvent { index?: number; } -export interface HomePageRecommendationClickEvent extends BaseEvent { +interface HomePageRecommendationClickEvent extends BaseEvent { type: EventType.HomePageRecommendationClickEvent; renderId: string; // TODO : Determine whether we need a render id to join with click event. moduleId: string; @@ -474,7 +473,7 @@ export interface HomePageRecommendationClickEvent extends BaseEvent { index?: number; } -export interface VisualLineageViewEvent extends BaseEvent { +interface VisualLineageViewEvent extends BaseEvent { type: EventType.VisualLineageViewEvent; entityType: EntityType; numUpstreams: number; @@ -486,12 +485,12 @@ export interface VisualLineageViewEvent extends BaseEvent { hasExpandableDownstreamsV3?: boolean; } -export interface VisualLineageExpandGraphEvent extends BaseEvent { +interface VisualLineageExpandGraphEvent extends BaseEvent { type: EventType.VisualLineageExpandGraphEvent; targetEntityType?: EntityType; } -export interface SearchAcrossLineageEvent extends BaseEvent { +interface SearchAcrossLineageEvent extends BaseEvent { type: EventType.SearchAcrossLineageEvent; query: string; entityTypeFilter?: EntityType; @@ -499,7 +498,7 @@ export interface SearchAcrossLineageEvent extends BaseEvent { originPath: string; maxDegree?: string; } -export interface SearchAcrossLineageResultsViewEvent extends BaseEvent { +interface SearchAcrossLineageResultsViewEvent extends BaseEvent { type: EventType.SearchAcrossLineageResultsViewEvent; query: string; entityTypeFilter?: EntityType; @@ -511,7 +510,7 @@ export interface SearchAcrossLineageResultsViewEvent extends BaseEvent { isSchemaFieldContext?: boolean; } -export interface DownloadAsCsvEvent extends BaseEvent { +interface DownloadAsCsvEvent extends BaseEvent { type: EventType.DownloadAsCsvEvent; query: string; // optional parameter if its coming from inside an entity page @@ -519,48 +518,48 @@ export interface DownloadAsCsvEvent extends BaseEvent { path: string; } -export interface CreateAccessTokenEvent extends BaseEvent { +interface CreateAccessTokenEvent extends BaseEvent { type: EventType.CreateAccessTokenEvent; accessTokenType: string; duration: string; } -export interface RevokeAccessTokenEvent extends BaseEvent { +interface RevokeAccessTokenEvent extends BaseEvent { type: EventType.RevokeAccessTokenEvent; } -export interface CreateGroupEvent extends BaseEvent { +interface CreateGroupEvent extends BaseEvent { type: EventType.CreateGroupEvent; } -export interface CreateInviteLinkEvent extends BaseEvent { +interface CreateInviteLinkEvent extends BaseEvent { type: EventType.CreateInviteLinkEvent; roleUrn?: string; } -export interface CreateResetCredentialsLinkEvent extends BaseEvent { +interface CreateResetCredentialsLinkEvent extends BaseEvent { type: EventType.CreateResetCredentialsLinkEvent; userUrn: string; } -export interface DeleteEntityEvent extends BaseEvent { +interface DeleteEntityEvent extends BaseEvent { type: EventType.DeleteEntityEvent; entityUrn: string; entityType: EntityType; } -export interface SelectUserRoleEvent extends BaseEvent { +interface SelectUserRoleEvent extends BaseEvent { type: EventType.SelectUserRoleEvent; roleUrn: string; userUrn: string; } -export interface SelectGroupRoleEvent extends BaseEvent { +interface SelectGroupRoleEvent extends BaseEvent { type: EventType.SelectGroupRoleEvent; roleUrn: string; groupUrn?: string; } -export interface BatchSelectUserRoleEvent extends BaseEvent { +interface BatchSelectUserRoleEvent extends BaseEvent { type: EventType.BatchSelectUserRoleEvent; roleUrn: string; userUrns: string[]; @@ -568,67 +567,67 @@ export interface BatchSelectUserRoleEvent extends BaseEvent { // Policy events -export interface CreatePolicyEvent extends BaseEvent { +interface CreatePolicyEvent extends BaseEvent { type: EventType.CreatePolicyEvent; } -export interface UpdatePolicyEvent extends BaseEvent { +interface UpdatePolicyEvent extends BaseEvent { type: EventType.UpdatePolicyEvent; policyUrn: string; } -export interface DeactivatePolicyEvent extends BaseEvent { +interface DeactivatePolicyEvent extends BaseEvent { type: EventType.DeactivatePolicyEvent; policyUrn: string; } -export interface ActivatePolicyEvent extends BaseEvent { +interface ActivatePolicyEvent extends BaseEvent { type: EventType.ActivatePolicyEvent; policyUrn: string; } -export interface ShowSimplifiedHomepageEvent extends BaseEvent { +interface ShowSimplifiedHomepageEvent extends BaseEvent { type: EventType.ShowSimplifiedHomepageEvent; } -export interface ShowStandardHomepageEvent extends BaseEvent { +interface ShowStandardHomepageEvent extends BaseEvent { type: EventType.ShowStandardHomepageEvent; } -export interface ShowV2ThemeEvent extends BaseEvent { +interface ShowV2ThemeEvent extends BaseEvent { type: EventType.ShowV2ThemeEvent; } -export interface RevertV2ThemeEvent extends BaseEvent { +interface RevertV2ThemeEvent extends BaseEvent { type: EventType.RevertV2ThemeEvent; } -export interface HomePageExploreAllClickEvent extends BaseEvent { +interface HomePageExploreAllClickEvent extends BaseEvent { type: EventType.HomePageExploreAllClickEvent; } -export interface SearchBarExploreAllClickEvent extends BaseEvent { +interface SearchBarExploreAllClickEvent extends BaseEvent { type: EventType.SearchBarExploreAllClickEvent; } -export interface SearchResultsExploreAllClickEvent extends BaseEvent { +interface SearchResultsExploreAllClickEvent extends BaseEvent { type: EventType.SearchResultsExploreAllClickEvent; } // Business glossary events -export interface CreateGlossaryEntityEvent extends BaseEvent { +interface CreateGlossaryEntityEvent extends BaseEvent { type: EventType.CreateGlossaryEntityEvent; entityType: EntityType; parentNodeUrn?: string; } -export interface CreateDomainEvent extends BaseEvent { +interface CreateDomainEvent extends BaseEvent { type: EventType.CreateDomainEvent; parentDomainUrn?: string; } -export interface MoveDomainEvent extends BaseEvent { +interface MoveDomainEvent extends BaseEvent { type: EventType.MoveDomainEvent; oldParentDomainUrn?: string; parentDomainUrn?: string; @@ -636,38 +635,38 @@ export interface MoveDomainEvent extends BaseEvent { // Managed Ingestion Events -export interface IngestionTestConnectionEvent extends BaseEvent { +interface IngestionTestConnectionEvent extends BaseEvent { type: EventType.IngestionTestConnectionEvent; sourceType: string; sourceUrn?: string; outcome?: string; } -export interface IngestionViewAllClickEvent extends BaseEvent { +interface IngestionViewAllClickEvent extends BaseEvent { type: EventType.IngestionViewAllClickEvent; executionUrn?: string; } -export interface IngestionViewAllClickWarningEvent extends BaseEvent { +interface IngestionViewAllClickWarningEvent extends BaseEvent { type: EventType.IngestionViewAllClickWarningEvent; executionUrn?: string; } -export interface IngestionExecutionResultViewedEvent extends BaseEvent { +interface IngestionExecutionResultViewedEvent extends BaseEvent { type: EventType.IngestionExecutionResultViewedEvent; executionUrn: string; executionStatus: string; viewedSection: string; } -export interface IngestionSourceConfigurationImpressionEvent extends BaseEvent { +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 { +interface CreateIngestionSourceEvent extends BaseEvent { type: EventType.CreateIngestionSourceEvent; sourceType: string; sourceUrn?: string; @@ -676,7 +675,7 @@ export interface CreateIngestionSourceEvent extends BaseEvent { outcome?: string; } -export interface UpdateIngestionSourceEvent extends BaseEvent { +interface UpdateIngestionSourceEvent extends BaseEvent { type: EventType.UpdateIngestionSourceEvent; sourceType: string; sourceUrn: string; @@ -685,22 +684,22 @@ export interface UpdateIngestionSourceEvent extends BaseEvent { outcome?: string; } -export interface DeleteIngestionSourceEvent extends BaseEvent { +interface DeleteIngestionSourceEvent extends BaseEvent { type: EventType.DeleteIngestionSourceEvent; } -export interface ExecuteIngestionSourceEvent extends BaseEvent { +interface ExecuteIngestionSourceEvent extends BaseEvent { type: EventType.ExecuteIngestionSourceEvent; sourceType?: string; sourceUrn?: string; } // TODO: Find a way to use this event -export interface SsoEvent extends BaseEvent { +interface SsoEvent extends BaseEvent { type: EventType.SsoEvent; } -export interface ManuallyCreateLineageEvent extends BaseEvent { +interface ManuallyCreateLineageEvent extends BaseEvent { type: EventType.ManuallyCreateLineageEvent; direction: Direction; sourceEntityType?: EntityType; @@ -709,7 +708,7 @@ export interface ManuallyCreateLineageEvent extends BaseEvent { destinationEntityPlatform?: string; } -export interface ManuallyDeleteLineageEvent extends BaseEvent { +interface ManuallyDeleteLineageEvent extends BaseEvent { type: EventType.ManuallyDeleteLineageEvent; direction: Direction; sourceEntityType?: EntityType; @@ -721,7 +720,7 @@ export interface ManuallyDeleteLineageEvent extends BaseEvent { /** * Emitted when a new View is created. */ -export interface CreateViewEvent extends BaseEvent { +interface CreateViewEvent extends BaseEvent { type: EventType.CreateViewEvent; viewType?: DataHubViewType; filterFields: string[]; @@ -732,7 +731,7 @@ export interface CreateViewEvent extends BaseEvent { /** * Emitted when an existing View is updated. */ -export interface UpdateViewEvent extends BaseEvent { +interface UpdateViewEvent extends BaseEvent { type: EventType.UpdateViewEvent; viewType?: DataHubViewType; urn: string; @@ -744,7 +743,7 @@ export interface UpdateViewEvent extends BaseEvent { /** * Emitted when a user sets or clears their personal default view. */ -export interface SetUserDefaultViewEvent extends BaseEvent { +interface SetUserDefaultViewEvent extends BaseEvent { type: EventType.SetUserDefaultViewEvent; urn: string | null; viewType: DataHubViewType | null; @@ -753,36 +752,36 @@ export interface SetUserDefaultViewEvent extends BaseEvent { /** * Emitted when a user sets or clears the global default view. */ -export interface SetGlobalDefaultViewEvent extends BaseEvent { +interface SetGlobalDefaultViewEvent extends BaseEvent { type: EventType.SetGlobalDefaultViewEvent; urn: string | null; } -export interface LineageGraphTimeRangeSelectionEvent extends BaseEvent { +interface LineageGraphTimeRangeSelectionEvent extends BaseEvent { type: EventType.LineageGraphTimeRangeSelectionEvent; relativeStartDate: string; relativeEndDate: string; } -export interface LineageTabTimeRangeSelectionEvent extends BaseEvent { +interface LineageTabTimeRangeSelectionEvent extends BaseEvent { type: EventType.LineageTabTimeRangeSelectionEvent; relativeStartDate: string; relativeEndDate: string; } -export interface CreateQueryEvent extends BaseEvent { +interface CreateQueryEvent extends BaseEvent { type: EventType.CreateQueryEvent; } -export interface UpdateQueryEvent extends BaseEvent { +interface UpdateQueryEvent extends BaseEvent { type: EventType.UpdateQueryEvent; } -export interface DeleteQueryEvent extends BaseEvent { +interface DeleteQueryEvent extends BaseEvent { type: EventType.DeleteQueryEvent; } -export interface SelectAutoCompleteOption extends BaseEvent { +interface SelectAutoCompleteOption extends BaseEvent { type: EventType.SelectAutoCompleteOption; optionType: string; entityType?: EntityType; @@ -791,52 +790,51 @@ export interface SelectAutoCompleteOption extends BaseEvent { apiVariant?: SearchBarApi; } -export interface SelectQuickFilterEvent extends BaseEvent { +interface SelectQuickFilterEvent extends BaseEvent { type: EventType.SelectQuickFilterEvent; quickFilterType: string; quickFilterValue: string; } -export interface DeselectQuickFilterEvent extends BaseEvent { +interface DeselectQuickFilterEvent extends BaseEvent { type: EventType.DeselectQuickFilterEvent; quickFilterType: string; quickFilterValue: string; } -export interface EmbedProfileViewEvent extends BaseEvent { +interface EmbedProfileViewEvent extends BaseEvent { type: EventType.EmbedProfileViewEvent; entityType: string; entityUrn: string; } -export interface EmbedProfileViewInDataHubEvent extends BaseEvent { +interface EmbedProfileViewInDataHubEvent extends BaseEvent { type: EventType.EmbedProfileViewInDataHubEvent; entityType: string; entityUrn: string; } -export interface EmbedLookupNotFoundEvent extends BaseEvent { +interface EmbedLookupNotFoundEvent extends BaseEvent { type: EventType.EmbedLookupNotFoundEvent; url: string; reason: EmbedLookupNotFoundReason; } -export interface CreateBusinessAttributeEvent extends BaseEvent { +interface CreateBusinessAttributeEvent extends BaseEvent { type: EventType.CreateBusinessAttributeEvent; name: string; } export enum DocRequestCTASource { - TaskCenter = 'TaskCenter', AssetPage = 'AssetPage', } -export interface ClickDocRequestCTA extends BaseEvent { +interface ClickDocRequestCTA extends BaseEvent { type: EventType.ClickDocRequestCTA; source: DocRequestCTASource; } -export interface ExpandLineageEvent extends BaseEvent { +interface ExpandLineageEvent extends BaseEvent { type: EventType.ExpandLineageEvent; direction: LineageDirection; levelsExpanded: '1' | 'all'; @@ -844,14 +842,14 @@ export interface ExpandLineageEvent extends BaseEvent { entityType: EntityType; } -export interface ContractLineageEvent extends BaseEvent { +interface ContractLineageEvent extends BaseEvent { type: EventType.ContractLineageEvent; direction: LineageDirection; entityUrn: string; entityType?: EntityType; } -export interface ShowHideLineageColumnsEvent extends BaseEvent { +interface ShowHideLineageColumnsEvent extends BaseEvent { type: EventType.ShowHideLineageColumnsEvent; action: 'show' | 'hide'; entityUrn: string; @@ -859,14 +857,14 @@ export interface ShowHideLineageColumnsEvent extends BaseEvent { entityPlatformUrn?: string; } -export interface SearchLineageColumnsEvent extends BaseEvent { +interface SearchLineageColumnsEvent extends BaseEvent { type: EventType.SearchLineageColumnsEvent; entityUrn: string; entityType: EntityType; searchTextLength: number; } -export interface FilterLineageColumnsEvent extends BaseEvent { +interface FilterLineageColumnsEvent extends BaseEvent { type: EventType.FilterLineageColumnsEvent; action: 'enable' | 'disable'; entityUrn: string; @@ -874,7 +872,7 @@ export interface FilterLineageColumnsEvent extends BaseEvent { shownCount: number; } -export interface DrillDownLineageEvent extends BaseEvent { +interface DrillDownLineageEvent extends BaseEvent { type: EventType.DrillDownLineageEvent; action: 'select' | 'deselect'; entityUrn: string; @@ -884,7 +882,7 @@ export interface DrillDownLineageEvent extends BaseEvent { dataType?: string; } -export interface CreateStructuredPropertyClickEvent extends BaseEvent { +interface CreateStructuredPropertyClickEvent extends BaseEvent { type: EventType.CreateStructuredPropertyClickEvent; } @@ -904,21 +902,21 @@ interface StructuredPropertyEvent extends BaseEvent { showInColumnsTable: boolean; } -export interface CreateStructuredPropertyEvent extends StructuredPropertyEvent { +interface CreateStructuredPropertyEvent extends StructuredPropertyEvent { type: EventType.CreateStructuredPropertyEvent; } -export interface EditStructuredPropertyEvent extends StructuredPropertyEvent { +interface EditStructuredPropertyEvent extends StructuredPropertyEvent { type: EventType.EditStructuredPropertyEvent; propertyUrn: string; } -export interface DeleteStructuredPropertyEvent extends StructuredPropertyEvent { +interface DeleteStructuredPropertyEvent extends StructuredPropertyEvent { type: EventType.DeleteStructuredPropertyEvent; propertyUrn: string; } -export interface ViewStructuredPropertyEvent extends BaseEvent { +interface ViewStructuredPropertyEvent extends BaseEvent { type: EventType.ViewStructuredPropertyEvent; propertyUrn: string; } @@ -929,21 +927,21 @@ interface StructuredPropertyOnAssetEvent extends BaseEvent { assetUrn: string; assetType: EntityType; } -export interface ApplyStructuredPropertyEvent extends StructuredPropertyOnAssetEvent { +interface ApplyStructuredPropertyEvent extends StructuredPropertyOnAssetEvent { type: EventType.ApplyStructuredPropertyEvent; values: PropertyValueInput[]; } -export interface UpdateStructuredPropertyOnAssetEvent extends StructuredPropertyOnAssetEvent { +interface UpdateStructuredPropertyOnAssetEvent extends StructuredPropertyOnAssetEvent { type: EventType.UpdateStructuredPropertyOnAssetEvent; values: PropertyValueInput[]; } -export interface RemoveStructuredPropertyEvent extends StructuredPropertyOnAssetEvent { +interface RemoveStructuredPropertyEvent extends StructuredPropertyOnAssetEvent { type: EventType.RemoveStructuredPropertyEvent; } -export interface LinkAssetVersionEvent extends BaseEvent { +interface LinkAssetVersionEvent extends BaseEvent { type: EventType.LinkAssetVersionEvent; newAssetUrn: string; oldAssetUrn?: string; @@ -951,14 +949,14 @@ export interface LinkAssetVersionEvent extends BaseEvent { entityType: EntityType; } -export interface UnlinkAssetVersionEvent extends BaseEvent { +interface UnlinkAssetVersionEvent extends BaseEvent { type: EventType.UnlinkAssetVersionEvent; assetUrn: string; versionSetUrn?: string; entityType: EntityType; } -export interface ShowAllVersionsEvent extends BaseEvent { +interface ShowAllVersionsEvent extends BaseEvent { type: EventType.ShowAllVersionsEvent; assetUrn: string; versionSetUrn?: string; @@ -967,12 +965,12 @@ export interface ShowAllVersionsEvent extends BaseEvent { uiLocation: 'preview' | 'more-options'; } -export interface ClickUserProfileEvent extends BaseEvent { +interface ClickUserProfileEvent extends BaseEvent { type: EventType.ClickUserProfile; location?: 'statsTabTopUsers'; // add more locations here } -export interface ClickViewDocumentationEvent extends BaseEvent { +interface ClickViewDocumentationEvent extends BaseEvent { type: EventType.ClickViewDocumentation; link: string; location: 'statsTab'; // add more locations here @@ -983,10 +981,9 @@ export enum HomePageModule { Discover = 'Discover', Announcements = 'Announcements', PersonalSidebar = 'PersonalSidebar', - SidebarAnnouncements = 'SidebarAnnouncements', -} + } -export interface HomePageClickEvent extends BaseEvent { +interface HomePageClickEvent extends BaseEvent { type: EventType.HomePageClick; module: HomePageModule; section?: string; @@ -994,67 +991,67 @@ export interface HomePageClickEvent extends BaseEvent { value?: string; // what was actually clicked ie. an entity urn to go to a page, or "View all" for a section } -export interface SearchBarFilterEvent extends BaseEvent { +interface SearchBarFilterEvent extends BaseEvent { type: EventType.SearchBarFilter; field: string; // the filter field values: string[]; // the values being filtered for } -export interface NavBarExpandCollapseEvent extends BaseEvent { +interface NavBarExpandCollapseEvent extends BaseEvent { type: EventType.NavBarExpandCollapse; isExpanding: boolean; // whether this action is expanding or collapsing the nav bar } -export interface NavBarItemClickEvent extends BaseEvent { +interface NavBarItemClickEvent extends BaseEvent { type: EventType.NavBarItemClick; label: string; // the label of the item that is clicks from the nav sidebar } -export interface FilterStatsPageEvent extends BaseEvent { +interface FilterStatsPageEvent extends BaseEvent { type: EventType.FilterStatsPage; platform: string | null; } -export interface FilterStatsChartLookBackEvent extends BaseEvent { +interface FilterStatsChartLookBackEvent extends BaseEvent { type: EventType.FilterStatsChartLookBack; lookBackValue: string; chartName: string; } -export interface WelcomeToDataHubModalViewEvent extends BaseEvent { +interface WelcomeToDataHubModalViewEvent extends BaseEvent { type: EventType.WelcomeToDataHubModalViewEvent; } -export interface WelcomeToDataHubModalInteractEvent extends BaseEvent { +interface WelcomeToDataHubModalInteractEvent extends BaseEvent { type: EventType.WelcomeToDataHubModalInteractEvent; currentSlide: number; totalSlides: number; } -export interface WelcomeToDataHubModalExitEvent extends BaseEvent { +interface WelcomeToDataHubModalExitEvent extends BaseEvent { type: EventType.WelcomeToDataHubModalExitEvent; currentSlide: number; totalSlides: number; exitMethod: 'close_button' | 'get_started_button' | 'outside_click' | 'escape_key'; } -export interface WelcomeToDataHubModalClickViewDocumentationEvent extends BaseEvent { +interface WelcomeToDataHubModalClickViewDocumentationEvent extends BaseEvent { type: EventType.WelcomeToDataHubModalClickViewDocumentationEvent; url: string; } -export interface ProductTourButtonClickEvent extends BaseEvent { +interface ProductTourButtonClickEvent extends BaseEvent { type: EventType.ProductTourButtonClickEvent; originPage: string; // Page where the button was clicked } -export interface ClickProductUpdateEvent extends BaseEvent { +interface ClickProductUpdateEvent extends BaseEvent { type: EventType.ClickProductUpdate; id: string; url: string; } -export interface HomePageTemplateModuleCreateEvent extends BaseEvent { +interface HomePageTemplateModuleCreateEvent extends BaseEvent { type: EventType.HomePageTemplateModuleCreate; templateUrn: string; isPersonal: boolean; @@ -1062,7 +1059,7 @@ export interface HomePageTemplateModuleCreateEvent extends BaseEvent { location: PageTemplateSurfaceType; } -export interface HomePageTemplateModuleAddEvent extends BaseEvent { +interface HomePageTemplateModuleAddEvent extends BaseEvent { type: EventType.HomePageTemplateModuleAdd; templateUrn: string; isPersonal: boolean; @@ -1070,7 +1067,7 @@ export interface HomePageTemplateModuleAddEvent extends BaseEvent { location: PageTemplateSurfaceType; } -export interface HomePageTemplateModuleUpdateEvent extends BaseEvent { +interface HomePageTemplateModuleUpdateEvent extends BaseEvent { type: EventType.HomePageTemplateModuleUpdate; templateUrn: string; isPersonal: boolean; @@ -1078,7 +1075,7 @@ export interface HomePageTemplateModuleUpdateEvent extends BaseEvent { location: PageTemplateSurfaceType; } -export interface HomePageTemplateModuleDeleteEvent extends BaseEvent { +interface HomePageTemplateModuleDeleteEvent extends BaseEvent { type: EventType.HomePageTemplateModuleDelete; templateUrn: string; isPersonal: boolean; @@ -1086,99 +1083,99 @@ export interface HomePageTemplateModuleDeleteEvent extends BaseEvent { location: PageTemplateSurfaceType; } -export interface HomePageTemplateModuleMoveEvent extends BaseEvent { +interface HomePageTemplateModuleMoveEvent extends BaseEvent { type: EventType.HomePageTemplateModuleMove; templateUrn: string; isPersonal: boolean; location: PageTemplateSurfaceType; } -export interface HomePageTemplateModuleModalCreateOpenEvent extends BaseEvent { +interface HomePageTemplateModuleModalCreateOpenEvent extends BaseEvent { type: EventType.HomePageTemplateModuleModalCreateOpen; moduleType: DataHubPageModuleType; location: PageTemplateSurfaceType; } -export interface HomePageTemplateModuleModalEditOpenEvent extends BaseEvent { +interface HomePageTemplateModuleModalEditOpenEvent extends BaseEvent { type: EventType.HomePageTemplateModuleModalEditOpen; moduleType: DataHubPageModuleType; location: PageTemplateSurfaceType; } -export interface HomePageTemplateModuleModalCancelEvent extends BaseEvent { +interface HomePageTemplateModuleModalCancelEvent extends BaseEvent { type: EventType.HomePageTemplateModuleModalCancel; moduleType: DataHubPageModuleType; location: PageTemplateSurfaceType; } -export interface HomePageTemplateGlobalTemplateEditingStartEvent extends BaseEvent { +interface HomePageTemplateGlobalTemplateEditingStartEvent extends BaseEvent { type: EventType.HomePageTemplateGlobalTemplateEditingStart; } -export interface HomePageTemplateGlobalTemplateEditingDoneEvent extends BaseEvent { +interface HomePageTemplateGlobalTemplateEditingDoneEvent extends BaseEvent { type: EventType.HomePageTemplateGlobalTemplateEditingDone; } -export interface HomePageTemplateResetToGlobalTemplateEvent extends BaseEvent { +interface HomePageTemplateResetToGlobalTemplateEvent extends BaseEvent { type: EventType.HomePageTemplateResetToGlobalTemplate; } -export interface HomePageTemplateModuleAssetClickEvent extends BaseEvent { +interface HomePageTemplateModuleAssetClickEvent extends BaseEvent { type: EventType.HomePageTemplateModuleAssetClick; moduleType: DataHubPageModuleType; assetUrn: string; location: PageTemplateSurfaceType; } -export interface HomePageTemplateModuleExpandClickEvent extends BaseEvent { +interface HomePageTemplateModuleExpandClickEvent extends BaseEvent { type: EventType.HomePageTemplateModuleExpandClick; moduleType: DataHubPageModuleType; assetUrn: string; location: PageTemplateSurfaceType; } -export interface HomePageTemplateModuleViewAllClickEvent extends BaseEvent { +interface HomePageTemplateModuleViewAllClickEvent extends BaseEvent { type: EventType.HomePageTemplateModuleViewAllClick; moduleType: DataHubPageModuleType; location: PageTemplateSurfaceType; } -export interface HomePageTemplateModuleLinkClickEvent extends BaseEvent { +interface HomePageTemplateModuleLinkClickEvent extends BaseEvent { type: EventType.HomePageTemplateModuleLinkClick; link: string; } -export interface HomePageTemplateModuleAnnouncementDismissEvent extends BaseEvent { +interface HomePageTemplateModuleAnnouncementDismissEvent extends BaseEvent { type: EventType.HomePageTemplateModuleAnnouncementDismiss; } -export interface SetDeprecationEvent extends BaseEvent { +interface SetDeprecationEvent extends BaseEvent { type: EventType.SetDeprecation; entityUrns: string[]; deprecated: boolean; resources?: ResourceRefInput[]; } -export interface AssetPageAddSummaryElementEvent extends BaseEvent { +interface AssetPageAddSummaryElementEvent extends BaseEvent { type: EventType.AssetPageAddSummaryElement; templateUrn: string; elementType: SummaryElementType; } -export interface AssetPageRemoveSummaryElementEvent extends BaseEvent { +interface AssetPageRemoveSummaryElementEvent extends BaseEvent { type: EventType.AssetPageRemoveSummaryElement; templateUrn: string; elementType: SummaryElementType; } -export interface AssetPageReplaceSummaryElementEvent extends BaseEvent { +interface AssetPageReplaceSummaryElementEvent extends BaseEvent { type: EventType.AssetPageReplaceSummaryElement; templateUrn: string; currentElementType: SummaryElementType; newElementType: SummaryElementType; } -export interface FileUploadAttemptEvent extends BaseEvent { +interface FileUploadAttemptEvent extends BaseEvent { type: EventType.FileUploadAttemptEvent; fileType: string; fileSize: number; @@ -1188,7 +1185,7 @@ export interface FileUploadAttemptEvent extends BaseEvent { schemaFieldUrn?: string; } -export interface FileUploadFailedEvent extends BaseEvent { +interface FileUploadFailedEvent extends BaseEvent { type: EventType.FileUploadFailedEvent; fileType: string; fileSize: number; @@ -1200,7 +1197,7 @@ export interface FileUploadFailedEvent extends BaseEvent { comment?: string; } -export interface FileUploadSucceededEvent extends BaseEvent { +interface FileUploadSucceededEvent extends BaseEvent { type: EventType.FileUploadSucceededEvent; fileType: string; fileSize: number; @@ -1210,7 +1207,7 @@ export interface FileUploadSucceededEvent extends BaseEvent { schemaFieldUrn?: string; } -export interface FileDownloadViewEvent extends BaseEvent { +interface FileDownloadViewEvent extends BaseEvent { type: EventType.FileDownloadViewEvent; // These fields aren't accessible while downloading // fileType: string; diff --git a/datahub-web-react/src/app/analytics/plugin/mixpanel.ts b/datahub-web-react/src/app/analytics/plugin/mixpanel.ts index 755f8c825ea7b1..02cb1b276e9ae0 100644 --- a/datahub-web-react/src/app/analytics/plugin/mixpanel.ts +++ b/datahub-web-react/src/app/analytics/plugin/mixpanel.ts @@ -6,7 +6,7 @@ const mixpanelConfigs = analyticsConfig.mixpanel; const isEnabled: boolean = mixpanelConfigs || false; const token = isEnabled ? mixpanelConfigs.token : undefined; -export const getMixpanelPlugin = (t: string) => { +const getMixpanelPlugin = (t: string) => { // Mixpanel does not really have a page view event. // (Technically they have $mp_web_page_view as per // https://github.com/mixpanel/mixpanel-js/blob/41e7958af75263a1dc00e960952c27ca655579b2/src/mixpanel-core.js#L1269 diff --git a/datahub-web-react/src/app/analyticsDashboard/components/AnalyticsPage.tsx b/datahub-web-react/src/app/analyticsDashboard/components/AnalyticsPage.tsx index ccd17a69051452..21cf0a40fa66bf 100644 --- a/datahub-web-react/src/app/analyticsDashboard/components/AnalyticsPage.tsx +++ b/datahub-web-react/src/app/analyticsDashboard/components/AnalyticsPage.tsx @@ -9,15 +9,14 @@ import { useUserContext } from '@app/context/useUserContext'; import { ANTD_GRAY } from '@app/entity/shared/constants'; import filterSearchQuery from '@app/search/utils/filterSearchQuery'; import { Message } from '@app/shared/Message'; -import { useIsThemeV2 } from '@app/useIsThemeV2'; import { useShowNavBarRedesign } from '@src/app/useShowNavBarRedesign'; import { useGetAnalyticsChartsQuery, useGetMetadataAnalyticsChartsQuery } from '@graphql/analytics.generated'; import { useListDomainsQuery } from '@graphql/domain.generated'; import { useGetHighlightsQuery } from '@graphql/highlights.generated'; -const PageContainer = styled.div<{ isV2: boolean; $isShowNavBarRedesign?: boolean }>` - background-color: ${(props) => (props.isV2 ? '#fff' : 'inherit')}; +const PageContainer = styled.div<{ $isShowNavBarRedesign?: boolean }>` + background-color: #fff; ${(props) => props.$isShowNavBarRedesign && ` @@ -29,12 +28,12 @@ const PageContainer = styled.div<{ isV2: boolean; $isShowNavBarRedesign?: boolea ${(props) => !props.$isShowNavBarRedesign && ` - margin-right: ${props.isV2 ? '24px' : '0'}; - margin-bottom: ${props.isV2 ? '24px' : '0'}; + margin-right: 24px; + margin-bottom: 24px; `} border-radius: ${(props) => { - if (props.isV2 && props.$isShowNavBarRedesign) return props.theme.styles['border-radius-navbar-redesign']; - return props.isV2 ? '8px' : '0'; + if (props.$isShowNavBarRedesign) return props.theme.styles['border-radius-navbar-redesign']; + return '8px'; }}; `; @@ -75,7 +74,6 @@ const StyledSearchBar = styled(Input)` `; export const AnalyticsPage = () => { - const isV2 = useIsThemeV2(); const isShowNavBarRedesign = useShowNavBarRedesign(); const me = useUserContext(); const canManageDomains = me?.platformPrivileges?.createDomains; @@ -118,7 +116,7 @@ export const AnalyticsPage = () => { const isLoading = highlightLoading || chartLoading || domainLoading || metadataAnalyticsLoading; return ( - + {isLoading && } {highlightError && ( diff --git a/datahub-web-react/src/app/applications/CreateNewApplicationModal/ApplicationDetailsSection.tsx b/datahub-web-react/src/app/applications/CreateNewApplicationModal/ApplicationDetailsSection.tsx index 09f5dc1f90908f..5c0dd5c815cbae 100644 --- a/datahub-web-react/src/app/applications/CreateNewApplicationModal/ApplicationDetailsSection.tsx +++ b/datahub-web-react/src/app/applications/CreateNewApplicationModal/ApplicationDetailsSection.tsx @@ -2,7 +2,7 @@ import { Input } from '@components'; import React from 'react'; import styled from 'styled-components'; -export interface ApplicationDetailsProps { +interface ApplicationDetailsProps { applicationName: string; setApplicationName: React.Dispatch>; applicationDescription: string; diff --git a/datahub-web-react/src/app/auth/LogIn.tsx b/datahub-web-react/src/app/auth/LogIn.tsx index 4ad99d03cdd2e1..ddd2a8c47d179f 100644 --- a/datahub-web-react/src/app/auth/LogIn.tsx +++ b/datahub-web-react/src/app/auth/LogIn.tsx @@ -63,7 +63,7 @@ const SsoTextSpan = styled.span` padding-top: 6px; `; -export type LogInProps = Record; +type LogInProps = Record; export const LogIn: React.VFC = () => { const isLoggedIn = useReactiveVar(isLoggedInVar); diff --git a/datahub-web-react/src/app/auth/ResetCredentials.tsx b/datahub-web-react/src/app/auth/ResetCredentials.tsx index e66663d39717bb..0f1acc21f93ea0 100644 --- a/datahub-web-react/src/app/auth/ResetCredentials.tsx +++ b/datahub-web-react/src/app/auth/ResetCredentials.tsx @@ -50,7 +50,7 @@ const StyledFormItem = styled(Form.Item)` } `; -export type ResetCredentialsProps = Record; +type ResetCredentialsProps = Record; export const ResetCredentials: React.VFC = () => { const isLoggedIn = useReactiveVar(isLoggedInVar); diff --git a/datahub-web-react/src/app/auth/SignUp.tsx b/datahub-web-react/src/app/auth/SignUp.tsx index bbcc2a5c228630..8b6b569cb2c08b 100644 --- a/datahub-web-react/src/app/auth/SignUp.tsx +++ b/datahub-web-react/src/app/auth/SignUp.tsx @@ -65,7 +65,7 @@ const StyledFormItem = styled(Form.Item)` } `; -export type SignUpProps = Record; +type SignUpProps = Record; export const SignUp: React.VFC = () => { const history = useHistory(); diff --git a/datahub-web-react/src/app/browse/BrowseResultCard.tsx b/datahub-web-react/src/app/browse/BrowseResultCard.tsx index fc248d15637ce3..bcdbff95bcff6d 100644 --- a/datahub-web-react/src/app/browse/BrowseResultCard.tsx +++ b/datahub-web-react/src/app/browse/BrowseResultCard.tsx @@ -21,7 +21,7 @@ const ResultCard = styled(Card)` } `; -export interface BrowseResultProps { +interface BrowseResultProps { url: string; name: string; count?: number | undefined; diff --git a/datahub-web-react/src/app/buildEntityRegistry.ts b/datahub-web-react/src/app/buildEntityRegistry.ts deleted file mode 100644 index fd1c0b69384fe8..00000000000000 --- a/datahub-web-react/src/app/buildEntityRegistry.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { RoleEntity } from '@app/entity/Access/RoleEntity'; -import EntityRegistry from '@app/entity/EntityRegistry'; -import { ApplicationEntity } from '@app/entity/application/ApplicationEntity'; -import { BusinessAttributeEntity } from '@app/entity/businessAttribute/BusinessAttributeEntity'; -import { ChartEntity } from '@app/entity/chart/ChartEntity'; -import { ContainerEntity } from '@app/entity/container/ContainerEntity'; -import { DashboardEntity } from '@app/entity/dashboard/DashboardEntity'; -import { DataContractEntity } from '@app/entity/dataContract/DataContractEntity'; -import { DataFlowEntity } from '@app/entity/dataFlow/DataFlowEntity'; -import { DataJobEntity } from '@app/entity/dataJob/DataJobEntity'; -import { DataPlatformEntity } from '@app/entity/dataPlatform/DataPlatformEntity'; -import { DataPlatformInstanceEntity } from '@app/entity/dataPlatformInstance/DataPlatformInstanceEntity'; -import { DataProcessInstanceEntity } from '@app/entity/dataProcessInstance/DataProcessInstanceEntity'; -import { DataProductEntity } from '@app/entity/dataProduct/DataProductEntity'; -import { DatasetEntity } from '@app/entity/dataset/DatasetEntity'; -import { DomainEntity } from '@app/entity/domain/DomainEntity'; -import { ERModelRelationshipEntity } from '@app/entity/ermodelrelationships/ERModelRelationshipEntity'; -import GlossaryNodeEntity from '@app/entity/glossaryNode/GlossaryNodeEntity'; -import { GlossaryTermEntity } from '@app/entity/glossaryTerm/GlossaryTermEntity'; -import { GroupEntity } from '@app/entity/group/Group'; -import { MLFeatureEntity } from '@app/entity/mlFeature/MLFeatureEntity'; -import { MLFeatureTableEntity } from '@app/entity/mlFeatureTable/MLFeatureTableEntity'; -import { MLModelEntity } from '@app/entity/mlModel/MLModelEntity'; -import { MLModelGroupEntity } from '@app/entity/mlModelGroup/MLModelGroupEntity'; -import { MLPrimaryKeyEntity } from '@app/entity/mlPrimaryKey/MLPrimaryKeyEntity'; -import { RestrictedEntity } from '@app/entity/restricted/RestrictedEntity'; -import { SchemaFieldPropertiesEntity } from '@app/entity/schemaField/SchemaFieldPropertiesEntity'; -import { StructuredPropertyEntity } from '@app/entity/structuredProperty/StructuredPropertyEntity'; -import { TagEntity } from '@app/entity/tag/Tag'; -import { UserEntity } from '@app/entity/user/User'; - -export default function buildEntityRegistry() { - const registry = new EntityRegistry(); - registry.register(new DatasetEntity()); - registry.register(new DataContractEntity()); - registry.register(new DashboardEntity()); - registry.register(new ChartEntity()); - registry.register(new UserEntity()); - registry.register(new GroupEntity()); - registry.register(new TagEntity()); - registry.register(new DataFlowEntity()); - registry.register(new DataJobEntity()); - registry.register(new GlossaryTermEntity()); - registry.register(new MLFeatureEntity()); - registry.register(new MLPrimaryKeyEntity()); - registry.register(new MLFeatureTableEntity()); - registry.register(new MLModelEntity()); - registry.register(new MLModelGroupEntity()); - registry.register(new DomainEntity()); - registry.register(new ContainerEntity()); - registry.register(new GlossaryNodeEntity()); - registry.register(new RoleEntity()); - registry.register(new DataPlatformEntity()); - registry.register(new DataProductEntity()); - registry.register(new DataPlatformInstanceEntity()); - registry.register(new ERModelRelationshipEntity()); - registry.register(new RestrictedEntity()); - registry.register(new BusinessAttributeEntity()); - registry.register(new SchemaFieldPropertiesEntity()); - registry.register(new StructuredPropertyEntity()); - registry.register(new DataProcessInstanceEntity()); - registry.register(new ApplicationEntity()); - return registry; -} diff --git a/datahub-web-react/src/app/businessAttribute/AttributeBrowser.tsx b/datahub-web-react/src/app/businessAttribute/AttributeBrowser.tsx deleted file mode 100644 index 451ff4f2050fd7..00000000000000 --- a/datahub-web-react/src/app/businessAttribute/AttributeBrowser.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, { useEffect } from 'react'; -import styled from 'styled-components/macro'; - -import AttributeItem from '@app/businessAttribute/AttributeItem'; -import { sortBusinessAttributes } from '@app/businessAttribute/businessAttributeUtils'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { ListBusinessAttributesQuery, useListBusinessAttributesQuery } from '@graphql/businessAttribute.generated'; - -const BrowserWrapper = styled.div` - color: #262626; - font-size: 12px; - max-height: calc(100% - 47px); - padding: 10px 20px 20px 20px; - overflow: auto; -`; - -interface Props { - isSelecting?: boolean; - hideTerms?: boolean; - refreshBrowser?: boolean; - selectAttribute?: (urn: string, displayName: string) => void; - attributeData?: ListBusinessAttributesQuery; -} - -function AttributeBrowser(props: Props) { - const { isSelecting, hideTerms, refreshBrowser, selectAttribute, attributeData } = props; - - const { refetch: refetchAttributes } = useListBusinessAttributesQuery({ - variables: { - start: 0, - count: 10, - query: '*', - }, - }); - - const displayedAttributes = attributeData?.listBusinessAttributes?.businessAttributes || []; - - const entityRegistry = useEntityRegistry(); - const sortedAttributes = displayedAttributes.sort((termA, termB) => - sortBusinessAttributes(entityRegistry, termA, termB), - ); - - useEffect(() => { - if (refreshBrowser) { - refetchAttributes(); - } - }, [refreshBrowser, refetchAttributes]); - - return ( - - {!hideTerms && - sortedAttributes.map((attribute) => ( - - ))} - - ); -} - -export default AttributeBrowser; diff --git a/datahub-web-react/src/app/businessAttribute/AttributeItem.tsx b/datahub-web-react/src/app/businessAttribute/AttributeItem.tsx deleted file mode 100644 index 1b3e3e73a24f87..00000000000000 --- a/datahub-web-react/src/app/businessAttribute/AttributeItem.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react'; -import styled from 'styled-components/macro'; - -import { ANTD_GRAY } from '@app/entity/shared/constants'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -const AttributeWrapper = styled.div` - font-weight: normal; - margin-bottom: 4px; -`; - -const nameStyles = ` - color: #262626; - display: inline-block; - height: 100%; - padding: 3px 4px; - width: 100%; -`; - -export const NameWrapper = styled.span<{ showSelectStyles?: boolean }>` - ${nameStyles} - - &:hover { - ${(props) => - props.showSelectStyles && - ` - background-color: ${ANTD_GRAY[3]}; - cursor: pointer; - `} - } -`; - -interface Props { - attribute: any; - isSelecting?: boolean; - selectAttribute?: (urn: string, displayName: string) => void; -} - -function AttributeItem(props: Props) { - const { attribute, isSelecting, selectAttribute } = props; - - const entityRegistry = useEntityRegistry(); - - function handleSelectAttribute() { - if (selectAttribute) { - const displayName = entityRegistry.getDisplayName(attribute.type, attribute); - selectAttribute(attribute.urn, displayName); - } - } - - return ( - - {isSelecting && ( - - {entityRegistry.getDisplayName(attribute.type, attribute)} - - )} - - ); -} - -export default AttributeItem; diff --git a/datahub-web-react/src/app/businessAttribute/businessAttributeUtils.ts b/datahub-web-react/src/app/businessAttribute/businessAttributeUtils.ts index 20b56bd5cee854..30f723add3db34 100644 --- a/datahub-web-react/src/app/businessAttribute/businessAttributeUtils.ts +++ b/datahub-web-react/src/app/businessAttribute/businessAttributeUtils.ts @@ -1,12 +1,6 @@ import EntityRegistry from '@app/entity/EntityRegistry'; -import { Entity, EntityType } from '@types'; - -export function sortBusinessAttributes(entityRegistry: EntityRegistry, nodeA?: Entity | null, nodeB?: Entity | null) { - const nodeAName = entityRegistry.getDisplayName(EntityType.BusinessAttribute, nodeA) || ''; - const nodeBName = entityRegistry.getDisplayName(EntityType.BusinessAttribute, nodeB) || ''; - return nodeAName.localeCompare(nodeBName); -} +import { EntityType } from '@types'; export function getRelatedEntitiesUrl(entityRegistry: EntityRegistry, urn: string) { return `${entityRegistry.getEntityUrl(EntityType.BusinessAttribute, urn)}/${encodeURIComponent( diff --git a/datahub-web-react/src/app/context/userContext.tsx b/datahub-web-react/src/app/context/userContext.tsx index a5c9fa63097e18..670f12215b0210 100644 --- a/datahub-web-react/src/app/context/userContext.tsx +++ b/datahub-web-react/src/app/context/userContext.tsx @@ -43,7 +43,7 @@ export type UserContextType = { refetchUser: () => any; }; -export const DEFAULT_LOCAL_STATE: LocalState = { +const DEFAULT_LOCAL_STATE: LocalState = { selectedViewUrn: undefined, }; @@ -58,7 +58,7 @@ export const DEFAULT_STATE: State = { customState: DEFAULT_CUSTOM_STATE, }; -export const DEFAULT_CONTEXT = { +const DEFAULT_CONTEXT = { loaded: false, urn: undefined, user: undefined, diff --git a/datahub-web-react/src/app/dataviz/ChartCard.tsx b/datahub-web-react/src/app/dataviz/ChartCard.tsx deleted file mode 100644 index 033797dbd0dbd8..00000000000000 --- a/datahub-web-react/src/app/dataviz/ChartCard.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -import { ANTD_GRAY } from '@app/entity/shared/constants'; - -const Card = styled.div` - display: flex; - flex-direction: column; - padding: 1rem; - background-color: white; - box-shadow: 0px 3px 6px 0px ${ANTD_GRAY[5]}; - border-radius: 8px; - - text { - fill: ${ANTD_GRAY[8]}; - font-weight: 400 !important; - } -`; - -const Header = styled.div` - display: flex; - align-items: center; - justify-content: space-between; -`; - -const Body = styled.div` - position: relative; - display: flex; - align-items: center; - justify-content: center; -`; - -const Heading = styled(Typography.Text)` - display: block; - font-size: 14px;s - font-weight: 600; - color: ${ANTD_GRAY[8]}; - min-width: 300px; -`; - -interface Props { - title: string; - chart: React.ReactElement; - flex?: number; -} - -export const ChartCard = ({ title, chart, flex = 1 }: Props) => ( - -
- {title} -
- {chart} -
-); diff --git a/datahub-web-react/src/app/dataviz/ChartLoading.tsx b/datahub-web-react/src/app/dataviz/ChartLoading.tsx deleted file mode 100644 index 393791aba6603f..00000000000000 --- a/datahub-web-react/src/app/dataviz/ChartLoading.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import React from 'react'; - -export const ChartLoading = () => <>Chart; diff --git a/datahub-web-react/src/app/dataviz/Legend.tsx b/datahub-web-react/src/app/dataviz/Legend.tsx deleted file mode 100644 index 77ba80835502f2..00000000000000 --- a/datahub-web-react/src/app/dataviz/Legend.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { LegendItem, LegendLabel, LegendOrdinal } from '@visx/legend'; -import React from 'react'; - -interface Props { - scale: any; -} - -export const Legend = ({ scale }: Props) => { - return ( - - {(labels) => ( -
- {labels.map((label) => ( - - - - - - {label.text} - - - ))} -
- )} -
- ); -}; diff --git a/datahub-web-react/src/app/dataviz/bar/BarChart.tsx b/datahub-web-react/src/app/dataviz/bar/BarChart.tsx deleted file mode 100644 index 1e97dcd1070278..00000000000000 --- a/datahub-web-react/src/app/dataviz/bar/BarChart.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { ParentSize } from '@visx/responsive'; -import { Axis, BarSeries, BarStack, Grid, XYChart } from '@visx/xychart'; -import dayjs from 'dayjs'; -import React from 'react'; - -import { Legend } from '@app/dataviz/Legend'; -import { ChartWrapper } from '@app/dataviz/components'; -import { abbreviateNumber } from '@app/dataviz/utils'; - -export const BarChart = ({ - data, - dataKeys, - xAccessor, - yAccessor, - colorAccessor, - tickFormat = 'MMM D', - yAxisLabel, -}: { - data: any; - dataKeys: DataKeys; - xAccessor: (d: Data) => string; - yAccessor: (d: any, key: string) => string; - colorAccessor: (d: string) => string; - tickFormat?: string; - yAxisLabel?: string; -}) => { - if (!Array.isArray(dataKeys)) throw new Error('Datakeys must be an array'); - - const multipleData = dataKeys.length > 1; - const margin = { top: 20, right: 20, bottom: 30, left: 60 }; - const tickCount = Math.max(1, Math.min(data.length, 10)); - - return ( - - - {({ width }) => { - if (!width) return null; - - return ( - <> - - - {multipleData ? ( - - {dataKeys - .slice() - .reverse() - .map((dK) => ( - yAccessor(d, dK)} - colorAccessor={() => colorAccessor(dK)} - /> - ))} - - ) : ( - yAccessor(d, dataKeys[0])} - colorAccessor={() => colorAccessor(dataKeys[0])} - radiusTop - /> - )} - dayjs(d).format(tickFormat)} - hideAxisLine - /> - {/* Left Axis is for COUNT/NUMBER values only */} - (Number.isInteger(value) ? value : '')} - tickComponent={({ x, y, formattedValue }) => ( - - {abbreviateNumber(formattedValue)} - - )} - hideAxisLine - /> - - - - ); - }} - - - ); -}; diff --git a/datahub-web-react/src/app/dataviz/bar/HorizontalBarChart.tsx b/datahub-web-react/src/app/dataviz/bar/HorizontalBarChart.tsx deleted file mode 100644 index 9119708e738d47..00000000000000 --- a/datahub-web-react/src/app/dataviz/bar/HorizontalBarChart.tsx +++ /dev/null @@ -1,154 +0,0 @@ -/* eslint-disable @typescript-eslint/dot-notation, no-param-reassign */ -import { AxisLeft } from '@visx/axis'; -import { ParentSize } from '@visx/responsive'; -import { scaleBand, scaleLinear } from '@visx/scale'; -import { Bar, BarStackHorizontal } from '@visx/shape'; -import { Grid, XYChart } from '@visx/xychart'; -import React from 'react'; - -import { Legend } from '@app/dataviz/Legend'; -import { ChartWrapper } from '@app/dataviz/components'; -import { COMPLETED_COLOR, IN_PROGRESS_COLOR, NOT_STARTED_COLOR } from '@app/dataviz/constants'; - -export const HorizontalBarChart = ({ - data, - dataKeys, - yAccessor, - colorAccessor, -}: { - data: Data[]; - dataKeys: DataKeys; - yAccessor: (d: Data) => string; - colorAccessor: (d: string) => string; -}) => { - if (!data || !data.length || !dataKeys) return null; - if (!Array.isArray(dataKeys)) throw new Error('Datakeys must be an array'); - - const totals = data.reduce((allTotals, currentDate) => { - const total = dataKeys.reduce((dailyTotal, k) => { - dailyTotal += Number(currentDate[k]); - return dailyTotal; - }, 0); - allTotals.push(total); - return allTotals; - }, [] as number[]); - - const xScale = scaleLinear({ - domain: [0, Math.max(...totals)], - nice: true, - }); - - const yScale = scaleBand({ - domain: data.map(yAccessor), - padding: 0.2, - }); - - return ( - - - {({ width: parentWidth }) => { - if (!parentWidth) return null; - - const margin = { top: 20, right: 0, bottom: 0, left: 120 }; - const baseHeight = data.length < 2 ? 180 : 280; - const xMax = parentWidth - margin.left - margin.right; - const yMax = baseHeight - margin.top - margin.bottom; - - xScale.rangeRound([0, xMax]); - yScale.rangeRound([yMax, 0]); - - return ( - <> - - - - - {(barStacks) => - barStacks.map((barStack) => - barStack.bars.map((bar) => { - // Use the bar color to determine which label to display for Doc Initiatives - let label = null; - if (bar.color === COMPLETED_COLOR) label = bar.bar.data['Completed']; - if (bar.color === IN_PROGRESS_COLOR) - label = bar.bar.data['In Progress']; - if (bar.color === NOT_STARTED_COLOR) - label = bar.bar.data['Not Started']; - - if (label === '0') return null; - - const { x, y, width, height } = bar; - const { left } = margin; - - const newX = x + left + 10; - const barWidth = width + 5; - - let textX = barWidth <= 20 ? newX + barWidth - 5 : newX + barWidth - 15; - if (barWidth > 300) textX = newX + barWidth - 20; - - return ( - - - {label && ( - - {label} - - )} - - ); - }), - ) - } - - - - - - ); - }} - - - ); -}; diff --git a/datahub-web-react/src/app/dataviz/bar/HorizontalFullBarChart.tsx b/datahub-web-react/src/app/dataviz/bar/HorizontalFullBarChart.tsx deleted file mode 100644 index 81fca3e6ccb5e0..00000000000000 --- a/datahub-web-react/src/app/dataviz/bar/HorizontalFullBarChart.tsx +++ /dev/null @@ -1,154 +0,0 @@ -/* eslint-disable @typescript-eslint/dot-notation, no-param-reassign */ -import { AxisLeft } from '@visx/axis'; -import { ParentSize } from '@visx/responsive'; -import { scaleBand, scaleLinear } from '@visx/scale'; -import { Bar, BarStackHorizontal } from '@visx/shape'; -import { Grid, XYChart } from '@visx/xychart'; -import React from 'react'; - -import { Legend } from '@app/dataviz/Legend'; -import { ChartWrapper } from '@app/dataviz/components'; -import { COMPLETED_COLOR, IN_PROGRESS_COLOR, NOT_STARTED_COLOR } from '@app/dataviz/constants'; - -export const HorizontalFullBarChart = ({ - data, - dataKeys, - yAccessor, - colorAccessor, -}: { - data: Data[]; - dataKeys: DataKeys; - yAccessor: (d: Data) => string; - colorAccessor: (d: string) => string; -}) => { - if (!data || !data.length || !dataKeys) return null; - if (!Array.isArray(dataKeys)) throw new Error('Datakeys must be an array'); - - const totals = data.reduce((allTotals, currentDate) => { - const total = dataKeys.reduce((dailyTotal, k) => { - dailyTotal += Number(currentDate[k]); - return dailyTotal; - }, 0); - allTotals.push(total); - return allTotals; - }, [] as number[]); - - const xScale = scaleLinear({ - domain: [0, Math.max(...totals)], - nice: true, - }); - - const yScale = scaleBand({ - domain: data.map(yAccessor), - padding: 0.2, - }); - - return ( - - - {({ width: parentWidth }) => { - if (!parentWidth) return null; - - const margin = { top: 20, right: 0, bottom: 0, left: 120 }; - const baseHeight = 280; - const calculatedHeight = data.length > 4 ? baseHeight + data.length * 10 : baseHeight; - const xMax = parentWidth - margin.left - margin.right; - const yMax = calculatedHeight - margin.top - margin.bottom; - - xScale.rangeRound([0, xMax]); - yScale.rangeRound([yMax, 0]); - - return ( - <> - - - - - {(barStacks) => - barStacks.map((barStack) => - barStack.bars.map((bar) => { - // Use the bar color to determine which label to display for Doc Initiatives - let label = null; - if (bar.color === COMPLETED_COLOR) label = bar.bar.data['Completed']; - if (bar.color === IN_PROGRESS_COLOR) - label = bar.bar.data['In Progress']; - if (bar.color === NOT_STARTED_COLOR) - label = bar.bar.data['Not Started']; - - if (label === '0') return null; - - const { x, y, width, height } = bar; - const { left } = margin; - - const newX = x + left + 10; - const barWidth = width + 5; - - let textX = barWidth <= 20 ? newX + barWidth - 5 : newX + barWidth - 10; - if (barWidth > 300) textX = newX + barWidth - 20; - - return ( - - - {label && ( - - {label} - - )} - - ); - }), - ) - } - - - - - - ); - }} - - - ); -}; diff --git a/datahub-web-react/src/app/dataviz/candle/CandleStick.tsx b/datahub-web-react/src/app/dataviz/candle/CandleStick.tsx deleted file mode 100644 index 35d4dd3a8bce24..00000000000000 --- a/datahub-web-react/src/app/dataviz/candle/CandleStick.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { GlyphCircle, GlyphDiamond } from '@visx/glyph'; -import { GlyphCircleProps } from '@visx/glyph/lib/glyphs/GlyphCircle'; -import { GlyphDiamondProps } from '@visx/glyph/lib/glyphs/GlyphDiamond'; -import { Group } from '@visx/group'; -import { Bar } from '@visx/shape'; -import { BarProps } from '@visx/shape/lib/shapes/Bar'; -import { AddSVGProps } from '@visx/shape/lib/types'; -import React from 'react'; - -type DiamondProps = GlyphDiamondProps & Omit, keyof GlyphDiamondProps>; -type CircleProps = GlyphCircleProps & Omit, keyof GlyphCircleProps>; -type CandleBarProps = AddSVGProps; - -type Props = { - parentChartHeight: number; - candleHeight: number; - barWidth: number; - shapeSize: number; - leftOffset: number; - color: string; - shape: - | { - type: 'diamond'; - extraProps?: DiamondProps; - } - | { - type: 'circle'; - extraProps?: CircleProps; - }; - markerOverlapPx?: number; - extraBarProps?: CandleBarProps; - opacity?: number; - wrapper?: (children: JSX.Element) => JSX.Element; -}; -export const CandleStick = ({ - parentChartHeight, - candleHeight, - barWidth, - shapeSize, - leftOffset, - color, - shape, - wrapper, - opacity, - markerOverlapPx, - extraBarProps, -}: Props) => { - const yOffset = parentChartHeight - candleHeight; - - const shapeProps: DiamondProps | CircleProps = { - top: yOffset, - left: leftOffset, - fill: color, - stroke: 'white', - strokeWidth: (markerOverlapPx ?? 1) > 1 ? 1 / (markerOverlapPx ?? 1) : 1, - filter: markerOverlapPx ? undefined : 'drop-shadow(0px 1px 2.5px rgb(0 0 0 / 0.1))', - size: shapeSize, - ...shape.extraProps, - }; - const barProps: CandleBarProps = { - height: candleHeight, - width: barWidth, - x: leftOffset - barWidth / 2, - y: yOffset, - fill: color, - stroke: 'white', - strokeWidth: (markerOverlapPx ?? 1) > 1 ? 1 / (markerOverlapPx ?? 1) : 1, - ...extraBarProps, - }; - - const candleGroup = ( - - - {shape.type === 'diamond' ? : } - - ); - return wrapper ? wrapper(candleGroup) : candleGroup; -}; diff --git a/datahub-web-react/src/app/dataviz/components.ts b/datahub-web-react/src/app/dataviz/components.ts deleted file mode 100644 index 9be7476522ce17..00000000000000 --- a/datahub-web-react/src/app/dataviz/components.ts +++ /dev/null @@ -1,24 +0,0 @@ -import styled from 'styled-components'; - -export const ChartWrapper = styled.div` - width: 100%; - height: 100%; - position: relative; - - .horizontalBarChartTick { - foreignObject { - text-align: right; - } - } - - .horizontalBarChartInlineLabel { - fill: #fff; - font-weight: 600; - font-family: 'Manrope', sans-serif; - } - - .visx-axis-label { - font-weight: 600 !important; - font-family: 'Manrope', sans-serif !important; - } -`; diff --git a/datahub-web-react/src/app/dataviz/constants.ts b/datahub-web-react/src/app/dataviz/constants.ts deleted file mode 100644 index ba8b2d642ca68e..00000000000000 --- a/datahub-web-react/src/app/dataviz/constants.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type STATUS = 'Completed' | 'In Progress' | 'Not Started'; - -export const COMPLETED_COLOR = '#20D3BD'; -export const IN_PROGRESS_COLOR = '#7532A4'; -export const NOT_STARTED_COLOR = '#1677FF'; diff --git a/datahub-web-react/src/app/dataviz/index.ts b/datahub-web-react/src/app/dataviz/index.ts deleted file mode 100644 index 070c56bb50351e..00000000000000 --- a/datahub-web-react/src/app/dataviz/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './ChartCard'; -export * from './ChartLoading'; -export * from './bar/BarChart'; -export * from './bar/HorizontalBarChart'; -export * from './bar/HorizontalFullBarChart'; -export * from './pie/PieChart'; diff --git a/datahub-web-react/src/app/dataviz/line/SimpleLineChart.tsx b/datahub-web-react/src/app/dataviz/line/SimpleLineChart.tsx deleted file mode 100644 index e5329505aa0fdd..00000000000000 --- a/datahub-web-react/src/app/dataviz/line/SimpleLineChart.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { curveCatmullRom } from '@visx/curve'; -import { MarkerCircle } from '@visx/marker'; -import { ParentSize } from '@visx/responsive'; -import { scaleLinear, scaleTime } from '@visx/scale'; -import { LinePath } from '@visx/shape'; -import { extent, max } from '@visx/vendor/d3-array'; -import React from 'react'; - -import { ChartWrapper } from '@app/dataviz/components'; - -interface Props { - data: any; -} - -type Data = { - date: string; - value: number; -}; - -export const SimpleLineChart = ({ data }: Props) => { - const getDate = (d: Data) => new Date(d.date); - const getValue = (d: Data) => d.value; - - const markerEnd = 'url(#marker-circle)'; - - return ( - - - {({ width }) => { - if (!width) return null; - - const height = 20; - - const xScale = scaleTime({ - range: [0, width - 10], - domain: extent(data, getDate) as [Date, Date], - }); - - const yScale = scaleLinear({ - range: [height / 2, 5], - domain: [0, (max(data, getValue) || 0) as number], - nice: true, - }); - - return ( - - - - - - - - - xScale(getDate(d))} - y={(d: Data) => yScale(getValue(d))} - curve={curveCatmullRom} - markerEnd={markerEnd} - stroke="#9F33CC" - /> - - ); - }} - - - ); -}; diff --git a/datahub-web-react/src/app/dataviz/pie/PieChart.tsx b/datahub-web-react/src/app/dataviz/pie/PieChart.tsx deleted file mode 100644 index e89fef1f6790da..00000000000000 --- a/datahub-web-react/src/app/dataviz/pie/PieChart.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { Annotation, Connector, Label } from '@visx/annotation'; -import { Pie } from '@visx/shape'; -import { PieArcDatum } from '@visx/shape/lib/shapes/Pie'; -import React from 'react'; - -import { useDataAnnotationPosition } from '@app/dataviz/pie/usePieDataAnnotation'; - -const PieDataAnnotation = ({ - title, - arc, - path, - subtitle, -}: { - title: string; - path: any; - arc: PieArcDatum<{ [x: string]: string }>; - subtitle?: string; -}) => { - const { surfaceX, surfaceY, labelX, labelY } = useDataAnnotationPosition({ arc, path }); - - return ( - - - - ); -}; - -export const PieChart = ({ data }: any) => { - const width = 380; - const height = 280; - const margin = { top: 50, right: 50, bottom: 50, left: 50 }; - - const innerWidth = width - margin.left - margin.right; - const innerHeight = height - margin.top - margin.bottom; - const radius = Math.min(innerWidth, innerHeight) * 0.45; - const centerY = innerHeight / 2; - const centerX = innerWidth / 2; - - const pieSortValues = (a, b) => b - a; - const value = (d) => d.value; - - const createValueLabel = (count: number) => { - let total = 0; - data.forEach((d) => { - total += d.value; - }); - const percent = Math.round((count / total) * 100); - return `${percent}% (${count.toLocaleString()})`; - }; - - return ( - - - - {(pie) => { - return pie.arcs.map((arc) => { - const { name } = arc.data; - const { color } = arc.data; - const arcPath = pie.path(arc); - return ( - - - {arc.endAngle - arc.startAngle !== 0 ? ( - - ) : null} - - ); - }); - }} - - - - ); -}; diff --git a/datahub-web-react/src/app/dataviz/pie/usePieDataAnnotation.ts b/datahub-web-react/src/app/dataviz/pie/usePieDataAnnotation.ts deleted file mode 100644 index 1aeed5f169a848..00000000000000 --- a/datahub-web-react/src/app/dataviz/pie/usePieDataAnnotation.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { PieArcDatum } from '@visx/shape/lib/shapes/Pie'; - -const useDataAnnotationPosition = ({ - arc, - path, -}: { - arc: PieArcDatum<{ [x: string]: string }>; - path: any; -}): { - labelX: number; - labelY: number; - surfaceX: number; - surfaceY: number; -} => { - const middleAngle = Math.PI / 2 - (arc.startAngle + (arc.endAngle - arc.startAngle) / 2); - - const outerRadius: number = path.outerRadius()(arc); - - const normalX = Math.cos(middleAngle); - const normalY = Math.sin(-middleAngle); - - const labelX = normalX * outerRadius * 0.1 * (middleAngle < Math.PI ? 1 : -1); - const labelY = normalY * outerRadius * 0.1; - - const surfaceX = normalX * outerRadius; - const surfaceY = normalY * outerRadius; - - return { - labelX, - labelY, - surfaceX, - surfaceY, - }; -}; - -export { useDataAnnotationPosition }; diff --git a/datahub-web-react/src/app/dataviz/stat/SingleStat.tsx b/datahub-web-react/src/app/dataviz/stat/SingleStat.tsx deleted file mode 100644 index bce1553c2d7f57..00000000000000 --- a/datahub-web-react/src/app/dataviz/stat/SingleStat.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react'; - -export const SingleStat = () => { - return <>stat; -}; diff --git a/datahub-web-react/src/app/dataviz/utils.ts b/datahub-web-react/src/app/dataviz/utils.ts index 7ea4544375f34d..b631206beccee9 100644 --- a/datahub-web-react/src/app/dataviz/utils.ts +++ b/datahub-web-react/src/app/dataviz/utils.ts @@ -1,41 +1,4 @@ -import { scaleOrdinal } from '@visx/scale'; -import dayjs from 'dayjs'; -import { COMPLETED_COLOR, IN_PROGRESS_COLOR, NOT_STARTED_COLOR } from '@app/dataviz/constants'; - -// Mock Data Util -export const generateDateSeries = (numOfDays) => - Array(numOfDays) - .fill(0) - .map((d, i) => ({ - date: dayjs(new Date(Date.now() - 24 * 60 * 60 * 1000 * i)).format(), - value: Math.round(Math.max(10, Math.random() * 100 || 0)), - })); - -// Status Ordinal Scale -export const statusOrdinalScale = scaleOrdinal({ - domain: ['Not Started', 'In Progress', 'Completed'], - range: [NOT_STARTED_COLOR, IN_PROGRESS_COLOR, COMPLETED_COLOR], -}); - -// private utils to help with rounding y axis numbers -const NUMERICAL_ABBREVIATIONS = ['k', 'm', 'b', 't']; -function roundToPrecision(n: number, precision: number) { - const prec = 10 ** precision; - return Math.round(n * prec) / prec; -} - -/** - * ie. 24044 -> 24k - * @param n - */ -export const truncateNumberForDisplay = (n: number, skipRounding?: boolean): string => { - let base = Math.floor(Math.log(Math.abs(n)) / Math.log(1000)); - const suffix = NUMERICAL_ABBREVIATIONS[Math.min(NUMERICAL_ABBREVIATIONS.length - 1, base - 1)]; - base = NUMERICAL_ABBREVIATIONS.indexOf(suffix) + 1; - const roundedNumber = skipRounding ? n : Math.round(n); - return suffix ? roundToPrecision(n / 1000 ** base, 0) + suffix : `${roundedNumber}`; -}; // Number Abbreviations export const abbreviateNumber = (str) => { @@ -48,83 +11,3 @@ export const abbreviateNumber = (str) => { const shortNumber = number / 10 ** (index * 3); return `${shortNumber}${suffix}`; }; - -// Byte Abbreviations -export const abbreviateBytes = (str): string => { - const bytes = parseFloat(str); - if (Number.isNaN(bytes)) return str; - if (bytes < 1024) return `${bytes} B`; - const units = ['B', 'KB', 'MB', 'GB', 'TB']; - const index = Math.floor(Math.log(bytes) / Math.log(1024)); - const shortBytes = bytes / 1024 ** index; - - return `${abbreviateNumber(shortBytes.toFixed())} ${units[index]}`; -}; - -type CalculateYScaleExtentForChartOptions = { - defaultYValue: number; - // Between 0-1, represents what % of the chart's height should be empty above and below. - // ie. if this is .1, then 10% of the chart's height will be empty. - yScaleBufferFactor?: number; -}; - -/** - * Creates a yscale range for charts, with optional buffers - * @param yValues - * @param options - */ -export const calculateYScaleExtentForChart = ( - yValues: number[], - options: CalculateYScaleExtentForChartOptions = { defaultYValue: 0 }, -): { min: number; max: number } => { - let min = yValues.length ? Math.min(...yValues) : options.defaultYValue; - let max = yValues.length ? Math.max(...yValues) : options.defaultYValue; - - // Add some extra range above and below so things aren't pushed to the edge - const { yScaleBufferFactor } = options; - if (yScaleBufferFactor) { - let yScaleBuffer = 0; - // Edge case: if max and min are the same, add some buffer above and below so the points are nicely centered - if (max === min) { - // Ie. Let's say max/min=1.5B. In this case we want to add ~100M buffer and and below - // This will make the y-axis display 1.4B at the bottom and 1.6B at the top, - // While nicely centering the data points. - const decimalPlaceValue = max.toString().length; - yScaleBuffer = 10 ** (decimalPlaceValue - 1); - } else { - // By default, the chart will put the min at the bottom edge and the max at the top edge. - // So if yScaleBufferFactor=0.1, then we want 10% of the chart at the top and bottom to be empty - const distance = max - min; - const newDistance = distance / (1 - yScaleBufferFactor); - yScaleBuffer = newDistance * yScaleBufferFactor; - } - - min -= yScaleBuffer; - max += yScaleBuffer; - } - - return { min, max }; -}; - -/** - * Gets the px overlap between two markers - * @param marker1 - * @param marker2 - * @returns {number | undefined} undefined if no overlap - */ -export function calculateOverlapBetweenTwoMarkers( - marker1: { xOffset: number; width: number }, - marker2: { xOffset: number; width: number }, -): undefined | number { - let markerOverlapPx: number | undefined; - - // Take width of the left and right half of the two markers (where they will collide) - const netWidth = marker1.width / 2 + marker2.width / 2; - - // Calculate distance and potential overlap - const distance = Math.abs(marker1.xOffset - marker2.xOffset); - if (distance < netWidth) { - markerOverlapPx = netWidth - distance; - } - return markerOverlapPx; -} diff --git a/datahub-web-react/src/app/domain/CreateDomainModal.tsx b/datahub-web-react/src/app/domain/CreateDomainModal.tsx deleted file mode 100644 index da49f25058bf30..00000000000000 --- a/datahub-web-react/src/app/domain/CreateDomainModal.tsx +++ /dev/null @@ -1,232 +0,0 @@ -import { Button, Collapse, Form, Input, Modal, Tag, Typography, message } from 'antd'; -import React, { useState } from 'react'; -import styled from 'styled-components'; - -import analytics, { EventType } from '@app/analytics'; -import { useDomainsContext } from '@app/domain/DomainsContext'; -import DomainParentSelect from '@app/entity/shared/EntityDropdown/DomainParentSelect'; -import { validateCustomUrnId } from '@app/shared/textUtil'; -import { useEnterKeyListener } from '@app/shared/useEnterKeyListener'; -import { useIsNestedDomainsEnabled } from '@app/useAppConfig'; - -import { useCreateDomainMutation } from '@graphql/domain.generated'; - -const SuggestedNamesGroup = styled.div` - margin-top: 8px; -`; - -const ClickableTag = styled(Tag)` - :hover { - cursor: pointer; - } -`; - -const FormItem = styled(Form.Item)` - .ant-form-item-label { - padding-bottom: 2px; - } -`; - -const FormItemWithMargin = styled(FormItem)` - margin-bottom: 16px; -`; - -const FormItemNoMargin = styled(FormItem)` - margin-bottom: 0; -`; - -const FormItemLabel = styled(Typography.Text)` - font-weight: 600; - color: #373d44; -`; - -const AdvancedLabel = styled(Typography.Text)` - color: #373d44; -`; - -type Props = { - onClose: () => void; - onCreate: ( - urn: string, - id: string | undefined, - name: string, - description: string | undefined, - parentDomain?: string, - ) => void; -}; - -const SUGGESTED_DOMAIN_NAMES = ['Engineering', 'Marketing', 'Sales', 'Product']; - -const ID_FIELD_NAME = 'id'; -const NAME_FIELD_NAME = 'name'; -const DESCRIPTION_FIELD_NAME = 'description'; - -export default function CreateDomainModal({ onClose, onCreate }: Props) { - const isNestedDomainsEnabled = useIsNestedDomainsEnabled(); - const [createDomainMutation] = useCreateDomainMutation(); - const { entityData } = useDomainsContext(); - const [selectedParentUrn, setSelectedParentUrn] = useState( - (isNestedDomainsEnabled && entityData?.urn) || '', - ); - const [createButtonEnabled, setCreateButtonEnabled] = useState(false); - const [form] = Form.useForm(); - - const onCreateDomain = () => { - createDomainMutation({ - variables: { - input: { - id: form.getFieldValue(ID_FIELD_NAME), - name: form.getFieldValue(NAME_FIELD_NAME), - description: form.getFieldValue(DESCRIPTION_FIELD_NAME), - parentDomain: selectedParentUrn || undefined, - }, - }, - }) - .then(({ data, errors }) => { - if (!errors) { - analytics.event({ - type: EventType.CreateDomainEvent, - parentDomainUrn: selectedParentUrn || undefined, - }); - message.success({ - content: `Created domain!`, - duration: 3, - }); - onCreate( - data?.createDomain || '', - form.getFieldValue(ID_FIELD_NAME), - form.getFieldValue(NAME_FIELD_NAME), - form.getFieldValue(DESCRIPTION_FIELD_NAME), - selectedParentUrn || undefined, - ); - form.resetFields(); - } - }) - .catch((e) => { - message.destroy(); - message.error({ content: `Failed to create Domain!: \n ${e.message || ''}`, duration: 3 }); - }); - onClose(); - }; - - // Handle the Enter press - useEnterKeyListener({ - querySelectorToExecuteClick: '#createDomainButton', - }); - - return ( - - - - - } - > -
{ - setCreateButtonEnabled(!form.getFieldsError().some((field) => field.errors.length > 0)); - }} - > - {isNestedDomainsEnabled && ( - Parent (optional)}> - - - )} - Name}> - - - - - {SUGGESTED_DOMAIN_NAMES.map((name) => { - return ( - { - form.setFieldsValue({ - name, - }); - setCreateButtonEnabled(true); - }} - > - {name} - - ); - })} - - - Description} - help="You can always change the description later." - > - - - - - - Advanced Options} key="1"> - Domain Id} - help="By default, a random UUID will be generated to uniquely identify this domain. If - you'd like to provide a custom id instead to more easily keep track of this domain, - you may provide it here. Be careful, you cannot easily change the domain id after - creation." - > - ({ - validator(_, value) { - if (value && validateCustomUrnId(value)) { - return Promise.resolve(); - } - return Promise.reject(new Error('Please enter a valid Domain id')); - }, - }), - ]} - > - - - - - -
-
- ); -} diff --git a/datahub-web-react/src/app/domain/DomainItemMenu.tsx b/datahub-web-react/src/app/domain/DomainItemMenu.tsx deleted file mode 100644 index 99ccd357b0cabc..00000000000000 --- a/datahub-web-react/src/app/domain/DomainItemMenu.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { DeleteOutlined } from '@ant-design/icons'; -import { Dropdown, Menu, Modal, message } from 'antd'; -import React from 'react'; - -import { MenuIcon } from '@app/entity/shared/EntityDropdown/EntityDropdown'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { useDeleteDomainMutation } from '@graphql/domain.generated'; -import { EntityType } from '@types'; - -type Props = { - urn: string; - name: string; - onDelete?: () => void; -}; - -export default function DomainItemMenu({ name, urn, onDelete }: Props) { - const entityRegistry = useEntityRegistry(); - const [deleteDomainMutation] = useDeleteDomainMutation(); - - const deleteDomain = () => { - deleteDomainMutation({ - variables: { - urn, - }, - }) - .then(({ errors }) => { - if (!errors) { - message.success('Deleted Domain!'); - onDelete?.(); - } - }) - .catch(() => { - message.destroy(); - message.error({ content: `Failed to delete Domain!: An unknown error occurred.`, duration: 3 }); - }); - }; - - const onConfirmDelete = () => { - Modal.confirm({ - title: `Delete Domain '${name}'`, - content: `Are you sure you want to remove this ${entityRegistry.getEntityName(EntityType.Domain)}?`, - onOk() { - deleteDomain(); - }, - onCancel() {}, - okText: 'Yes', - maskClosable: true, - closable: true, - }); - }; - - const items = [ - { - key: 0, - label: ( - -  Delete - - ), - }, - ]; - - return ( - - - - ); -} diff --git a/datahub-web-react/src/app/domain/DomainListColumns.tsx b/datahub-web-react/src/app/domain/DomainListColumns.tsx deleted file mode 100644 index 10368d6b3cfaa7..00000000000000 --- a/datahub-web-react/src/app/domain/DomainListColumns.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { Tag, Tooltip, Typography } from 'antd'; -import React from 'react'; -import { Link } from 'react-router-dom'; -import styled from 'styled-components'; - -import DomainItemMenu from '@app/domain/DomainItemMenu'; -import AvatarsGroup from '@app/shared/avatar/AvatarsGroup'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { Maybe, Ownership } from '@types'; - -interface DomainEntry { - name: string; - entities: string; - urn: string; - ownership?: Maybe; - url: string; -} - -const AvatarGroupWrapper = styled.div` - margin-right: 10px; - display: inline-block; -`; - -const DomainNameContainer = styled.div` - margin-left: 16px; - margin-right: 16px; - display: inline; -`; - -export function DomainListMenuColumn(handleDelete: (urn: string) => void) { - return (record: DomainEntry) => ( - handleDelete(record.urn)} /> - ); -} - -export function DomainNameColumn(logoIcon: JSX.Element) { - return (record: DomainEntry) => ( - - - {logoIcon} - - {record.name} - - - {record.entities} entities - - - - ); -} - -export function DomainOwnersColumn(ownership: Maybe) { - const entityRegistry = useEntityRegistry(); - - if (!ownership) { - return null; - } - - const { owners } = ownership; - if (!owners || owners.length === 0) { - return null; - } - return ( - - - - ); -} diff --git a/datahub-web-react/src/app/domain/DomainRoutes.tsx b/datahub-web-react/src/app/domain/DomainRoutes.tsx deleted file mode 100644 index 46c90c30bc3140..00000000000000 --- a/datahub-web-react/src/app/domain/DomainRoutes.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React, { useState } from 'react'; -import { Route, Switch } from 'react-router-dom'; -import styled from 'styled-components/macro'; - -import { DomainsContext } from '@app/domain/DomainsContext'; -import ManageDomainsPageV2 from '@app/domain/nestedDomains/ManageDomainsPageV2'; -import ManageDomainsSidebar from '@app/domain/nestedDomains/ManageDomainsSidebar'; -import { EntityPage } from '@app/entity/EntityPage'; -import { GenericEntityProperties } from '@app/entity/shared/types'; -import { useEntityRegistry } from '@app/useEntityRegistry'; -import { PageRoutes } from '@conf/Global'; - -import { EntityType } from '@types'; - -const ContentWrapper = styled.div` - display: flex; - flex: 1; - overflow: hidden; -`; - -export default function DomainRoutes() { - const entityRegistry = useEntityRegistry(); - const [entityData, setEntityData] = useState(null); - const [parentDomainsToUpdate, setParentDomainsToUpdate] = useState([]); - - return ( - - - - - } - /> - } /> - - - - ); -} diff --git a/datahub-web-react/src/app/domain/DomainSearch.tsx b/datahub-web-react/src/app/domain/DomainSearch.tsx deleted file mode 100644 index 975e7d3aac7f9c..00000000000000 --- a/datahub-web-react/src/app/domain/DomainSearch.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import { LoadingOutlined } from '@ant-design/icons'; -import React, { useRef, useState } from 'react'; -import styled from 'styled-components/macro'; - -import DomainSearchResultItem from '@app/domain/DomainSearchResultItem'; -import { SearchBar } from '@app/search/SearchBar'; -import ClickOutside from '@app/shared/ClickOutside'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { useGetSearchResultsForMultipleQuery } from '@graphql/search.generated'; -import { EntityType } from '@types'; - -const DomainSearchWrapper = styled.div` - position: relative; -`; - -const ResultsWrapper = styled.div` - background-color: white; - border-radius: 5px; - box-shadow: - 0 3px 6px -4px rgb(0 0 0 / 12%), - 0 6px 16px 0 rgb(0 0 0 / 8%), - 0 9px 28px 8px rgb(0 0 0 / 5%); - max-height: 380px; - overflow: auto; - padding: 8px; - position: absolute; - max-height: 210px; - overflow: auto; - width: calc(100% - 24px); - left: 12px; - top: 45px; - z-index: 1; -`; - -const LoadingWrapper = styled.div` - display: flex; - align-items: center; - justify-content: center; - height: 350px; - font-size: 30px; -`; - -function DomainSearch() { - const [query, setQuery] = useState(''); - const [isSearchBarFocused, setIsSearchBarFocused] = useState(false); - const entityRegistry = useEntityRegistry(); - const { data, loading } = useGetSearchResultsForMultipleQuery({ - variables: { - input: { - types: [EntityType.Domain], - query, - start: 0, - count: 50, - }, - }, - }); - - const searchResults = data?.searchAcrossEntities?.searchResults; - const timerRef = useRef(-1); - - const handleQueryChange = (q: string) => { - window.clearTimeout(timerRef.current); - timerRef.current = window.setTimeout(() => { - setQuery(q); - }, 250); - }; - - const renderLoadingIndicator = () => ( - - - - ); - - const renderSearchResults = () => ( - - {searchResults?.map((result) => ( - setIsSearchBarFocused(false)} - /> - ))} - - ); - - return ( - - setIsSearchBarFocused(false)}> - null} - onQueryChange={(q) => handleQueryChange(q)} - entityRegistry={entityRegistry} - onFocus={() => setIsSearchBarFocused(true)} - /> - {loading && renderLoadingIndicator()} - {!loading && isSearchBarFocused && !!searchResults?.length && renderSearchResults()} - - - ); -} - -export default DomainSearch; diff --git a/datahub-web-react/src/app/domain/DomainSearchResultItem.tsx b/datahub-web-react/src/app/domain/DomainSearchResultItem.tsx deleted file mode 100644 index 96e92ce525d9d6..00000000000000 --- a/datahub-web-react/src/app/domain/DomainSearchResultItem.tsx +++ /dev/null @@ -1,70 +0,0 @@ -// Create a new component called SearchResultItem.js -import React from 'react'; -import Highlight from 'react-highlighter'; -import { Link } from 'react-router-dom'; -import styled from 'styled-components/macro'; - -import DomainIcon from '@app/domain/DomainIcon'; -import { getParentDomains } from '@app/domain/utils'; -import { IconStyleType } from '@app/entity/Entity'; -import EntityRegistry from '@app/entity/EntityRegistry'; -import { ANTD_GRAY } from '@app/entity/shared/constants'; -import ParentEntities from '@app/search/filters/ParentEntities'; - -import { Entity, EntityType } from '@types'; - -type Props = { - entity: Entity; - entityRegistry: EntityRegistry; - query: string; - onResultClick: () => void; -}; - -const SearchResult = styled(Link)` - color: #262626; - display: flex; - align-items: center; - gap: 8px; - height: 100%; - padding: 6px 8px; - width: 100%; - &:hover { - background-color: ${ANTD_GRAY[3]}; - color: #262626; - } -`; - -const IconWrapper = styled.span``; - -const highlightMatchStyle = { - fontWeight: 'bold', - background: 'none', - padding: 0, -}; - -function DomainSearchResultItem({ entity, entityRegistry, query, onResultClick }: Props) { - return ( - - - {entity.type === EntityType.Domain ? ( - - ) : ( - entityRegistry.getIcon(entity.type, 12, IconStyleType.ACCENT) - )} - -
- - - {entityRegistry.getDisplayName(entity.type, entity)} - -
-
- ); -} - -export default DomainSearchResultItem; diff --git a/datahub-web-react/src/app/domain/DomainsContext.tsx b/datahub-web-react/src/app/domain/DomainsContext.tsx index da88ed1f3d582f..3a93fed0dfc683 100644 --- a/datahub-web-react/src/app/domain/DomainsContext.tsx +++ b/datahub-web-react/src/app/domain/DomainsContext.tsx @@ -2,14 +2,14 @@ import React, { useContext } from 'react'; import { GenericEntityProperties } from '@app/entity/shared/types'; -export interface DomainsContextType { +interface DomainsContextType { entityData: GenericEntityProperties | null; setEntityData: (entityData: GenericEntityProperties | null) => void; parentDomainsToUpdate: string[]; setParentDomainsToUpdate: (values: string[]) => void; } -export const DomainsContext = React.createContext({ +const DomainsContext = React.createContext({ entityData: null, setEntityData: () => {}, parentDomainsToUpdate: [], // used to tell domains to refetch their children count after updates (create, move, delete) diff --git a/datahub-web-react/src/app/domain/DomainsList.tsx b/datahub-web-react/src/app/domain/DomainsList.tsx deleted file mode 100644 index 8787a67d68c322..00000000000000 --- a/datahub-web-react/src/app/domain/DomainsList.tsx +++ /dev/null @@ -1,213 +0,0 @@ -import { PlusOutlined } from '@ant-design/icons'; -import { Button, Empty, Pagination, Typography } from 'antd'; -import * as QueryString from 'query-string'; -import { AlignType } from 'rc-table/lib/interface'; -import React, { useEffect, useState } from 'react'; -import { useLocation } from 'react-router'; -import styled from 'styled-components'; - -import CreateDomainModal from '@app/domain/CreateDomainModal'; -import DomainIcon from '@app/domain/DomainIcon'; -import { DomainListMenuColumn, DomainNameColumn, DomainOwnersColumn } from '@app/domain/DomainListColumns'; -import { addToListDomainsCache, removeFromListDomainsCache } from '@app/domain/utils'; -import { StyledTable } from '@app/entity/shared/components/styled/StyledTable'; -import TabToolbar from '@app/entity/shared/components/styled/TabToolbar'; -import { getElasticCappedTotalValueText } from '@app/entity/shared/constants'; -import { OnboardingTour } from '@app/onboarding/OnboardingTour'; -import { DOMAINS_CREATE_DOMAIN_ID, DOMAINS_INTRO_ID } from '@app/onboarding/config/DomainsOnboardingConfig'; -import { SearchBar } from '@app/search/SearchBar'; -import { Message } from '@app/shared/Message'; -import { scrollToTop } from '@app/shared/searchUtils'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { useListDomainsQuery } from '@graphql/domain.generated'; -import { EntityType } from '@types'; - -const DomainsContainer = styled.div``; - -export const DomainsPaginationContainer = styled.div` - display: flex; - justify-content: center; - padding: 12px; - padding-left: 16px; - border-bottom: 1px solid; - border-color: ${(props) => props.theme.styles['border-color-base']}; - display: flex; - justify-content: space-between; - align-items: center; -`; - -const PaginationInfo = styled(Typography.Text)` - padding: 0px; -`; - -const DEFAULT_PAGE_SIZE = 25; - -export const DomainsList = () => { - const entityRegistry = useEntityRegistry(); - const location = useLocation(); - const params = QueryString.parse(location.search, { arrayFormat: 'comma' }); - const paramsQuery = (params?.query as string) || undefined; - const [query, setQuery] = useState(undefined); - useEffect(() => setQuery(paramsQuery), [paramsQuery]); - - const [page, setPage] = useState(1); - const [isCreatingDomain, setIsCreatingDomain] = useState(false); - - const pageSize = DEFAULT_PAGE_SIZE; - const start = (page - 1) * pageSize; - - const { loading, error, data, client, refetch } = useListDomainsQuery({ - variables: { - input: { - start, - count: pageSize, - query, - }, - }, - fetchPolicy: query && query.length > 0 ? 'no-cache' : 'cache-first', - }); - - const totalDomains = data?.listDomains?.total || 0; - const lastResultIndex = start + pageSize > totalDomains ? totalDomains : start + pageSize; - const domains = data?.listDomains?.domains || []; - - const onChangePage = (newPage: number) => { - scrollToTop(); - setPage(newPage); - }; - - const handleDelete = (urn: string) => { - removeFromListDomainsCache(client, urn, page, pageSize); - setTimeout(() => { - refetch?.(); - }, 2000); - }; - - const allColumns = [ - { - title: 'Name', - dataIndex: '', - key: 'name', - sorter: (sourceA, sourceB) => { - return sourceA.name.localeCompare(sourceB.name); - }, - render: DomainNameColumn( - , - ), - }, - { - title: 'Owners', - dataIndex: 'ownership', - width: '10%', - key: 'ownership', - render: DomainOwnersColumn, - }, - { - title: '', - dataIndex: '', - width: '5%', - align: 'right' as AlignType, - key: 'menu', - render: DomainListMenuColumn(handleDelete), - }, - ]; - - const tableData = domains.map((domain) => { - const displayName = entityRegistry.getDisplayName(EntityType.Domain, domain); - const totalEntitiesText = getElasticCappedTotalValueText(domain.entities?.total || 0); - const url = entityRegistry.getEntityUrl(EntityType.Domain, domain.urn); - - return { - urn: domain.urn, - name: displayName, - entities: totalEntitiesText, - ownership: domain.ownership, - url, - }; - }); - - return ( - <> - {!data && loading && } - {error && } - - - - - null} - onQueryChange={(q) => setQuery(q && q.length > 0 ? q : undefined)} - entityRegistry={entityRegistry} - hideRecommendations - /> - - }} - /> - - - - {lastResultIndex > 0 ? (page - 1) * pageSize + 1 : 0} - {lastResultIndex} - - of {totalDomains} - - - - - {isCreatingDomain && ( - setIsCreatingDomain(false)} - onCreate={(urn, _, name, description) => { - addToListDomainsCache( - client, - { - urn, - properties: { - name, - description: description || null, - }, - ownership: null, - entities: null, - displayProperties: null, - institutionalMemory: null, - }, - pageSize, - ); - setTimeout(() => refetch(), 2000); - }} - /> - )} - - - ); -}; diff --git a/datahub-web-react/src/app/domain/EmptyDomainDescription.tsx b/datahub-web-react/src/app/domain/EmptyDomainDescription.tsx deleted file mode 100644 index b232aa3780c7c5..00000000000000 --- a/datahub-web-react/src/app/domain/EmptyDomainDescription.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components/macro'; - -import { ANTD_GRAY } from '@app/entity/shared/constants'; - -const StyledParagraph = styled(Typography.Paragraph)` - text-align: justify; - text-justify: inter-word; - margin: 40px 0; - font-size: 15px; -`; - -function EmptyDomainDescription() { - return ( - <> - - Welcome to your Data Domains! It looks like this space - is ready to be transformed into a well-organized data universe. Start by creating your first domain - a - high-level category for your data assets. - - - Create Nested Domains: Want to dive deeper? You can - also create nested domains to add granularity and structure. Just like nesting Russian dolls, its all - about refining your organization. - - - Build Data Products: Once your domains are set, go a - step further! Organize your data assets into data products to realize a data mesh architecture. Data - products empower you to treat data as a product, making it more accessible and manageable. - - - Ready to embark on this data adventure? Click the Create Domain button to begin shaping your data - landscape! - - - ); -} - -export default EmptyDomainDescription; diff --git a/datahub-web-react/src/app/domain/EmptyDomainsSection.tsx b/datahub-web-react/src/app/domain/EmptyDomainsSection.tsx deleted file mode 100644 index cffde970b4749e..00000000000000 --- a/datahub-web-react/src/app/domain/EmptyDomainsSection.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { PlusOutlined } from '@ant-design/icons'; -import { Button, Empty, Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components/macro'; - -import { ANTD_GRAY } from '@app/entity/shared/constants'; - -const EmptyDomainContainer = styled.div` - display: flex; - justify-content: center; - align-items: center; -`; - -const StyledEmpty = styled(Empty)` - width: 35vw; - @media screen and (max-width: 1300px) { - width: 50vw; - } - @media screen and (max-width: 896px) { - overflow-y: auto; - max-height: 75vh; - &::-webkit-scrollbar { - width: 5px; - background: #d6d6d6; - } - } - padding: 60px 40px; - .ant-empty-image { - display: none; - } -`; - -const StyledButton = styled(Button)` - margin: 18px 8px 0 0; -`; - -const IconContainer = styled.span` - color: ${ANTD_GRAY[7]}; - font-size: 40px; -`; - -interface Props { - title?: string; - setIsCreatingDomain: React.Dispatch>; - description?: React.ReactNode; - icon?: React.ReactNode; -} - -function EmptyDomainsSection(props: Props) { - const { title, description, setIsCreatingDomain, icon } = props; - return ( - - - {icon} - {title} - {description} - - } - > - setIsCreatingDomain(true)}> - Create Domain - - - - ); -} - -export default EmptyDomainsSection; diff --git a/datahub-web-react/src/app/domain/ManageDomainsPage.tsx b/datahub-web-react/src/app/domain/ManageDomainsPage.tsx deleted file mode 100644 index d662377b5b51ba..00000000000000 --- a/datahub-web-react/src/app/domain/ManageDomainsPage.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { Typography } from 'antd'; -import React, { useState } from 'react'; -import styled from 'styled-components'; - -import { DomainsContext } from '@app/domain/DomainsContext'; -import { DomainsList } from '@app/domain/DomainsList'; -import { GenericEntityProperties } from '@app/entity/shared/types'; - -const PageContainer = styled.div` - padding-top: 20px; -`; - -const PageHeaderContainer = styled.div` - && { - padding-left: 24px; - } -`; - -const PageTitle = styled(Typography.Title)` - && { - margin-bottom: 12px; - } -`; - -const ListContainer = styled.div``; - -export const ManageDomainsPage = () => { - const [entityData, setEntityData] = useState(null); - const [parentDomainsToUpdate, setParentDomainsToUpdate] = useState([]); - - return ( - - - - Domains - - View your DataHub Domains. Take administrative actions. - - - - - - - - ); -}; diff --git a/datahub-web-react/src/app/domain/nestedDomains/DomainsSidebarHeader.tsx b/datahub-web-react/src/app/domain/nestedDomains/DomainsSidebarHeader.tsx deleted file mode 100644 index 7e723e31837d1d..00000000000000 --- a/datahub-web-react/src/app/domain/nestedDomains/DomainsSidebarHeader.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { PlusOutlined } from '@ant-design/icons'; -import { useApolloClient } from '@apollo/client'; -import { Button } from 'antd'; -import React, { useState } from 'react'; -import { Link } from 'react-router-dom'; -import styled from 'styled-components'; - -import CreateDomainModal from '@app/domain/CreateDomainModal'; -import { useDomainsContext } from '@app/domain/DomainsContext'; -import DomainsTitle from '@app/domain/nestedDomains/DomainsTitle'; -import { updateListDomainsCache } from '@app/domain/utils'; -import { ANTD_GRAY, ANTD_GRAY_V2 } from '@app/entity/shared/constants'; -import { PageRoutes } from '@conf/Global'; - -const HeaderWrapper = styled.div` - border-bottom: 1px solid ${ANTD_GRAY[4]}; - padding: 16px; - font-size: 20px; - display: flex; - align-items: center; - justify-content: space-between; -`; - -const StyledButton = styled(Button)` - box-shadow: none; - border-color: ${ANTD_GRAY_V2[6]}; -`; - -const StyledLink = styled(Link)` - color: inherit; - - &:hover { - color: inherit; - } -`; - -export default function DomainsSidebarHeader() { - const { setParentDomainsToUpdate } = useDomainsContext(); - const [isCreatingDomain, setIsCreatingDomain] = useState(false); - const client = useApolloClient(); - - return ( - - - - - } onClick={() => setIsCreatingDomain(true)} /> - {isCreatingDomain && ( - setIsCreatingDomain(false)} - onCreate={(urn, id, name, description, parentDomain) => { - updateListDomainsCache(client, urn, id, name, description, parentDomain); - if (parentDomain) setParentDomainsToUpdate([parentDomain]); - }} - /> - )} - - ); -} diff --git a/datahub-web-react/src/app/domain/nestedDomains/DomainsTitle.tsx b/datahub-web-react/src/app/domain/nestedDomains/DomainsTitle.tsx deleted file mode 100644 index ea6a93a445ce7e..00000000000000 --- a/datahub-web-react/src/app/domain/nestedDomains/DomainsTitle.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import styled from 'styled-components'; - -import DomainIcon from '@app/domain/DomainIcon'; - -const IconWrapper = styled.span` - margin-right: 10px; -`; - -export default function DomainsTitle() { - return ( - - - - - Domains - - ); -} diff --git a/datahub-web-react/src/app/domain/nestedDomains/ManageDomainsPageV2.tsx b/datahub-web-react/src/app/domain/nestedDomains/ManageDomainsPageV2.tsx deleted file mode 100644 index 3d7d3187f515c3..00000000000000 --- a/datahub-web-react/src/app/domain/nestedDomains/ManageDomainsPageV2.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { PlusOutlined } from '@ant-design/icons'; -import { useApolloClient } from '@apollo/client'; -import { Button } from 'antd'; -import React, { useEffect, useState } from 'react'; -import styled from 'styled-components/macro'; - -import CreateDomainModal from '@app/domain/CreateDomainModal'; -import { useDomainsContext } from '@app/domain/DomainsContext'; -import DomainsTitle from '@app/domain/nestedDomains/DomainsTitle'; -import RootDomains from '@app/domain/nestedDomains/RootDomains'; -import { updateListDomainsCache } from '@app/domain/utils'; -import { ANTD_GRAY_V2 } from '@app/entity/shared/constants'; -import { OnboardingTour } from '@app/onboarding/OnboardingTour'; -import { DOMAINS_CREATE_DOMAIN_ID, DOMAINS_INTRO_ID } from '@app/onboarding/config/DomainsOnboardingConfig'; - -const PageWrapper = styled.div` - background-color: ${ANTD_GRAY_V2[1]}; - flex: 1; - display: flex; - flex-direction: column; - overflow: hidden; -`; - -const Header = styled.div` - display: flex; - justify-content: space-between; - padding: 32px 24px; - font-size: 30px; - align-items: center; -`; - -export default function ManageDomainsPageV2() { - const { setEntityData, setParentDomainsToUpdate } = useDomainsContext(); - const [isCreatingDomain, setIsCreatingDomain] = useState(false); - const client = useApolloClient(); - - useEffect(() => { - setEntityData(null); - }, [setEntityData]); - - return ( - - -
- - -
- - {isCreatingDomain && ( - setIsCreatingDomain(false)} - onCreate={(urn, id, name, description, parentDomain) => { - updateListDomainsCache(client, urn, id, name, description, parentDomain); - if (parentDomain) setParentDomainsToUpdate([parentDomain]); - }} - /> - )} -
- ); -} diff --git a/datahub-web-react/src/app/domain/nestedDomains/ManageDomainsSidebar.tsx b/datahub-web-react/src/app/domain/nestedDomains/ManageDomainsSidebar.tsx deleted file mode 100644 index a9f2a55941657a..00000000000000 --- a/datahub-web-react/src/app/domain/nestedDomains/ManageDomainsSidebar.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React, { useState } from 'react'; - -import DomainSearch from '@app/domain/DomainSearch'; -import DomainsSidebarHeader from '@app/domain/nestedDomains/DomainsSidebarHeader'; -import DomainNavigator from '@app/domain/nestedDomains/domainNavigator/DomainNavigator'; -import { ProfileSidebarResizer } from '@app/entity/shared/containers/profile/sidebar/ProfileSidebarResizer'; -import { MAX_BROWSER_WIDTH, MIN_BROWSWER_WIDTH } from '@app/glossary/BusinessGlossaryPage'; -import { SidebarWrapper } from '@app/shared/sidebar/components'; - -export default function ManageDomainsSidebar() { - const [browserWidth, setBrowserWith] = useState(window.innerWidth * 0.2); - - return ( - <> - - - - - - - setBrowserWith(Math.min(Math.max(width, MIN_BROWSWER_WIDTH), MAX_BROWSER_WIDTH)) - } - initialSize={browserWidth} - isSidebarOnLeft - /> - - ); -} diff --git a/datahub-web-react/src/app/domain/nestedDomains/RootDomains.tsx b/datahub-web-react/src/app/domain/nestedDomains/RootDomains.tsx deleted file mode 100644 index fda3912e70ce96..00000000000000 --- a/datahub-web-react/src/app/domain/nestedDomains/RootDomains.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { ReadOutlined } from '@ant-design/icons'; -import React from 'react'; -import styled from 'styled-components'; - -import EmptyDomainDescription from '@app/domain/EmptyDomainDescription'; -import EmptyDomainsSection from '@app/domain/EmptyDomainsSection'; -import useListDomains from '@app/domain/useListDomains'; -import { ResultWrapper } from '@app/search/SearchResultList'; -import { Message } from '@app/shared/Message'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { EntityType } from '@types'; - -const DomainsWrapper = styled.div` - overflow: auto; - padding: 0 28px 16px 28px; -`; - -interface Props { - setIsCreatingDomain: React.Dispatch>; -} -export default function RootDomains({ setIsCreatingDomain }: Props) { - const entityRegistry = useEntityRegistry(); - const { loading, error, data, sortedDomains } = useListDomains({}); - - return ( - <> - {!data && loading && } - {error && } - {!loading && (!data || !data?.listDomains?.domains?.length) && ( - } - title="Organize your data" - description={} - setIsCreatingDomain={setIsCreatingDomain} - /> - )} - - {sortedDomains?.map((domain) => ( - - {entityRegistry.renderSearchResult(EntityType.Domain, { entity: domain, matchedFields: [] })} - - ))} - - - ); -} diff --git a/datahub-web-react/src/app/domain/utils.ts b/datahub-web-react/src/app/domain/utils.ts index 9bb6ad994e6e3e..87a129634ccc91 100644 --- a/datahub-web-react/src/app/domain/utils.ts +++ b/datahub-web-react/src/app/domain/utils.ts @@ -1,11 +1,5 @@ import { ApolloClient } from '@apollo/client'; -import { isEqual } from 'lodash'; -import { useEffect } from 'react'; - -import { useDomainsContext } from '@app/domain/DomainsContext'; import EntityRegistry from '@app/entity/EntityRegistry'; -import { GenericEntityProperties } from '@app/entity/shared/types'; -import usePrevious from '@app/shared/usePrevious'; import { useEntityRegistry } from '@app/useEntityRegistry'; import { ListDomainsDocument, ListDomainsQuery } from '@graphql/domain.generated'; @@ -14,7 +8,7 @@ import { Entity, EntityType } from '@types'; /** * Add an entry to the list domains cache. */ -export const addToListDomainsCache = (client, newDomain, pageSize, parentDomain?: string) => { +const addToListDomainsCache = (client, newDomain, pageSize, parentDomain?: string) => { // Read the data from our cache for this query. const currData: ListDomainsQuery | null = client.readQuery({ query: ListDomainsDocument, @@ -124,17 +118,6 @@ export const removeFromListDomainsCache = (client, urn, page, pageSize, parentDo }); }; -export function useUpdateDomainEntityDataOnChange(entityData: GenericEntityProperties | null, entityType: EntityType) { - const { setEntityData } = useDomainsContext(); - const previousEntityData = usePrevious(entityData); - - useEffect(() => { - if (EntityType.Domain === entityType && !isEqual(entityData, previousEntityData)) { - setEntityData(entityData); - } - }); -} - export function useSortedDomains(domains?: Array, sortBy?: 'displayName') { const entityRegistry = useEntityRegistry(); if (!domains || !sortBy) return domains; diff --git a/datahub-web-react/src/app/domainV2/DomainsList.tsx b/datahub-web-react/src/app/domainV2/DomainsList.tsx index 38835f2c83c006..4c5e13041bb99e 100644 --- a/datahub-web-react/src/app/domainV2/DomainsList.tsx +++ b/datahub-web-react/src/app/domainV2/DomainsList.tsx @@ -31,6 +31,7 @@ export const DomainsPaginationContainer = styled.div` padding: 12px; padding-left: 16px; border-bottom: 1px solid; + border-color: ${(props) => props.theme.styles['border-color-base']}; display: flex; justify-content: space-between; align-items: center; diff --git a/datahub-web-react/src/app/entity/Access/RoleEntity.tsx b/datahub-web-react/src/app/entity/Access/RoleEntity.tsx deleted file mode 100644 index 5f9c7d8f51a3f1..00000000000000 --- a/datahub-web-react/src/app/entity/Access/RoleEntity.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { TagFilled, TagOutlined } from '@ant-design/icons'; -import * as React from 'react'; -import styled from 'styled-components'; - -import RoleEntityProfile from '@app/entity/Access/RoleEntityProfile'; -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { urlEncodeUrn } from '@app/entity/shared/utils'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; - -import { useGetExternalRoleQuery } from '@graphql/accessrole.generated'; -import { EntityType, Role, SearchResult } from '@types'; - -const PreviewTagIcon = styled(TagOutlined)` - font-size: 20px; -`; -// /** -// * Definition of the DataHub Access Role entity. -// */ -export class RoleEntity implements Entity { - type: EntityType = EntityType.Role; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => false; - - isLineageEnabled = () => false; - - getAutoCompleteFieldName = () => 'name'; - - getPathName: () => string = () => 'role'; - - getCollectionName: () => string = () => 'Roles'; - - getEntityName: () => string = () => 'Role'; - - useEntityQuery = useGetExternalRoleQuery; - - renderProfile: (urn: string) => JSX.Element = (_) => ; - - renderPreview = (_: PreviewType, data: Role) => ( - } - type="Role" - typeIcon={this.icon(14, IconStyleType.ACCENT)} - /> - ); - - renderSearch = (result: SearchResult) => { - return this.renderPreview(PreviewType.SEARCH, result.entity as Role); - }; - - displayName = (data: Role) => { - return data.properties?.name || data.urn; - }; - - getOverridePropertiesFromEntity = (data: Role) => { - return { - name: data.properties?.name, - }; - }; - - getGenericEntityProperties = (role: Role) => { - return getDataForEntityType({ data: role, entityType: this.type, getOverrideProperties: (data) => data }); - }; - - supportedCapabilities = () => { - return new Set([EntityCapabilityType.OWNERS]); - }; - - getGraphName = () => { - return 'roleEntity'; - }; -} diff --git a/datahub-web-react/src/app/entity/Access/RoleEntityProfile.tsx b/datahub-web-react/src/app/entity/Access/RoleEntityProfile.tsx deleted file mode 100644 index 0d75fcc833da4e..00000000000000 --- a/datahub-web-react/src/app/entity/Access/RoleEntityProfile.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { grey } from '@ant-design/colors'; -import { Divider, Typography } from 'antd'; -import React from 'react'; -import { useParams } from 'react-router'; -import styled from 'styled-components'; - -import { decodeUrn } from '@app/entity/shared/utils'; -import { Message } from '@app/shared/Message'; - -import { useGetExternalRoleQuery } from '@graphql/accessrole.generated'; - -const PageContainer = styled.div` - padding: 32px 100px; -`; - -const LoadingMessage = styled(Message)` - margin-top: 10%; -`; - -type RolePageParams = { - urn: string; -}; - -const TitleLabel = styled(Typography.Text)` - &&& { - color: ${grey[2]}; - font-size: 12px; - display: block; - line-height: 20px; - font-weight: 700; - } -`; - -const DescriptionLabel = styled(Typography.Text)` - &&& { - text-align: left; - font-weight: bold; - font-size: 14px; - line-height: 28px; - color: rgb(38, 38, 38); - } -`; - -const TitleText = styled(Typography.Text)` - &&& { - color: ${grey[10]}; - font-weight: 700; - font-size: 20px; - line-height: 28px; - display: inline-block; - margin: 0px 7px; - } -`; - -const { Paragraph } = Typography; - -export default function RoleEntityProfile() { - const { urn: encodedUrn } = useParams(); - const urn = decodeUrn(encodedUrn); - const { data, loading } = useGetExternalRoleQuery({ variables: { urn } }); - - return ( - - {loading && } - Role - {data?.role?.properties?.name} - - {/* Role Description */} - About - - {data?.role?.properties?.description} - - - ); -} diff --git a/datahub-web-react/src/app/entity/application/ApplicationEntitiesTab.tsx b/datahub-web-react/src/app/entity/application/ApplicationEntitiesTab.tsx deleted file mode 100644 index 38944d16e3c9ac..00000000000000 --- a/datahub-web-react/src/app/entity/application/ApplicationEntitiesTab.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; - -import { useEntityData } from '@app/entity/shared/EntityContext'; -import { SearchCardContext } from '@app/entityV2/shared/SearchCardContext'; -import { EmbeddedListSearchSection } from '@app/entityV2/shared/components/styled/search/EmbeddedListSearchSection'; -import { UnionType } from '@app/searchV2/utils/constants'; - -export function ApplicationEntitiesTab() { - const { urn } = useEntityData(); - - const fixedOrFilters = [ - { - field: 'applications', - values: [urn], - }, - ]; - - return ( - - - - ); -} diff --git a/datahub-web-react/src/app/entity/application/ApplicationEntity.tsx b/datahub-web-react/src/app/entity/application/ApplicationEntity.tsx deleted file mode 100644 index b7144b0c399b7e..00000000000000 --- a/datahub-web-react/src/app/entity/application/ApplicationEntity.tsx +++ /dev/null @@ -1,259 +0,0 @@ -import { - AppstoreOutlined, - FileDoneOutlined, - FileOutlined, - ReadOutlined, - UnorderedListOutlined, -} from '@ant-design/icons'; -import { ListBullets } from '@phosphor-icons/react'; -import * as React from 'react'; - -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entityV2/Entity'; -import { ApplicationEntitiesTab } from '@app/entityV2/application/ApplicationEntitiesTab'; -import { ApplicationSummaryTab } from '@app/entityV2/application/ApplicationSummaryTab'; -import { Preview } from '@app/entityV2/application/preview/Preview'; -import { EntityMenuItems } from '@app/entityV2/shared/EntityDropdown/EntityMenuActions'; -import { TYPE_ICON_CLASS_NAME } from '@app/entityV2/shared/components/subtypes'; -import { EntityProfileTab } from '@app/entityV2/shared/constants'; -import { EntityProfile } from '@app/entityV2/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entityV2/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import { SidebarDomainSection } from '@app/entityV2/shared/containers/profile/sidebar/Domain/SidebarDomainSection'; -import { SidebarOwnerSection } from '@app/entityV2/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import SidebarEntityHeader from '@app/entityV2/shared/containers/profile/sidebar/SidebarEntityHeader'; -import { SidebarGlossaryTermsSection } from '@app/entityV2/shared/containers/profile/sidebar/SidebarGlossaryTermsSection'; -import { SidebarTagsSection } from '@app/entityV2/shared/containers/profile/sidebar/SidebarTagsSection'; -import StatusSection from '@app/entityV2/shared/containers/profile/sidebar/shared/StatusSection'; -import { getDataForEntityType } from '@app/entityV2/shared/containers/profile/utils'; -import SidebarNotesSection from '@app/entityV2/shared/sidebarSection/SidebarNotesSection'; -import SidebarStructuredProperties from '@app/entityV2/shared/sidebarSection/SidebarStructuredProperties'; -import { DocumentationTab } from '@app/entityV2/shared/tabs/Documentation/DocumentationTab'; -import { PropertiesTab } from '@app/entityV2/shared/tabs/Properties/PropertiesTab'; - -import { useGetApplicationQuery } from '@graphql/application.generated'; -import { Application, EntityType, SearchResult } from '@types'; - -const headerDropdownItems = new Set([EntityMenuItems.SHARE, EntityMenuItems.DELETE, EntityMenuItems.EDIT]); - -type ApplicationWithChildren = Application & { - children?: { - total: number; - } | null; -}; - -/** - * Definition of the DataHub Application entity. - */ -export class ApplicationEntity implements Entity { - type: EntityType = EntityType.Application; - - icon = (fontSize?: number, styleType?: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ( - - ); - } - - if (styleType === IconStyleType.SVG) { - return ( - - ); - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => true; - - isLineageEnabled = () => false; - - getAutoCompleteFieldName = () => 'name'; - - getPathName = () => 'application'; - - getEntityName = () => 'Application'; - - getCollectionName = () => 'Applications'; - - useEntityQuery = useGetApplicationQuery; - - renderProfile = (urn: string) => ( - { - return !loading ? entityData?.children?.total : undefined; - }, - component: ApplicationEntitiesTab, - icon: AppstoreOutlined, - }, - { - name: 'Properties', - component: PropertiesTab, - icon: UnorderedListOutlined, - }, - ]} - sidebarSections={this.getSidebarSections()} - sidebarTabs={this.getSidebarTabs()} - /> - ); - - getSidebarSections = () => [ - { - component: SidebarEntityHeader, - }, - { - component: SidebarAboutSection, - }, - { - component: SidebarNotesSection, - }, - { - component: SidebarOwnerSection, - }, - { - component: SidebarDomainSection, - properties: { - updateOnly: true, - }, - }, - { - component: SidebarTagsSection, - }, - { - component: SidebarGlossaryTermsSection, - }, - { - component: SidebarStructuredProperties, - }, - { - component: StatusSection, - }, - ]; - - getSidebarTabs = () => [ - { - name: 'Properties', - component: PropertiesTab, - description: 'View additional properties about this asset', - icon: ListBullets, - }, - ]; - - renderPreview = (previewType: PreviewType, data: any) => { - const genericProperties = this.getGenericEntityProperties(data); - return ( - - ); - }; - - renderSearch = (result: SearchResult) => { - const data = result.entity as Application; - const genericProperties = this.getGenericEntityProperties(data); - return ( - - ); - }; - - displayName = (data: Application) => { - return data?.properties?.name || data.urn; - }; - - getOverridePropertiesFromEntity = (data: Application) => { - const name = data?.properties?.name; - const externalUrl = data?.properties?.externalUrl; - const entityCount = (data as ApplicationWithChildren)?.children?.total || undefined; - const parentDomains = { - domains: (data?.domain && [data?.domain?.domain]) || [], - count: (data?.domain && 1) || 0, - }; - return { - name, - externalUrl, - entityCount, - parentDomains, - }; - }; - - getGenericEntityProperties = (data: Application) => { - return getDataForEntityType({ - data, - entityType: this.type, - getOverrideProperties: this.getOverridePropertiesFromEntity, - }); - }; - - supportedCapabilities = () => { - return new Set([ - EntityCapabilityType.OWNERS, - EntityCapabilityType.GLOSSARY_TERMS, - EntityCapabilityType.TAGS, - EntityCapabilityType.DOMAINS, - ]); - }; - - getGraphName = () => { - return 'application'; - }; -} diff --git a/datahub-web-react/src/app/entity/application/ApplicationSummaryTab.tsx b/datahub-web-react/src/app/entity/application/ApplicationSummaryTab.tsx deleted file mode 100644 index 06b3d927794563..00000000000000 --- a/datahub-web-react/src/app/entity/application/ApplicationSummaryTab.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; - -import { AssetsSection } from '@app/entityV2/application/AssetsSections'; -import { SummaryTabWrapper } from '@app/entityV2/shared/summary/HeaderComponents'; -import SummaryAboutSection from '@app/entityV2/shared/summary/SummaryAboutSection'; - -export const ApplicationSummaryTab = () => { - return ( - - - - - ); -}; diff --git a/datahub-web-react/src/app/entity/application/AssetsSections.tsx b/datahub-web-react/src/app/entity/application/AssetsSections.tsx deleted file mode 100644 index 2ad0f4dd056e2f..00000000000000 --- a/datahub-web-react/src/app/entity/application/AssetsSections.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { AppstoreOutlined } from '@ant-design/icons'; -import { Button } from 'antd'; -import React from 'react'; -import { useHistory } from 'react-router'; -import styled from 'styled-components'; - -import { useEntityData } from '@app/entity/shared/EntityContext'; -import ContentSectionLoading from '@app/entityV2/domain/summary/ContentSectionLoading'; -import { - getContentsSummary, - getDomainEntitiesFilterUrl, - navigateToDomainEntities, -} from '@app/entityV2/shared/containers/profile/sidebar/Domain/utils'; -import { SummaryTabHeaderTitle, SummaryTabHeaderWrapper } from '@app/entityV2/shared/summary/HeaderComponents'; -import { getContentTypeIcon } from '@app/entityV2/shared/summary/IconComponents'; -import { HorizontalList } from '@app/entityV2/shared/summary/ListComponents'; -import { pluralize } from '@app/shared/textUtil'; -import { EntityCountCard } from '@app/sharedV2/cards/EntityCountCard'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { useGetSearchResultsForMultipleQuery } from '@graphql/search.generated'; - -const AssetsSectionWrapper = styled.div` - flex: 1; - min-width: 100px; -`; - -export const StyledHeaderWrapper = styled(SummaryTabHeaderWrapper)` - margin-bottom: 8px; -`; - -export const AssetsSection = () => { - const history = useHistory(); - const entityRegistry = useEntityRegistry(); - const { urn, entityType } = useEntityData(); - - const { data, loading } = useGetSearchResultsForMultipleQuery({ - skip: !urn, - variables: { - input: { - types: [], - query: '', - orFilters: [{ and: [{ field: 'applications', values: [urn] }] }], - count: 1000, - }, - }, - fetchPolicy: 'cache-first', - }); - - const contentsSummary = data?.searchAcrossEntities && getContentsSummary(data.searchAcrossEntities); - const contentsCount = contentsSummary?.total || 0; - const hasContents = contentsCount > 0; - - if (!hasContents) { - return null; - } - - return ( - - - } title={`Assets (${contentsCount})`} /> - - - {loading && } - - - {!loading && - contentsSummary?.types?.map((summary) => { - const { type, count, entityType: summaryEntityType } = summary; - const typeName = ( - type || - entityRegistry.getEntityName(summaryEntityType) || - summaryEntityType - ).toLocaleLowerCase(); - const link = getDomainEntitiesFilterUrl( - urn, - entityType, - entityRegistry, - [summary.entityType], - summary.type ? [summary.type] : undefined, - ); - return ( - - ); - })} - - - ); -}; diff --git a/datahub-web-react/src/app/entity/application/preview/Preview.tsx b/datahub-web-react/src/app/entity/application/preview/Preview.tsx deleted file mode 100644 index 3d0d594988b110..00000000000000 --- a/datahub-web-react/src/app/entity/application/preview/Preview.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import React from 'react'; - -import { GenericEntityProperties } from '@app/entity/shared/types'; -import { EntityMenuActions, IconStyleType, PreviewType } from '@app/entityV2/Entity'; -import { EntityMenuItems } from '@app/entityV2/shared/EntityDropdown/EntityMenuActions'; -import DefaultPreviewCard from '@app/previewV2/DefaultPreviewCard'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { Domain, EntityPath, EntityType, GlobalTags, GlossaryTerms, Owner } from '@types'; - -interface Props { - urn: string; - data: GenericEntityProperties | null; - name: string; - description?: string | null; - owners?: Array | null; - domain?: Domain | null; - globalTags?: GlobalTags | null; - glossaryTerms?: GlossaryTerms | null; - entityCount?: number; - externalUrl?: string | null; - degree?: number; - paths?: EntityPath[]; - headerDropdownItems?: Set; - previewType: PreviewType; - actions?: EntityMenuActions; -} - -export const Preview = ({ - urn, - data, - name, - description, - owners, - globalTags, - domain, - glossaryTerms, - entityCount, - externalUrl, - degree, - paths, - headerDropdownItems, - previewType, - actions, -}: Props): JSX.Element => { - const entityRegistry = useEntityRegistry(); - - return ( - - ); -}; diff --git a/datahub-web-react/src/app/entity/businessAttribute/BusinessAttributeEntity.tsx b/datahub-web-react/src/app/entity/businessAttribute/BusinessAttributeEntity.tsx deleted file mode 100644 index d2cad8f094c425..00000000000000 --- a/datahub-web-react/src/app/entity/businessAttribute/BusinessAttributeEntity.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import { GlobalOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { Preview } from '@app/entity/businessAttribute/preview/Preview'; -import { BusinessAttributeDataTypeSection } from '@app/entity/businessAttribute/profile/BusinessAttributeDataTypeSection'; -import BusinessAttributeRelatedEntity from '@app/entity/businessAttribute/profile/BusinessAttributeRelatedEntity'; -import { EntityMenuItems } from '@app/entity/shared/EntityDropdown/EntityDropdown'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import { SidebarTagsSection } from '@app/entity/shared/containers/profile/sidebar/SidebarTagsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { DocumentationTab } from '@app/entity/shared/tabs/Documentation/DocumentationTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; -import { PageRoutes } from '@conf/Global'; - -import { useGetBusinessAttributeQuery } from '@graphql/businessAttribute.generated'; -import { BusinessAttribute, EntityType, SearchResult } from '@types'; - -/** - * Definition of datahub Business Attribute Entity - */ -/* eslint-disable @typescript-eslint/no-unused-vars */ -export class BusinessAttributeEntity implements Entity { - type: EntityType = EntityType.BusinessAttribute; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - if (styleType === IconStyleType.SVG) { - // TODO: Update the returned path value to the correct svg icon path - return ( - - ); - } - - return ( - - ); - }; - - displayName = (data: BusinessAttribute) => { - return data?.properties?.name || data?.urn; - }; - - getPathName = () => 'business-attribute'; - - getEntityName = () => 'Business Attribute'; - - getCollectionName = () => 'Business Attributes'; - - getGraphName = () => 'businessAttribute'; - - getCustomCardUrlPath = () => PageRoutes.BUSINESS_ATTRIBUTE; - - isBrowseEnabled = () => false; - - isLineageEnabled = () => false; - - isSearchEnabled = () => true; - - getOverridePropertiesFromEntity = (data: BusinessAttribute) => { - return { - name: data.properties?.name, - }; - }; - - getGenericEntityProperties = (data: BusinessAttribute) => { - return getDataForEntityType({ - data, - entityType: this.type, - getOverrideProperties: this.getOverridePropertiesFromEntity, - }); - }; - - renderPreview = (previewType: PreviewType, data: BusinessAttribute) => { - return ( - - ); - }; - - renderProfile = (urn: string) => { - return ( - - ); - }; - - renderSearch = (result: SearchResult) => { - return this.renderPreview(PreviewType.SEARCH, result.entity as BusinessAttribute); - }; - - supportedCapabilities = () => { - return new Set([ - EntityCapabilityType.OWNERS, - EntityCapabilityType.TAGS, - EntityCapabilityType.GLOSSARY_TERMS, - EntityCapabilityType.BUSINESS_ATTRIBUTES, - ]); - }; -} diff --git a/datahub-web-react/src/app/entity/businessAttribute/preview/Preview.tsx b/datahub-web-react/src/app/entity/businessAttribute/preview/Preview.tsx deleted file mode 100644 index 5fad4ea1bf4cf5..00000000000000 --- a/datahub-web-react/src/app/entity/businessAttribute/preview/Preview.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { GlobalOutlined } from '@ant-design/icons'; -import React from 'react'; - -import { getRelatedEntitiesUrl } from '@app/businessAttribute/businessAttributeUtils'; -import { IconStyleType, PreviewType } from '@app/entity/Entity'; -import UrlButton from '@app/entity/shared/UrlButton'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { EntityType, Owner } from '@types'; - -export const Preview = ({ - urn, - name, - description, - owners, - previewType, -}: { - urn: string; - name: string; - description?: string | null; - owners?: Array | null; - previewType: PreviewType; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - return ( - } - type="Business Attribute" - typeIcon={entityRegistry.getIcon(EntityType.BusinessAttribute, 14, IconStyleType.ACCENT)} - entityTitleSuffix={ - View Related Entities - } - /> - ); -}; diff --git a/datahub-web-react/src/app/entity/businessAttribute/preview/_tests_/Preview.test.tsx b/datahub-web-react/src/app/entity/businessAttribute/preview/_tests_/Preview.test.tsx deleted file mode 100644 index a9875df3255b58..00000000000000 --- a/datahub-web-react/src/app/entity/businessAttribute/preview/_tests_/Preview.test.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { MockedProvider } from '@apollo/client/testing'; -import { render } from '@testing-library/react'; -import React from 'react'; - -import { PreviewType } from '@app/entity/Entity'; -import { Preview } from '@app/entity/businessAttribute/preview/Preview'; -import { mocks } from '@src/Mocks'; -import TestPageContainer from '@utils/test-utils/TestPageContainer'; - -describe('Preview', () => { - it('renders', () => { - const { getByText } = render( - - - - - , - ); - expect(getByText('definition')).toBeInTheDocument(); - }); -}); diff --git a/datahub-web-react/src/app/entity/businessAttribute/profile/BusinessAttributeDataTypeSection.tsx b/datahub-web-react/src/app/entity/businessAttribute/profile/BusinessAttributeDataTypeSection.tsx deleted file mode 100644 index badfb718833b63..00000000000000 --- a/datahub-web-react/src/app/entity/businessAttribute/profile/BusinessAttributeDataTypeSection.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { EditOutlined } from '@ant-design/icons'; -import { Button, Select, message } from 'antd'; -import React, { useEffect, useState } from 'react'; -import styled from 'styled-components'; - -import { SchemaFieldDataType } from '@app/businessAttribute/businessAttributeUtils'; -import { useEntityData, useRefetch } from '@app/entity/shared/EntityContext'; -import { SidebarHeader } from '@app/entity/shared/containers/profile/sidebar/SidebarHeader'; - -import { useUpdateBusinessAttributeMutation } from '@graphql/businessAttribute.generated'; - -interface Props { - readOnly?: boolean; -} - -const DataTypeSelect = styled(Select)` - && { - width: 100%; - margin-top: 1em; - margin-bottom: 1em; - } -`; -// Ensures that any newly added datatype is automatically included in the user dropdown. -const DATA_TYPES = Object.values(SchemaFieldDataType); -export const BusinessAttributeDataTypeSection = ({ readOnly }: Props) => { - const { urn, entityData } = useEntityData(); - const [originalDescription, setOriginalDescription] = useState(null); - const [isEditing, setEditing] = useState(false); - const refetch = useRefetch(); - - useEffect(() => { - if (entityData?.properties?.businessAttributeDataType) { - setOriginalDescription(entityData?.properties?.businessAttributeDataType); - } - }, [entityData]); - - const [updateBusinessAttribute] = useUpdateBusinessAttributeMutation(); - - const handleChange = (value) => { - if (value === originalDescription) { - setEditing(false); - return; - } - - updateBusinessAttribute({ variables: { urn, input: { type: value } } }) - .then(() => { - setEditing(false); - setOriginalDescription(value); - message.success({ content: 'Data Type Updated', duration: 2 }); - refetch(); - }) - .catch((e: unknown) => { - message.destroy(); - if (e instanceof Error) { - message.error({ content: `Failed to update Data Type: \n ${e.message || ''}`, duration: 3 }); - } - }); - }; - - // Toggle editing mode - const handleEditClick = () => { - setEditing(!isEditing); - }; - - return ( -
- - - - ) - } - /> - {originalDescription} - {isEditing && ( - - {DATA_TYPES.map((dataType: SchemaFieldDataType) => ( - - {dataType} - - ))} - - )} -
- ); -}; - -export default BusinessAttributeDataTypeSection; diff --git a/datahub-web-react/src/app/entity/businessAttribute/profile/BusinessAttributeRelatedEntity.tsx b/datahub-web-react/src/app/entity/businessAttribute/profile/BusinessAttributeRelatedEntity.tsx deleted file mode 100644 index 7cce43befb9c8e..00000000000000 --- a/datahub-web-react/src/app/entity/businessAttribute/profile/BusinessAttributeRelatedEntity.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import * as React from 'react'; - -import { useEntityData } from '@app/entity/shared/EntityContext'; -import { EmbeddedListSearchSection } from '@app/entity/shared/components/styled/search/EmbeddedListSearchSection'; -import { UnionType } from '@app/search/utils/constants'; - -export default function BusinessAttributeRelatedEntity() { - const { entityData } = useEntityData(); - - const entityUrn = entityData?.urn; - - const fixedOrFilters = - (entityUrn && [ - { - field: 'businessAttribute', - values: [entityUrn], - }, - ]) || - []; - - entityData?.isAChildren?.relationships?.forEach((businessAttribute) => { - const childUrn = businessAttribute.entity?.urn; - - if (childUrn) { - fixedOrFilters.push({ - field: 'businessAttributes', - values: [childUrn], - }); - } - }); - - return ( - - ); -} diff --git a/datahub-web-react/src/app/entity/chart/ChartEntity.tsx b/datahub-web-react/src/app/entity/chart/ChartEntity.tsx deleted file mode 100644 index 1e6a3427ee5291..00000000000000 --- a/datahub-web-react/src/app/entity/chart/ChartEntity.tsx +++ /dev/null @@ -1,297 +0,0 @@ -import { LineChartOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { ChartQueryTab } from '@app/entity/chart/ChartQueryTab'; -import { ChartPreview } from '@app/entity/chart/preview/ChartPreview'; -import { ChartStatsSummarySubHeader } from '@app/entity/chart/profile/stats/ChartStatsSummarySubHeader'; -import { EntityMenuItems } from '@app/entity/shared/EntityDropdown/EntityDropdown'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import DataProductSection from '@app/entity/shared/containers/profile/sidebar/DataProduct/DataProductSection'; -import { SidebarDomainSection } from '@app/entity/shared/containers/profile/sidebar/Domain/SidebarDomainSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import { SidebarTagsSection } from '@app/entity/shared/containers/profile/sidebar/SidebarTagsSection'; -import SidebarStructuredPropsSection from '@app/entity/shared/containers/profile/sidebar/StructuredProperties/SidebarStructuredPropsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import EmbeddedProfile from '@app/entity/shared/embed/EmbeddedProfile'; -import { DocumentationTab } from '@app/entity/shared/tabs/Documentation/DocumentationTab'; -import { EmbedTab } from '@app/entity/shared/tabs/Embed/EmbedTab'; -import { ChartDashboardsTab } from '@app/entity/shared/tabs/Entity/ChartDashboardsTab'; -import { InputFieldsTab } from '@app/entity/shared/tabs/Entity/InputFieldsTab'; -import { IncidentTab } from '@app/entity/shared/tabs/Incident/IncidentTab'; -import { LineageTab } from '@app/entity/shared/tabs/Lineage/LineageTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; -import { GenericEntityProperties } from '@app/entity/shared/types'; -import { getDataProduct } from '@app/entity/shared/utils'; -import { LOOKER_URN } from '@app/ingest/source/builder/constants'; -import { MatchedFieldList } from '@app/search/matches/MatchedFieldList'; -import { matchedInputFieldRenderer } from '@app/search/matches/matchedInputFieldRenderer'; -import { capitalizeFirstLetterOnly } from '@app/shared/textUtil'; - -import { GetChartQuery, useGetChartQuery, useUpdateChartMutation } from '@graphql/chart.generated'; -import { Chart, EntityType, LineageDirection, SearchResult } from '@types'; - -/** - * Definition of the DataHub Chart entity. - */ -export class ChartEntity implements Entity { - type: EntityType = EntityType.Chart; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - if (styleType === IconStyleType.SVG) { - return ( - - ); - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => true; - - isLineageEnabled = () => true; - - getAutoCompleteFieldName = () => 'title'; - - getGraphName = () => 'chart'; - - getPathName = () => 'chart'; - - getEntityName = () => 'Chart'; - - getCollectionName = () => 'Charts'; - - useEntityQuery = useGetChartQuery; - - getSidebarSections = () => [ - { - component: SidebarAboutSection, - }, - { - component: SidebarOwnerSection, - }, - { - component: SidebarTagsSection, - properties: { - hasTags: true, - hasTerms: true, - }, - }, - { - component: SidebarDomainSection, - }, - { - component: DataProductSection, - }, - { - component: SidebarStructuredPropsSection, - }, - ]; - - renderProfile = (urn: string) => ( - (chart?.chart?.query?.rawQuery && true) || false, - enabled: (_, chart: GetChartQuery) => (chart?.chart?.query?.rawQuery && true) || false, - }, - }, - { - name: 'Documentation', - component: DocumentationTab, - }, - { - name: 'Fields', - component: InputFieldsTab, - display: { - visible: (_, chart: GetChartQuery) => (chart?.chart?.inputFields?.fields?.length || 0) > 0, - enabled: (_, chart: GetChartQuery) => (chart?.chart?.inputFields?.fields?.length || 0) > 0, - }, - }, - { - name: 'Preview', - component: EmbedTab, - display: { - visible: (_, chart: GetChartQuery) => - !!chart?.chart?.embed?.renderUrl && chart?.chart?.platform?.urn === LOOKER_URN, - enabled: (_, chart: GetChartQuery) => - !!chart?.chart?.embed?.renderUrl && chart?.chart?.platform?.urn === LOOKER_URN, - }, - }, - { - name: 'Lineage', - component: LineageTab, - properties: { - defaultDirection: LineageDirection.Upstream, - }, - }, - { - name: 'Dashboards', - component: ChartDashboardsTab, - display: { - visible: (_, _1) => true, - enabled: (_, chart: GetChartQuery) => (chart?.chart?.dashboards?.total || 0) > 0, - }, - }, - { - name: 'Properties', - component: PropertiesTab, - }, - { - name: 'Incidents', - component: IncidentTab, - getDynamicName: (_, chart) => { - const activeIncidentCount = chart?.chart?.activeIncidents?.total; - return `Incidents${(activeIncidentCount && ` (${activeIncidentCount})`) || ''}`; - }, - }, - ]} - sidebarSections={this.getSidebarSections()} - /> - ); - - getOverridePropertiesFromEntity = (chart?: Chart | null): GenericEntityProperties => { - // TODO: Get rid of this once we have correctly formed platform coming back. - const name = chart?.properties?.name; - const subTypes = chart?.subTypes; - const externalUrl = chart?.properties?.externalUrl; - return { - name, - externalUrl, - entityTypeOverride: subTypes ? capitalizeFirstLetterOnly(subTypes.typeNames?.[0]) : '', - }; - }; - - renderPreview = (_: PreviewType, data: Chart) => { - const genericProperties = this.getGenericEntityProperties(data); - return ( - - ); - }; - - renderSearch = (result: SearchResult) => { - const data = result.entity as Chart; - const genericProperties = this.getGenericEntityProperties(data); - return ( - matchedInputFieldRenderer(matchedField, data)} - /> - } - degree={(result as any).degree} - paths={(result as any).paths} - health={data.health} - /> - ); - }; - - getLineageVizConfig = (entity: Chart) => { - return { - urn: entity.urn, - name: entity.properties?.name || entity.urn, - type: EntityType.Chart, - icon: entity?.platform?.properties?.logoUrl || undefined, - platform: entity?.platform, - subtype: entity?.subTypes?.typeNames?.[0] || undefined, - health: entity?.health || undefined, - }; - }; - - displayName = (data: Chart) => { - return data.properties?.name || data.urn; - }; - - getGenericEntityProperties = (data: Chart) => { - return getDataForEntityType({ - data, - entityType: this.type, - getOverrideProperties: this.getOverridePropertiesFromEntity, - }); - }; - - supportedCapabilities = () => { - return new Set([ - EntityCapabilityType.OWNERS, - EntityCapabilityType.GLOSSARY_TERMS, - EntityCapabilityType.TAGS, - EntityCapabilityType.DOMAINS, - EntityCapabilityType.DEPRECATION, - EntityCapabilityType.SOFT_DELETE, - EntityCapabilityType.DATA_PRODUCTS, - ]); - }; - - renderEmbeddedProfile = (urn: string) => ( - - ); -} diff --git a/datahub-web-react/src/app/entity/chart/ChartQueryTab.tsx b/datahub-web-react/src/app/entity/chart/ChartQueryTab.tsx deleted file mode 100644 index 02bd34aa1d2ffc..00000000000000 --- a/datahub-web-react/src/app/entity/chart/ChartQueryTab.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { Typography } from 'antd'; -import React from 'react'; -import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; -import styled from 'styled-components'; - -import { useBaseEntity } from '@app/entity/shared/EntityContext'; -import { InfoItem } from '@app/entity/shared/components/styled/InfoItem'; -import { ANTD_GRAY } from '@app/entity/shared/constants'; - -import { GetChartQuery } from '@graphql/chart.generated'; - -const InfoSection = styled.div` - border-bottom: 1px solid ${ANTD_GRAY[4.5]}; - padding: 16px 20px; -`; - -const InfoItemContainer = styled.div<{ justifyContent }>` - display: flex; - position: relative; - justify-content: ${(props) => props.justifyContent}; - padding: 12px 2px; -`; - -const InfoItemContent = styled.div` - padding-top: 8px; -`; - -const QueryText = styled(Typography.Paragraph)` - margin-top: 20px; - background-color: ${ANTD_GRAY[2]}; -`; - -// NOTE: Yes, using `!important` is a shame. However, the SyntaxHighlighter is applying styles directly -// to the component, so there's no way around this -const NestedSyntax = styled(SyntaxHighlighter)` - background-color: transparent !important; - border: none !important; -`; - -export function ChartQueryTab() { - const baseEntity = useBaseEntity(); - const query = baseEntity?.chart?.query?.rawQuery || 'UNKNOWN'; - const type = baseEntity?.chart?.query?.type || 'UNKNOWN'; - - return ( - <> - - Details - - - {type.toUpperCase()} - - - - - Query - - {query} - - - - ); -} diff --git a/datahub-web-react/src/app/entity/chart/preview/ChartPreview.tsx b/datahub-web-react/src/app/entity/chart/preview/ChartPreview.tsx deleted file mode 100644 index a8a13a6b7ef48c..00000000000000 --- a/datahub-web-react/src/app/entity/chart/preview/ChartPreview.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import React from 'react'; - -import { IconStyleType, PreviewType } from '@app/entity/Entity'; -import { ChartStatsSummary as ChartStatsSummaryView } from '@app/entity/chart/shared/ChartStatsSummary'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { capitalizeFirstLetterOnly } from '@app/shared/textUtil'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { - AccessLevel, - ChartStatsSummary, - Container, - DataProduct, - Deprecation, - Domain, - EntityPath, - EntityType, - GlobalTags, - GlossaryTerms, - Health, - Owner, - ParentContainersResult, - SearchInsight, -} from '@types'; - -export const ChartPreview = ({ - urn, - name, - description, - platform, - platformInstanceId, - access, - owners, - tags, - glossaryTerms, - domain, - dataProduct, - container, - insights, - logoUrl, - deprecation, - statsSummary, - lastUpdatedMs, - createdMs, - externalUrl, - parentContainers, - snippet, - degree, - paths, - subType, - health, - previewType, -}: { - urn: string; - platform?: string; - platformInstanceId?: string; - name?: string; - description?: string | null; - access?: AccessLevel | null; - owners?: Array | null; - tags?: GlobalTags; - glossaryTerms?: GlossaryTerms | null; - domain?: Domain | null; - dataProduct?: DataProduct | null; - container?: Container | null; - insights?: Array | null; - logoUrl?: string | null; - deprecation?: Deprecation | null; - statsSummary?: ChartStatsSummary | null; - lastUpdatedMs?: number | null; - createdMs?: number | null; - externalUrl?: string | null; - parentContainers?: ParentContainersResult | null; - snippet?: React.ReactNode | null; - degree?: number; - paths?: EntityPath[]; - subType?: string | null; - health?: Health[] | null; - previewType?: PreviewType; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - - return ( - - } - degree={degree} - paths={paths} - health={health || undefined} - previewType={previewType} - /> - ); -}; diff --git a/datahub-web-react/src/app/entity/chart/profile/stats/ChartStatsSummarySubHeader.tsx b/datahub-web-react/src/app/entity/chart/profile/stats/ChartStatsSummarySubHeader.tsx deleted file mode 100644 index 336fb1f1d0ec4b..00000000000000 --- a/datahub-web-react/src/app/entity/chart/profile/stats/ChartStatsSummarySubHeader.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; - -import { ChartStatsSummary } from '@app/entity/chart/shared/ChartStatsSummary'; -import { useBaseEntity } from '@app/entity/shared/EntityContext'; - -import { GetChartQuery } from '@graphql/chart.generated'; -import { ChartStatsSummary as ChartStatsSummaryObj } from '@types'; - -export const ChartStatsSummarySubHeader = () => { - const result = useBaseEntity(); - const chart = result?.chart; - const maybeStatsSummary = chart?.statsSummary as ChartStatsSummaryObj; - const viewCount = maybeStatsSummary?.viewCount; - const uniqueUserCountLast30Days = maybeStatsSummary?.uniqueUserCountLast30Days; - const lastUpdatedMs = chart?.properties?.lastModified?.time; - const createdMs = chart?.properties?.created?.time; - - return ( - - ); -}; diff --git a/datahub-web-react/src/app/entity/chart/shared/ChartStatsSummary.tsx b/datahub-web-react/src/app/entity/chart/shared/ChartStatsSummary.tsx deleted file mode 100644 index d5cf88b4a848d9..00000000000000 --- a/datahub-web-react/src/app/entity/chart/shared/ChartStatsSummary.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { ClockCircleOutlined, EyeOutlined, QuestionCircleOutlined, TeamOutlined } from '@ant-design/icons'; -import { Popover, Tooltip } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -import ExpandingStat from '@app/entity/dataset/shared/ExpandingStat'; -import { StatsSummary } from '@app/entity/shared/components/styled/StatsSummary'; -import { ANTD_GRAY } from '@app/entity/shared/constants'; -import { formatNumberWithoutAbbreviation } from '@app/shared/formatNumber'; -import { toLocalDateTimeString, toRelativeTimeString } from '@app/shared/time/timeUtils'; -import { countFormatter, needsFormatting } from '@utils/formatter'; - -const StatText = styled.span` - color: ${ANTD_GRAY[8]}; -`; - -const HelpIcon = styled(QuestionCircleOutlined)` - color: ${ANTD_GRAY[7]}; - padding-left: 4px; -`; - -type Props = { - chartCount?: number | null; - viewCount?: number | null; - uniqueUserCountLast30Days?: number | null; - lastUpdatedMs?: number | null; - createdMs?: number | null; -}; - -export const ChartStatsSummary = ({ - chartCount, - viewCount, - uniqueUserCountLast30Days, - lastUpdatedMs, - createdMs, -}: Props) => { - const statsViews = [ - (!!chartCount && ( - ( - - {isExpanded ? formatNumberWithoutAbbreviation(chartCount) : countFormatter(chartCount)}{' '} - charts - - )} - /> - )) || - undefined, - (!!viewCount && ( - - - {formatNumberWithoutAbbreviation(viewCount)} views - - )) || - undefined, - (!!uniqueUserCountLast30Days && ( - - - {formatNumberWithoutAbbreviation(uniqueUserCountLast30Days)} unique users - - )) || - undefined, - (!!lastUpdatedMs && ( - - {createdMs &&
Created on {toLocalDateTimeString(createdMs)}.
} -
- Changed on {toLocalDateTimeString(lastUpdatedMs)}.{' '} - - - -
- - } - > - - - Changed {toRelativeTimeString(lastUpdatedMs)} - -
- )) || - undefined, - ].filter((stat) => stat !== undefined); - - return <>{statsViews.length > 0 && }; -}; diff --git a/datahub-web-react/src/app/entity/container/ContainerEntitiesTab.tsx b/datahub-web-react/src/app/entity/container/ContainerEntitiesTab.tsx deleted file mode 100644 index ac3864fc0caf3a..00000000000000 --- a/datahub-web-react/src/app/entity/container/ContainerEntitiesTab.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; - -import { useEntityData } from '@app/entity/shared/EntityContext'; -import { EmbeddedListSearchSection } from '@app/entity/shared/components/styled/search/EmbeddedListSearchSection'; -import { UnionType } from '@app/search/utils/constants'; - -export const ContainerEntitiesTab = () => { - const { urn } = useEntityData(); - - const fixedFilter = { - field: 'container', - values: [urn], - }; - - return ( - - ); -}; diff --git a/datahub-web-react/src/app/entity/container/ContainerEntity.tsx b/datahub-web-react/src/app/entity/container/ContainerEntity.tsx deleted file mode 100644 index d851d27c6c2a86..00000000000000 --- a/datahub-web-react/src/app/entity/container/ContainerEntity.tsx +++ /dev/null @@ -1,235 +0,0 @@ -import { FolderOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { ContainerEntitiesTab } from '@app/entity/container/ContainerEntitiesTab'; -import { Preview } from '@app/entity/container/preview/Preview'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import DataProductSection from '@app/entity/shared/containers/profile/sidebar/DataProduct/DataProductSection'; -import { SidebarDomainSection } from '@app/entity/shared/containers/profile/sidebar/Domain/SidebarDomainSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import { SidebarTagsSection } from '@app/entity/shared/containers/profile/sidebar/SidebarTagsSection'; -import SidebarStructuredPropsSection from '@app/entity/shared/containers/profile/sidebar/StructuredProperties/SidebarStructuredPropsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import EmbeddedProfile from '@app/entity/shared/embed/EmbeddedProfile'; -import AccessManagement from '@app/entity/shared/tabs/Dataset/AccessManagement/AccessManagement'; -import { DocumentationTab } from '@app/entity/shared/tabs/Documentation/DocumentationTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; -import { getDataProduct } from '@app/entity/shared/utils'; -import { capitalizeFirstLetterOnly } from '@app/shared/textUtil'; -import { useAppConfig } from '@app/useAppConfig'; - -import { GetContainerQuery, useGetContainerQuery } from '@graphql/container.generated'; -import { Container, EntityType, SearchResult } from '@types'; - -/** - * Definition of the DataHub Container entity. - */ -export class ContainerEntity implements Entity { - type: EntityType = EntityType.Container; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - if (styleType === IconStyleType.SVG) { - return ( - - ); - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => false; - - isLineageEnabled = () => false; - - getAutoCompleteFieldName = () => 'name'; - - getGraphName = () => 'container'; - - getPathName = () => 'container'; - - getEntityName = () => 'Container'; - - getCollectionName = () => 'Containers'; - - useEntityQuery = useGetContainerQuery; - - appconfig = useAppConfig; - - renderProfile = (urn: string) => ( - { - return ( - this.appconfig().config.featureFlags.showAccessManagement && - !!container?.container?.access - ); - }, - enabled: (_, container: GetContainerQuery) => { - const accessAspect = container?.container?.access; - const rolesList = accessAspect?.roles; - return !!accessAspect && !!rolesList && rolesList.length > 0; - }, - }, - }, - { - name: 'Properties', - component: PropertiesTab, - }, - ]} - sidebarSections={this.getSidebarSections()} - /> - ); - - getSidebarSections = () => [ - { - component: SidebarAboutSection, - }, - { - component: SidebarOwnerSection, - }, - { - component: SidebarTagsSection, - properties: { - hasTags: true, - hasTerms: true, - }, - }, - { - component: SidebarDomainSection, - }, - { - component: DataProductSection, - }, - { - component: SidebarStructuredPropsSection, - }, - // TODO: Add back once entity-level recommendations are complete. - // { - // component: SidebarRecommendationsSection, - // }, - ]; - - renderPreview = (_: PreviewType, data: Container) => { - const genericProperties = this.getGenericEntityProperties(data); - return ( - - ); - }; - - renderSearch = (result: SearchResult) => { - const data = result.entity as Container; - const genericProperties = this.getGenericEntityProperties(data); - return ( - - ); - }; - - displayName = (data: Container) => { - return data?.properties?.name || data?.properties?.qualifiedName || data?.urn; - }; - - getOverridePropertiesFromEntity = (data: Container) => { - return { - name: this.displayName(data), - externalUrl: data.properties?.externalUrl, - entityCount: data.entities?.total, - }; - }; - - getGenericEntityProperties = (data: Container) => { - return getDataForEntityType({ - data, - entityType: this.type, - getOverrideProperties: this.getOverridePropertiesFromEntity, - }); - }; - - supportedCapabilities = () => { - return new Set([ - EntityCapabilityType.OWNERS, - EntityCapabilityType.GLOSSARY_TERMS, - EntityCapabilityType.TAGS, - EntityCapabilityType.DOMAINS, - EntityCapabilityType.SOFT_DELETE, - EntityCapabilityType.DATA_PRODUCTS, - ]); - }; - - renderEmbeddedProfile = (urn: string) => ( - - ); -} diff --git a/datahub-web-react/src/app/entity/container/preview/Preview.tsx b/datahub-web-react/src/app/entity/container/preview/Preview.tsx deleted file mode 100644 index 746171354f5d59..00000000000000 --- a/datahub-web-react/src/app/entity/container/preview/Preview.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -import { IconStyleType } from '@app/entity/Entity'; -import { ANTD_GRAY } from '@app/entity/shared/constants'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { capitalizeFirstLetterOnly } from '@app/shared/textUtil'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { - Container, - DataProduct, - Deprecation, - Domain, - EntityPath, - EntityType, - GlobalTags, - GlossaryTerms, - Owner, - ParentContainersResult, - SearchInsight, - SubTypes, -} from '@types'; - -const StatText = styled(Typography.Text)` - color: ${ANTD_GRAY[8]}; -`; - -export const Preview = ({ - urn, - name, - platformName, - platformLogo, - platformInstanceId, - description, - owners, - tags, - glossaryTerms, - insights, - subTypes, - logoComponent, - container, - entityCount, - domain, - dataProduct, - parentContainers, - externalUrl, - deprecation, - degree, - paths, -}: { - urn: string; - name: string; - platformName?: string; - platformLogo?: string | null; - platformInstanceId?: string; - description?: string | null; - owners?: Array | null; - tags?: GlobalTags | null; - glossaryTerms?: GlossaryTerms | null; - insights?: Array | null; - subTypes?: SubTypes | null; - logoComponent?: JSX.Element; - container?: Container | null; - entityCount?: number; - domain?: Domain | null; - dataProduct?: DataProduct | null; - deprecation?: Deprecation | null; - parentContainers?: ParentContainersResult | null; - externalUrl?: string | null; - degree?: number; - paths?: EntityPath[]; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - const typeName = capitalizeFirstLetterOnly(subTypes?.typeNames?.[0]) || 'Container'; - return ( - - {entityCount} {entityCount === 1 ? 'entity' : 'entities'} - , - ]) || - undefined - } - degree={degree} - paths={paths} - /> - ); -}; diff --git a/datahub-web-react/src/app/entity/dashboard/DashboardEntity.tsx b/datahub-web-react/src/app/entity/dashboard/DashboardEntity.tsx deleted file mode 100644 index 64409b5bfb854e..00000000000000 --- a/datahub-web-react/src/app/entity/dashboard/DashboardEntity.tsx +++ /dev/null @@ -1,305 +0,0 @@ -import { DashboardFilled, DashboardOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { DashboardPreview } from '@app/entity/dashboard/preview/DashboardPreview'; -import { DashboardStatsSummarySubHeader } from '@app/entity/dashboard/profile/DashboardStatsSummarySubHeader'; -import { EntityMenuItems } from '@app/entity/shared/EntityDropdown/EntityDropdown'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import DataProductSection from '@app/entity/shared/containers/profile/sidebar/DataProduct/DataProductSection'; -import { SidebarDomainSection } from '@app/entity/shared/containers/profile/sidebar/Domain/SidebarDomainSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import { SidebarTagsSection } from '@app/entity/shared/containers/profile/sidebar/SidebarTagsSection'; -import SidebarStructuredPropsSection from '@app/entity/shared/containers/profile/sidebar/StructuredProperties/SidebarStructuredPropsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import EmbeddedProfile from '@app/entity/shared/embed/EmbeddedProfile'; -import { DocumentationTab } from '@app/entity/shared/tabs/Documentation/DocumentationTab'; -import { EmbedTab } from '@app/entity/shared/tabs/Embed/EmbedTab'; -import { DashboardChartsTab } from '@app/entity/shared/tabs/Entity/DashboardChartsTab'; -import { DashboardDatasetsTab } from '@app/entity/shared/tabs/Entity/DashboardDatasetsTab'; -import { IncidentTab } from '@app/entity/shared/tabs/Incident/IncidentTab'; -import { LineageTab } from '@app/entity/shared/tabs/Lineage/LineageTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; -import { GenericEntityProperties } from '@app/entity/shared/types'; -import { getDataProduct } from '@app/entity/shared/utils'; -import { LOOKER_URN } from '@app/ingest/source/builder/constants'; -import { MatchedFieldList } from '@app/search/matches/MatchedFieldList'; -import { matchedInputFieldRenderer } from '@app/search/matches/matchedInputFieldRenderer'; -import { capitalizeFirstLetterOnly } from '@app/shared/textUtil'; - -import { GetDashboardQuery, useGetDashboardQuery, useUpdateDashboardMutation } from '@graphql/dashboard.generated'; -import { Dashboard, EntityType, LineageDirection, OwnershipType, SearchResult } from '@types'; - -/** - * Definition of the DataHub Dashboard entity. - */ -export class DashboardEntity implements Entity { - type: EntityType = EntityType.Dashboard; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - if (styleType === IconStyleType.SVG) { - return ( - - ); - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => true; - - isLineageEnabled = () => true; - - getAutoCompleteFieldName = () => 'title'; - - getPathName = () => 'dashboard'; - - getEntityName = () => 'Dashboard'; - - getCollectionName = () => 'Dashboards'; - - useEntityQuery = useGetDashboardQuery; - - getSidebarSections = () => [ - { - component: SidebarAboutSection, - }, - { - component: SidebarOwnerSection, - properties: { - defaultOwnerType: OwnershipType.TechnicalOwner, - }, - }, - { - component: SidebarTagsSection, - properties: { - hasTags: true, - hasTerms: true, - }, - }, - { - component: SidebarDomainSection, - }, - { - component: DataProductSection, - }, - { - component: SidebarStructuredPropsSection, - }, - ]; - - renderProfile = (urn: string) => ( - - (dashboard?.dashboard?.charts?.total || 0) > 0 || - (dashboard?.dashboard?.datasets?.total || 0) === 0, - enabled: (_, dashboard: GetDashboardQuery) => (dashboard?.dashboard?.charts?.total || 0) > 0, - }, - }, - { - name: 'Datasets', - component: DashboardDatasetsTab, - display: { - visible: (_, dashboard: GetDashboardQuery) => (dashboard?.dashboard?.datasets?.total || 0) > 0, - enabled: (_, dashboard: GetDashboardQuery) => (dashboard?.dashboard?.datasets?.total || 0) > 0, - }, - }, - { - name: 'Documentation', - component: DocumentationTab, - }, - { - name: 'Preview', - component: EmbedTab, - display: { - visible: (_, dashboard: GetDashboardQuery) => - !!dashboard?.dashboard?.embed?.renderUrl && - dashboard?.dashboard?.platform?.urn === LOOKER_URN, - enabled: (_, dashboard: GetDashboardQuery) => - !!dashboard?.dashboard?.embed?.renderUrl && - dashboard?.dashboard?.platform?.urn === LOOKER_URN, - }, - }, - { - name: 'Lineage', - component: LineageTab, - properties: { - defaultDirection: LineageDirection.Upstream, - }, - }, - { - name: 'Properties', - component: PropertiesTab, - }, - { - name: 'Incidents', - component: IncidentTab, - getDynamicName: (_, dashboard) => { - const activeIncidentCount = dashboard?.dashboard?.activeIncidents?.total; - return `Incidents${(activeIncidentCount && ` (${activeIncidentCount})`) || ''}`; - }, - }, - ]} - sidebarSections={this.getSidebarSections()} - /> - ); - - getOverridePropertiesFromEntity = (dashboard?: Dashboard | null): GenericEntityProperties => { - // TODO: Get rid of this once we have correctly formed platform coming back. - const name = dashboard?.properties?.name; - const externalUrl = dashboard?.properties?.externalUrl; - const subTypes = dashboard?.subTypes; - return { - name, - externalUrl, - entityTypeOverride: subTypes ? capitalizeFirstLetterOnly(subTypes.typeNames?.[0]) : '', - }; - }; - - renderPreview = (_: PreviewType, data: Dashboard) => { - const genericProperties = this.getGenericEntityProperties(data); - return ( - - ); - }; - - renderSearch = (result: SearchResult) => { - const data = result.entity as Dashboard; - const genericProperties = this.getGenericEntityProperties(data); - - return ( - matchedInputFieldRenderer(matchedField, data)} - matchSuffix="on a contained chart" - /> - } - subtype={data.subTypes?.typeNames?.[0]} - degree={(result as any).degree} - paths={(result as any).paths} - health={data.health} - /> - ); - }; - - getLineageVizConfig = (entity: Dashboard) => { - return { - urn: entity.urn, - name: entity.properties?.name || entity.urn, - type: EntityType.Dashboard, - subtype: entity?.subTypes?.typeNames?.[0] || undefined, - icon: entity?.platform?.properties?.logoUrl || undefined, - platform: entity?.platform, - health: entity?.health || undefined, - }; - }; - - displayName = (data: Dashboard) => { - return data.properties?.name || data.urn; - }; - - getGenericEntityProperties = (data: Dashboard) => { - return getDataForEntityType({ - data, - entityType: this.type, - getOverrideProperties: this.getOverridePropertiesFromEntity, - }); - }; - - supportedCapabilities = () => { - return new Set([ - EntityCapabilityType.OWNERS, - EntityCapabilityType.GLOSSARY_TERMS, - EntityCapabilityType.TAGS, - EntityCapabilityType.DOMAINS, - EntityCapabilityType.DEPRECATION, - EntityCapabilityType.SOFT_DELETE, - EntityCapabilityType.DATA_PRODUCTS, - ]); - }; - - getGraphName = () => this.getPathName(); - - renderEmbeddedProfile = (urn: string) => ( - - ); -} diff --git a/datahub-web-react/src/app/entity/dashboard/preview/DashboardPreview.tsx b/datahub-web-react/src/app/entity/dashboard/preview/DashboardPreview.tsx deleted file mode 100644 index d4dbe48d6e5ba9..00000000000000 --- a/datahub-web-react/src/app/entity/dashboard/preview/DashboardPreview.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import React from 'react'; - -import { IconStyleType, PreviewType } from '@app/entity/Entity'; -import { DashboardStatsSummary as DashboardStatsSummaryView } from '@app/entity/dashboard/shared/DashboardStatsSummary'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { capitalizeFirstLetterOnly } from '@app/shared/textUtil'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { - AccessLevel, - Container, - DashboardStatsSummary, - DataProduct, - Deprecation, - Domain, - EntityPath, - EntityType, - GlobalTags, - GlossaryTerms, - Health, - Owner, - ParentContainersResult, - SearchInsight, -} from '@types'; - -export const DashboardPreview = ({ - urn, - platform, - platformInstanceId, - name, - subtype, - description, - access, - owners, - tags, - glossaryTerms, - domain, - dataProduct, - container, - insights, - logoUrl, - chartCount, - statsSummary, - lastUpdatedMs, - createdMs, - externalUrl, - parentContainers, - deprecation, - snippet, - degree, - paths, - health, - previewType, -}: { - urn: string; - platform?: string; - platformInstanceId?: string; - name?: string; - subtype?: string | null; - description?: string | null; - access?: AccessLevel | null; - owners?: Array | null; - tags?: GlobalTags; - glossaryTerms?: GlossaryTerms | null; - domain?: Domain | null; - dataProduct?: DataProduct | null; - container?: Container | null; - deprecation?: Deprecation | null; - insights?: Array | null; - logoUrl?: string | null; - chartCount?: number | null; - statsSummary?: DashboardStatsSummary | null; - lastUpdatedMs?: number | null; - createdMs?: number | null; - externalUrl?: string | null; - parentContainers?: ParentContainersResult | null; - snippet?: React.ReactNode | null; - degree?: number; - paths?: EntityPath[]; - health?: Health[] | null; - previewType?: PreviewType; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - - return ( - - } - degree={degree} - paths={paths} - health={health || undefined} - previewType={previewType} - /> - ); -}; diff --git a/datahub-web-react/src/app/entity/dashboard/profile/DashboardStatsSummarySubHeader.tsx b/datahub-web-react/src/app/entity/dashboard/profile/DashboardStatsSummarySubHeader.tsx deleted file mode 100644 index 9bd5771c0b567b..00000000000000 --- a/datahub-web-react/src/app/entity/dashboard/profile/DashboardStatsSummarySubHeader.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; - -import { DashboardStatsSummary } from '@app/entity/dashboard/shared/DashboardStatsSummary'; -import { useBaseEntity } from '@app/entity/shared/EntityContext'; - -import { GetDashboardQuery } from '@graphql/dashboard.generated'; -import { DashboardStatsSummary as DashboardStatsSummaryObj } from '@types'; - -export const DashboardStatsSummarySubHeader = () => { - const result = useBaseEntity(); - const dashboard = result?.dashboard; - const maybeStatsSummary = dashboard?.statsSummary as DashboardStatsSummaryObj; - const chartCount = dashboard?.charts?.total; - const viewCount = maybeStatsSummary?.viewCount; - const uniqueUserCountLast30Days = maybeStatsSummary?.uniqueUserCountLast30Days; - const lastUpdatedMs = dashboard?.properties?.lastModified?.time; - const createdMs = dashboard?.properties?.created?.time; - - return ( - - ); -}; diff --git a/datahub-web-react/src/app/entity/dashboard/shared/DashboardStatsSummary.tsx b/datahub-web-react/src/app/entity/dashboard/shared/DashboardStatsSummary.tsx deleted file mode 100644 index 48f6488cb1c418..00000000000000 --- a/datahub-web-react/src/app/entity/dashboard/shared/DashboardStatsSummary.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { ClockCircleOutlined, EyeOutlined, QuestionCircleOutlined, TeamOutlined } from '@ant-design/icons'; -import { Popover, Tooltip } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -import ExpandingStat from '@app/entity/dataset/shared/ExpandingStat'; -import { StatsSummary } from '@app/entity/shared/components/styled/StatsSummary'; -import { ANTD_GRAY } from '@app/entity/shared/constants'; -import { formatNumberWithoutAbbreviation } from '@app/shared/formatNumber'; -import { toLocalDateTimeString, toRelativeTimeString } from '@app/shared/time/timeUtils'; -import { countFormatter, needsFormatting } from '@utils/formatter'; - -const StatText = styled.span` - color: ${ANTD_GRAY[8]}; - @media (min-width: 1024px) { - white-space: nowrap; -`; - -const HelpIcon = styled(QuestionCircleOutlined)` - color: ${ANTD_GRAY[7]}; - padding-left: 4px; -`; - -type Props = { - chartCount?: number | null; - viewCount?: number | null; - uniqueUserCountLast30Days?: number | null; - lastUpdatedMs?: number | null; - createdMs?: number | null; -}; - -export const DashboardStatsSummary = ({ - chartCount, - viewCount, - uniqueUserCountLast30Days, - lastUpdatedMs, - createdMs, -}: Props) => { - const statsViews = [ - (!!chartCount && ( - ( - - {isExpanded ? formatNumberWithoutAbbreviation(chartCount) : countFormatter(chartCount)}{' '} - charts - - )} - /> - )) || - undefined, - (!!viewCount && ( - - - {formatNumberWithoutAbbreviation(viewCount)} views - - )) || - undefined, - (!!uniqueUserCountLast30Days && ( - - - {formatNumberWithoutAbbreviation(uniqueUserCountLast30Days)} unique users - - )) || - undefined, - (!!lastUpdatedMs && ( - - {createdMs &&
Created on {toLocalDateTimeString(createdMs)}.
} -
- Changed on {toLocalDateTimeString(lastUpdatedMs)}.{' '} - - - -
- - } - > - - - Changed {toRelativeTimeString(lastUpdatedMs)} - -
- )) || - undefined, - ].filter((stat) => stat !== undefined); - - return <>{statsViews.length > 0 && }; -}; diff --git a/datahub-web-react/src/app/entity/dataContract/DataContractEntity.tsx b/datahub-web-react/src/app/entity/dataContract/DataContractEntity.tsx deleted file mode 100644 index afe7efe7e3251f..00000000000000 --- a/datahub-web-react/src/app/entity/dataContract/DataContractEntity.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { FileOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, IconStyleType } from '@app/entity/Entity'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { TYPE_ICON_CLASS_NAME } from '@src/app/shared/constants'; -import { DataContract, EntityType } from '@src/types.generated'; - -/** - * Definition of the DataHub DataContract entity. - */ -export class DataContractEntity implements Entity { - type: EntityType = EntityType.DataContract; - - icon = (fontSize?: number, styleType?: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => false; - - isLineageEnabled = () => false; - - getAutoCompleteFieldName = () => 'name'; - - getGraphName = () => 'dataContract'; - - getPathName = () => 'dataContracts'; - - getEntityName = () => 'Data Contract'; - - getCollectionName = () => 'Data Contracts'; - - renderProfile = () => Not Implemented; - - getSidebarSections = () => []; - - getSidebarTabs = () => []; - - getOverridePropertiesFromEntity = () => {}; - - renderPreview = () => { - return Not Implemented; - }; - - renderSearch = () => { - return Not Implemented; - }; - - displayName = () => { - return 'Data Contract'; - }; - - getGenericEntityProperties = (data: DataContract) => { - return getDataForEntityType({ - data, - entityType: this.type, - getOverrideProperties: (newData) => newData, - }); - }; - - supportedCapabilities = () => { - return new Set([]); - }; -} diff --git a/datahub-web-react/src/app/entity/dataFlow/DataFlowEntity.tsx b/datahub-web-react/src/app/entity/dataFlow/DataFlowEntity.tsx deleted file mode 100644 index 5ab90424f7a9b8..00000000000000 --- a/datahub-web-react/src/app/entity/dataFlow/DataFlowEntity.tsx +++ /dev/null @@ -1,217 +0,0 @@ -import { ShareAltOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { Preview } from '@app/entity/dataFlow/preview/Preview'; -import { EntityMenuItems } from '@app/entity/shared/EntityDropdown/EntityDropdown'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import DataProductSection from '@app/entity/shared/containers/profile/sidebar/DataProduct/DataProductSection'; -import { SidebarDomainSection } from '@app/entity/shared/containers/profile/sidebar/Domain/SidebarDomainSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import { SidebarTagsSection } from '@app/entity/shared/containers/profile/sidebar/SidebarTagsSection'; -import SidebarStructuredPropsSection from '@app/entity/shared/containers/profile/sidebar/StructuredProperties/SidebarStructuredPropsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { DocumentationTab } from '@app/entity/shared/tabs/Documentation/DocumentationTab'; -import { DataFlowJobsTab } from '@app/entity/shared/tabs/Entity/DataFlowJobsTab'; -import { IncidentTab } from '@app/entity/shared/tabs/Incident/IncidentTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; -import { GenericEntityProperties } from '@app/entity/shared/types'; -import { getDataProduct } from '@app/entity/shared/utils'; -import { capitalizeFirstLetterOnly } from '@app/shared/textUtil'; - -import { useGetDataFlowQuery, useUpdateDataFlowMutation } from '@graphql/dataFlow.generated'; -import { DataFlow, EntityType, OwnershipType, SearchResult } from '@types'; - -/** - * Definition of the DataHub DataFlow entity. - */ -export class DataFlowEntity implements Entity { - type: EntityType = EntityType.DataFlow; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => true; - - isLineageEnabled = () => false; - - getAutoCompleteFieldName = () => 'name'; - - getGraphName = () => 'dataFlow'; - - getPathName = () => 'pipelines'; - - getEntityName = () => 'Pipeline'; - - getCollectionName = () => 'Pipelines'; - - useEntityQuery = useGetDataFlowQuery; - - renderProfile = (urn: string) => ( - { - const activeIncidentCount = dataFlow?.dataFlow?.activeIncidents?.total; - return `Incidents${(activeIncidentCount && ` (${activeIncidentCount})`) || ''}`; - }, - }, - ]} - sidebarSections={this.getSidebarSections()} - /> - ); - - getSidebarSections = () => [ - { - component: SidebarAboutSection, - }, - { - component: SidebarOwnerSection, - properties: { - defaultOwnerType: OwnershipType.TechnicalOwner, - }, - }, - { - component: SidebarTagsSection, - properties: { - hasTags: true, - hasTerms: true, - }, - }, - { - component: SidebarDomainSection, - }, - { - component: DataProductSection, - }, - { - component: SidebarStructuredPropsSection, - }, - ]; - - getOverridePropertiesFromEntity = (dataFlow?: DataFlow | null): GenericEntityProperties => { - // TODO: Get rid of this once we have correctly formed platform coming back. - const name = dataFlow?.properties?.name; - const externalUrl = dataFlow?.properties?.externalUrl; - return { - name, - externalUrl, - }; - }; - - renderPreview = (_: PreviewType, data: DataFlow) => { - const genericProperties = this.getGenericEntityProperties(data); - return ( - - ); - }; - - renderSearch = (result: SearchResult) => { - const data = result.entity as DataFlow; - const genericProperties = this.getGenericEntityProperties(data); - return ( - - ); - }; - - displayName = (data: DataFlow) => { - return data.properties?.name || data.urn; - }; - - getGenericEntityProperties = (data: DataFlow) => { - return getDataForEntityType({ - data, - entityType: this.type, - getOverrideProperties: this.getOverridePropertiesFromEntity, - }); - }; - - supportedCapabilities = () => { - return new Set([ - EntityCapabilityType.OWNERS, - EntityCapabilityType.GLOSSARY_TERMS, - EntityCapabilityType.TAGS, - EntityCapabilityType.DOMAINS, - EntityCapabilityType.DEPRECATION, - EntityCapabilityType.SOFT_DELETE, - EntityCapabilityType.DATA_PRODUCTS, - ]); - }; -} diff --git a/datahub-web-react/src/app/entity/dataFlow/preview/Preview.tsx b/datahub-web-react/src/app/entity/dataFlow/preview/Preview.tsx deleted file mode 100644 index 850b0d5cadb4bc..00000000000000 --- a/datahub-web-react/src/app/entity/dataFlow/preview/Preview.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -import { IconStyleType } from '@app/entity/Entity'; -import { ANTD_GRAY } from '@app/entity/shared/constants'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { - DataProduct, - Deprecation, - Domain, - EntityPath, - EntityType, - GlobalTags, - Health, - Owner, - ParentContainersResult, - SearchInsight, -} from '@types'; - -const StatText = styled(Typography.Text)` - color: ${ANTD_GRAY[8]}; -`; - -export const Preview = ({ - urn, - name, - platformInstanceId, - description, - platformName, - platformLogo, - owners, - globalTags, - domain, - dataProduct, - externalUrl, - snippet, - insights, - jobCount, - deprecation, - degree, - paths, - health, - parentContainers, -}: { - urn: string; - name: string; - platformInstanceId?: string; - description?: string | null; - platformName?: string; - platformLogo?: string | null; - owners?: Array | null; - domain?: Domain | null; - dataProduct?: DataProduct | null; - globalTags?: GlobalTags | null; - deprecation?: Deprecation | null; - externalUrl?: string | null; - snippet?: React.ReactNode | null; - insights?: Array | null; - jobCount?: number | null; - degree?: number; - paths?: EntityPath[]; - health?: Health[] | null; - parentContainers?: ParentContainersResult | null; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - return ( - - {jobCount} {entityRegistry.getCollectionName(EntityType.DataJob)} - , - ]) || - undefined - } - degree={degree} - paths={paths} - health={health || undefined} - parentContainers={parentContainers} - /> - ); -}; diff --git a/datahub-web-react/src/app/entity/dataJob/DataJobEntity.tsx b/datahub-web-react/src/app/entity/dataJob/DataJobEntity.tsx deleted file mode 100644 index c86ff0761fca5c..00000000000000 --- a/datahub-web-react/src/app/entity/dataJob/DataJobEntity.tsx +++ /dev/null @@ -1,268 +0,0 @@ -import { ConsoleSqlOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { DataFlowEntity } from '@app/entity/dataFlow/DataFlowEntity'; -import { Preview } from '@app/entity/dataJob/preview/Preview'; -import { RunsTab } from '@app/entity/dataJob/tabs/RunsTab'; -import { EntityMenuItems } from '@app/entity/shared/EntityDropdown/EntityDropdown'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import DataProductSection from '@app/entity/shared/containers/profile/sidebar/DataProduct/DataProductSection'; -import { SidebarDomainSection } from '@app/entity/shared/containers/profile/sidebar/Domain/SidebarDomainSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import { SidebarTagsSection } from '@app/entity/shared/containers/profile/sidebar/SidebarTagsSection'; -import SidebarStructuredPropsSection from '@app/entity/shared/containers/profile/sidebar/StructuredProperties/SidebarStructuredPropsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { DocumentationTab } from '@app/entity/shared/tabs/Documentation/DocumentationTab'; -import { DataJobFlowTab } from '@app/entity/shared/tabs/Entity/DataJobFlowTab'; -import { IncidentTab } from '@app/entity/shared/tabs/Incident/IncidentTab'; -import { LineageTab } from '@app/entity/shared/tabs/Lineage/LineageTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; -import { GenericEntityProperties } from '@app/entity/shared/types'; -import { getDataProduct } from '@app/entity/shared/utils'; -import { capitalizeFirstLetterOnly } from '@app/shared/textUtil'; - -import { GetDataJobQuery, useGetDataJobQuery, useUpdateDataJobMutation } from '@graphql/dataJob.generated'; -import { DataJob, EntityType, OwnershipType, SearchResult } from '@types'; - -const getDataJobPlatformName = (data?: DataJob): string => { - return ( - data?.dataFlow?.platform?.properties?.displayName || - capitalizeFirstLetterOnly(data?.dataFlow?.platform?.name) || - '' - ); -}; - -/** - * Definition of the DataHub DataJob entity. - */ -export class DataJobEntity implements Entity { - type: EntityType = EntityType.DataJob; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => false; - - isLineageEnabled = () => true; - - getAutoCompleteFieldName = () => 'name'; - - getGraphName = () => 'dataJob'; - - getPathName = () => 'tasks'; - - getEntityName = () => 'Task'; - - getCollectionName = () => 'Tasks'; - - useEntityQuery = useGetDataJobQuery; - - renderProfile = (urn: string) => ( - true, - enabled: (_, dataJob: GetDataJobQuery) => (dataJob?.dataJob?.runs?.total || 0) !== 0, - }, - }, - { - name: 'Incidents', - component: IncidentTab, - getDynamicName: (_, dataJob) => { - const activeIncidentCount = dataJob?.dataJob?.activeIncidents?.total; - return `Incidents${(activeIncidentCount && ` (${activeIncidentCount})`) || ''}`; - }, - }, - ]} - sidebarSections={this.getSidebarSections()} - /> - ); - - getSidebarSections = () => [ - { - component: SidebarAboutSection, - }, - { - component: SidebarOwnerSection, - properties: { - defaultOwnerType: OwnershipType.TechnicalOwner, - }, - }, - { - component: SidebarTagsSection, - properties: { - hasTags: true, - hasTerms: true, - }, - }, - { - component: SidebarDomainSection, - }, - { - component: DataProductSection, - }, - { - component: SidebarStructuredPropsSection, - }, - ]; - - getOverridePropertiesFromEntity = (dataJob?: DataJob | null): GenericEntityProperties => { - // TODO: Get rid of this once we have correctly formed platform coming back. - const name = dataJob?.properties?.name; - const externalUrl = dataJob?.properties?.externalUrl; - return { - name, - externalUrl, - platform: dataJob?.dataFlow?.platform, - }; - }; - - renderPreview = (_: PreviewType, data: DataJob) => { - const genericProperties = this.getGenericEntityProperties(data); - return ( - - ); - }; - - renderSearch = (result: SearchResult) => { - const data = result.entity as DataJob; - const genericProperties = this.getGenericEntityProperties(data); - return ( - - ); - }; - - getExpandedNameForDataJob = (entity: DataJob): string => { - const name = this.displayName(entity); - const flowName = entity?.dataFlow ? new DataFlowEntity().displayName(entity?.dataFlow) : undefined; - - // if we have no name, just return blank. this should not happen, so dont try & construct a name - if (!name) { - return ''; - } - - // if we have a flow name, return the full name of flow.task - if (flowName) { - return `${flowName}.${name}`; - } - - // otherwise, just return the task name (same as non-expanded) - return name; - }; - - getLineageVizConfig = (entity: DataJob) => { - return { - urn: entity?.urn, - name: this.displayName(entity), - expandedName: this.getExpandedNameForDataJob(entity), - type: EntityType.DataJob, - icon: entity?.dataFlow?.platform?.properties?.logoUrl || undefined, - platform: entity?.dataFlow?.platform, - health: entity?.health || undefined, - }; - }; - - displayName = (data: DataJob) => { - return data.properties?.name || data.urn; - }; - - getGenericEntityProperties = (data: DataJob) => { - return getDataForEntityType({ - data, - entityType: this.type, - getOverrideProperties: this.getOverridePropertiesFromEntity, - }); - }; - - supportedCapabilities = () => { - return new Set([ - EntityCapabilityType.OWNERS, - EntityCapabilityType.GLOSSARY_TERMS, - EntityCapabilityType.TAGS, - EntityCapabilityType.DOMAINS, - EntityCapabilityType.DEPRECATION, - EntityCapabilityType.SOFT_DELETE, - EntityCapabilityType.DATA_PRODUCTS, - ]); - }; -} diff --git a/datahub-web-react/src/app/entity/dataJob/preview/Preview.tsx b/datahub-web-react/src/app/entity/dataJob/preview/Preview.tsx deleted file mode 100644 index 686b6040cce86f..00000000000000 --- a/datahub-web-react/src/app/entity/dataJob/preview/Preview.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { ClockCircleOutlined } from '@ant-design/icons'; -import { Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -import { IconStyleType } from '@app/entity/Entity'; -import { ANTD_GRAY } from '@app/entity/shared/constants'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { toRelativeTimeString } from '@app/shared/time/timeUtils'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { - DataProduct, - Deprecation, - Domain, - EntityPath, - EntityType, - GlobalTags, - Health, - Owner, - ParentContainersResult, - SearchInsight, -} from '@types'; - -const StatText = styled(Typography.Text)` - color: ${ANTD_GRAY[8]}; -`; - -export const Preview = ({ - urn, - name, - subType, - description, - platformName, - platformLogo, - platformInstanceId, - owners, - domain, - dataProduct, - deprecation, - globalTags, - snippet, - insights, - lastRunTimeMs, - externalUrl, - degree, - paths, - health, - parentContainers, -}: { - urn: string; - name: string; - subType?: string | null; - description?: string | null; - platformName: string; - platformLogo?: string | null; - platformInstanceId?: string; - owners?: Array | null; - domain?: Domain | null; - dataProduct?: DataProduct | null; - deprecation?: Deprecation | null; - globalTags?: GlobalTags | null; - snippet?: React.ReactNode | null; - insights?: Array | null; - lastRunTimeMs?: number | null; - externalUrl?: string | null; - degree?: number; - paths?: EntityPath[]; - health?: Health[] | null; - parentContainers?: ParentContainersResult | null; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - return ( - - - Last run {toRelativeTimeString(lastRunTimeMs)} - , - ]) || - undefined - } - degree={degree} - paths={paths} - health={health || undefined} - parentContainers={parentContainers} - /> - ); -}; diff --git a/datahub-web-react/src/app/entity/dataJob/tabs/RunsTab.tsx b/datahub-web-react/src/app/entity/dataJob/tabs/RunsTab.tsx deleted file mode 100644 index 7ac08b1176df38..00000000000000 --- a/datahub-web-react/src/app/entity/dataJob/tabs/RunsTab.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import { DeliveredProcedureOutlined } from '@ant-design/icons'; -import { Pagination, Table, Tooltip, Typography } from 'antd'; -import React, { useState } from 'react'; -import styled from 'styled-components'; - -import { useEntityData } from '@app/entity/shared/EntityContext'; -import { ANTD_GRAY } from '@app/entity/shared/constants'; -import { - getExecutionRequestStatusDisplayColor, - getExecutionRequestStatusDisplayText, - getExecutionRequestStatusIcon, -} from '@app/ingest/source/utils'; -import { CompactEntityNameList } from '@app/recommendations/renderer/component/CompactEntityNameList'; -import { scrollToTop } from '@app/shared/searchUtils'; - -import { useGetDataJobRunsQuery } from '@graphql/dataJob.generated'; -import { DataProcessInstanceRunResultType, DataProcessRunStatus } from '@types'; - -import LoadingSvg from '@images/datahub-logo-color-loading_pendulum.svg?react'; - -const ExternalUrlLink = styled.a` - font-size: 16px; - color: ${ANTD_GRAY[8]}; -`; - -const PaginationControlContainer = styled.div` - padding-top: 16px; - padding-bottom: 16px; - text-align: center; -`; - -const LoadingText = styled.div` - margin-top: 18px; - font-size: 12px; -`; - -const LoadingContainer = styled.div` - padding-top: 40px; - padding-bottom: 40px; - width: 100%; - text-align: center; -`; - -function getStatusForStyling(status: DataProcessRunStatus, resultType: DataProcessInstanceRunResultType) { - if (status === 'COMPLETE') { - if (resultType === 'SKIPPED') { - return 'CANCELLED'; - } - return resultType; - } - return 'RUNNING'; -} - -const columns = [ - { - title: 'Time', - dataIndex: 'time', - key: 'time', - render: (value) => ( - {new Date(Number(value)).toLocaleString()} - ), - }, - { - title: 'Run ID', - dataIndex: 'name', - key: 'name', - }, - { - title: 'Status', - dataIndex: 'status', - key: 'status', - render: (status: any, row) => { - const statusForStyling = getStatusForStyling(status, row?.resultType); - const Icon = getExecutionRequestStatusIcon(statusForStyling); - const text = getExecutionRequestStatusDisplayText(statusForStyling); - const color = getExecutionRequestStatusDisplayColor(statusForStyling); - return ( - <> -
- {Icon && } - - {text || 'N/A'} - -
- - ); - }, - }, - { - title: 'Inputs', - dataIndex: 'inputs', - key: 'inputs', - render: (inputs) => , - }, - { - title: 'Outputs', - dataIndex: 'outputs', - key: 'outputs', - render: (outputs) => , - }, - { - title: '', - dataIndex: 'externalUrl', - key: 'externalUrl', - render: (externalUrl) => - externalUrl && ( - - - - - - ), - }, -]; - -const PAGE_SIZE = 20; - -export const RunsTab = () => { - const { urn } = useEntityData(); - const [page, setPage] = useState(1); - - const { loading, data } = useGetDataJobRunsQuery({ - variables: { urn, start: (page - 1) * PAGE_SIZE, count: PAGE_SIZE }, - }); - const runs = data && data?.dataJob?.runs?.runs; - - const tableData = runs - ?.filter((run) => run) - .map((run) => ({ - time: run?.created?.time, - name: run?.name, - status: run?.state?.[0]?.status, - resultType: run?.state?.[0]?.result?.resultType, - inputs: run?.inputs?.relationships?.map((relationship) => relationship.entity), - outputs: run?.outputs?.relationships?.map((relationship) => relationship.entity), - externalUrl: run?.externalUrl, - })); - if (loading) { - return ( - - - Fetching runs... - - ); - } - - const onChangePage = (newPage: number) => { - scrollToTop(); - setPage(newPage); - }; - - return ( - <> - - - - - - ); -}; diff --git a/datahub-web-react/src/app/entity/dataPlatform/DataPlatformEntity.tsx b/datahub-web-react/src/app/entity/dataPlatform/DataPlatformEntity.tsx deleted file mode 100644 index 8b3dbb864d6a5c..00000000000000 --- a/datahub-web-react/src/app/entity/dataPlatform/DataPlatformEntity.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { DatabaseOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { GenericEntityProperties } from '@app/entity/shared/types'; - -import { useGetDataPlatformQuery } from '@graphql/dataPlatform.generated'; -import { DataPlatform, EntityType, SearchResult } from '@types'; - -const getDisplayName = (data?: DataPlatform): string => { - return data?.properties?.displayName || data?.name || ''; -}; - -/** - * Definition of the DataHub DataJob entity. - */ -export class DataPlatformEntity implements Entity { - type: EntityType = EntityType.DataPlatform; - - icon = (fontSize: number, _styleType: IconStyleType, color?: string) => { - return ( - - ); - }; - - isSearchEnabled = () => false; - - isBrowseEnabled = () => false; - - isLineageEnabled = () => false; - - // Currently unused. - getAutoCompleteFieldName = () => 'name'; - - // Currently unused. - getPathName = () => 'platform'; - - // Currently unused. - getEntityName = () => 'Data Platform'; - - // Currently unused. - getCollectionName = () => 'Data Platforms'; - - useEntityQuery = useGetDataPlatformQuery; - - // Currently unused. - renderProfile = (_: string) => <>; - - // Currently unused. - renderPreview = (_: PreviewType, _1: DataPlatform) => <>; - - // Currently unused. - renderSearch = (_: SearchResult) => <>; - - displayName = (data: DataPlatform) => { - return getDisplayName(data); - }; - - getGenericEntityProperties = (data: DataPlatform) => { - return { - ...data, - entityType: this.type, - name: getDisplayName(data), - platform: data, - } as GenericEntityProperties; - }; - - supportedCapabilities = () => { - return new Set([]); - }; - - getGraphName = () => { - return 'dataPlatform'; - }; -} diff --git a/datahub-web-react/src/app/entity/dataPlatformInstance/DataPlatformInstanceEntity.tsx b/datahub-web-react/src/app/entity/dataPlatformInstance/DataPlatformInstanceEntity.tsx deleted file mode 100644 index 684fb2cea0c541..00000000000000 --- a/datahub-web-react/src/app/entity/dataPlatformInstance/DataPlatformInstanceEntity.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import * as React from 'react'; - -import { Entity } from '@app/entity/Entity'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { GenericEntityProperties } from '@app/entity/shared/types'; - -import { DataPlatformInstance, EntityType } from '@types'; - -/** - * Definition of the DataHub DataPlatformInstance entity. - * Most of this still needs to be filled out. - */ -export class DataPlatformInstanceEntity implements Entity { - type: EntityType = EntityType.DataPlatformInstance; - - icon = () => { - return <>; - }; - - isSearchEnabled = () => false; - - isBrowseEnabled = () => false; - - isLineageEnabled = () => false; - - getAutoCompleteFieldName = () => 'name'; - - getPathName = () => 'dataPlatformInstance'; - - getEntityName = () => 'Data Platform Instance'; - - getCollectionName = () => 'Data Platform Instances'; - - renderProfile = () => <>; - - getOverridePropertiesFromEntity = (): GenericEntityProperties => { - return {}; - }; - - renderPreview = () => { - return <>; - }; - - renderSearch = () => { - return <>; - }; - - displayName = (data: DataPlatformInstance) => { - return data?.instanceId || data.urn; - }; - - getGenericEntityProperties = (data: DataPlatformInstance) => { - return getDataForEntityType({ - data, - entityType: this.type, - getOverrideProperties: this.getOverridePropertiesFromEntity, - }); - }; - - supportedCapabilities = () => { - return new Set([]); - }; - - getGraphName = () => { - return 'dataPlatformInstance'; - }; -} diff --git a/datahub-web-react/src/app/entity/dataProcessInstance/DataProcessInstanceEntity.tsx b/datahub-web-react/src/app/entity/dataProcessInstance/DataProcessInstanceEntity.tsx deleted file mode 100644 index 2b038801dc62f2..00000000000000 --- a/datahub-web-react/src/app/entity/dataProcessInstance/DataProcessInstanceEntity.tsx +++ /dev/null @@ -1,223 +0,0 @@ -import { ApiOutlined } from '@ant-design/icons'; -import React from 'react'; - -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { Preview } from '@app/entity/dataProcessInstance/preview/Preview'; -import SummaryTab from '@app/entity/dataProcessInstance/profile/DataProcessInstanceSummary'; -import { EntityMenuItems } from '@app/entity/shared/EntityDropdown/EntityDropdown'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import DataProductSection from '@app/entity/shared/containers/profile/sidebar/DataProduct/DataProductSection'; -import { SidebarDomainSection } from '@app/entity/shared/containers/profile/sidebar/Domain/SidebarDomainSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import { SidebarTagsSection } from '@app/entity/shared/containers/profile/sidebar/SidebarTagsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { LineageTab } from '@app/entity/shared/tabs/Lineage/LineageTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; -import { GenericEntityProperties } from '@app/entity/shared/types'; -import { getDataProduct } from '@app/entity/shared/utils'; - -import { GetDataProcessInstanceQuery, useGetDataProcessInstanceQuery } from '@graphql/dataProcessInstance.generated'; -import { DataProcessInstance, EntityType, Entity as GraphQLEntity, OwnershipType, SearchResult } from '@types'; - -const getParentEntities = (data: DataProcessInstance): GraphQLEntity[] => { - const parentEntity = data?.relationships?.relationships?.find( - (rel) => rel.type === 'InstanceOf' && rel.entity?.type === EntityType.DataJob, - ); - - if (!parentEntity || !parentEntity.entity) { - return []; - } - - // First cast to unknown, then to Entity with proper type - return [parentEntity.entity]; -}; - -/** - * Definition of the DataHub DataProcessInstance entity. - */ -export class DataProcessInstanceEntity implements Entity { - type: EntityType = EntityType.DataProcessInstance; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - return ( - - ); - }; - - isSearchEnabled = () => false; - - isBrowseEnabled = () => false; - - isLineageEnabled = () => true; - - getAutoCompleteFieldName = () => 'name'; - - getPathName = () => 'dataProcessInstance'; - - getEntityName = () => 'Process Instance'; - - getGraphName = () => 'dataProcessInstance'; - - getCollectionName = () => 'Process Instances'; - - useEntityQuery = useGetDataProcessInstanceQuery; - - renderProfile = (urn: string) => ( - - ); - - getSidebarSections = () => [ - { - component: SidebarAboutSection, - }, - { - component: SidebarOwnerSection, - properties: { - defaultOwnerType: OwnershipType.TechnicalOwner, - }, - }, - { - component: SidebarTagsSection, - properties: { - hasTags: true, - hasTerms: true, - }, - }, - { - component: SidebarDomainSection, - }, - { - component: DataProductSection, - }, - ]; - - getOverridePropertiesFromEntity = (processInstance?: DataProcessInstance | null): GenericEntityProperties => { - return { - name: processInstance && this.displayName(processInstance), - platform: (processInstance as GetDataProcessInstanceQuery['dataProcessInstance'])?.optionalPlatform, - }; - }; - - renderPreview = (_: PreviewType, data: DataProcessInstance) => { - const genericProperties = this.getGenericEntityProperties(data); - const parentEntities = getParentEntities(data); - return ( - - ); - }; - - renderSearch = (result: SearchResult) => { - const data = result.entity as DataProcessInstance; - const genericProperties = this.getGenericEntityProperties(data); - const parentEntities = getParentEntities(data); - - return ( - - ); - }; - - getLineageVizConfig = (entity: DataProcessInstance) => { - const properties = this.getGenericEntityProperties(entity); - return { - urn: entity?.urn, - name: this.displayName(entity), - type: EntityType.DataProcessInstance, - subtype: entity?.subTypes?.typeNames?.[0], - icon: properties?.platform?.properties?.logoUrl ?? undefined, - platform: properties?.platform ?? undefined, - container: entity?.container, - }; - }; - - displayName = (data: DataProcessInstance) => { - return data.properties?.name || data.urn; - }; - - getGenericEntityProperties = (data: DataProcessInstance) => { - return getDataForEntityType({ - data, - entityType: this.type, - getOverrideProperties: this.getOverridePropertiesFromEntity, - }); - }; - - supportedCapabilities = () => { - return new Set([ - EntityCapabilityType.OWNERS, - EntityCapabilityType.GLOSSARY_TERMS, - EntityCapabilityType.TAGS, - EntityCapabilityType.DOMAINS, - EntityCapabilityType.DEPRECATION, - EntityCapabilityType.SOFT_DELETE, - EntityCapabilityType.DATA_PRODUCTS, - ]); - }; -} diff --git a/datahub-web-react/src/app/entity/dataProcessInstance/preview/Preview.tsx b/datahub-web-react/src/app/entity/dataProcessInstance/preview/Preview.tsx deleted file mode 100644 index 42e8e71f2f9a96..00000000000000 --- a/datahub-web-react/src/app/entity/dataProcessInstance/preview/Preview.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import React from 'react'; - -import { IconStyleType } from '@app/entity/Entity'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { - Container, - DataProcessRunEvent, - DataProduct, - Deprecation, - Domain, - EntityPath, - EntityType, - Entity as GeneratedEntity, - GlobalTags, - Health, - Owner, - ParentContainersResult, - SearchInsight, -} from '@types'; - -export const Preview = ({ - urn, - name, - subType, - description, - platformName, - platformLogo, - platformInstanceId, - container, - owners, - domain, - dataProduct, - deprecation, - globalTags, - snippet, - insights, - externalUrl, - degree, - paths, - health, - parentEntities, - parentContainers, - lastRunEvent, -}: { - urn: string; - name: string; - subType?: string | null; - description?: string | null; - platformName?: string; - platformLogo?: string | null; - platformInstanceId?: string; - container?: Container; - owners?: Array | null; - domain?: Domain | null; - dataProduct?: DataProduct | null; - deprecation?: Deprecation | null; - globalTags?: GlobalTags | null; - snippet?: React.ReactNode | null; - insights?: Array | null; - externalUrl?: string | null; - degree?: number; - paths?: EntityPath[]; - health?: Health[] | null; - parentEntities?: Array | null; - parentContainers?: ParentContainersResult | null; - lastRunEvent?: DataProcessRunEvent | null; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - return ( - - ); -}; diff --git a/datahub-web-react/src/app/entity/dataProduct/DataProductEntitiesTab.tsx b/datahub-web-react/src/app/entity/dataProduct/DataProductEntitiesTab.tsx deleted file mode 100644 index 6f5cdc369e2b96..00000000000000 --- a/datahub-web-react/src/app/entity/dataProduct/DataProductEntitiesTab.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; - -import generateUseListDataProductAssets from '@app/entity/dataProduct/generateUseListDataProductAssets'; -import { useEntityData } from '@app/entity/shared/EntityContext'; -import { EmbeddedListSearchSection } from '@app/entity/shared/components/styled/search/EmbeddedListSearchSection'; - -export function DataProductEntitiesTab() { - const { urn } = useEntityData(); - - return ( - - ); -} diff --git a/datahub-web-react/src/app/entity/dataProduct/DataProductEntity.tsx b/datahub-web-react/src/app/entity/dataProduct/DataProductEntity.tsx deleted file mode 100644 index 14d7f332571e9e..00000000000000 --- a/datahub-web-react/src/app/entity/dataProduct/DataProductEntity.tsx +++ /dev/null @@ -1,204 +0,0 @@ -import { FileDoneOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { DataProductEntitiesTab } from '@app/entity/dataProduct/DataProductEntitiesTab'; -import { Preview } from '@app/entity/dataProduct/preview/Preview'; -import { EntityMenuItems } from '@app/entity/shared/EntityDropdown/EntityDropdown'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import { SidebarViewDefinitionSection } from '@app/entity/shared/containers/profile/sidebar/Dataset/View/SidebarViewDefinitionSection'; -import { SidebarDomainSection } from '@app/entity/shared/containers/profile/sidebar/Domain/SidebarDomainSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import { SidebarTagsSection } from '@app/entity/shared/containers/profile/sidebar/SidebarTagsSection'; -import SidebarStructuredPropsSection from '@app/entity/shared/containers/profile/sidebar/StructuredProperties/SidebarStructuredPropsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { EntityActionItem } from '@app/entity/shared/entity/EntityActions'; -import { DocumentationTab } from '@app/entity/shared/tabs/Documentation/DocumentationTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; - -import { useGetDataProductQuery } from '@graphql/dataProduct.generated'; -import { GetDatasetQuery } from '@graphql/dataset.generated'; -import { DataProduct, EntityType, OwnershipType, SearchResult } from '@types'; - -/** - * Definition of the DataHub Data Product entity. - */ -export class DataProductEntity implements Entity { - type: EntityType = EntityType.DataProduct; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - if (styleType === IconStyleType.SVG) { - return ( - - ); - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => true; - - isLineageEnabled = () => false; - - getAutoCompleteFieldName = () => 'name'; - - getPathName = () => 'dataProduct'; - - getEntityName = () => 'Data Product'; - - getCollectionName = () => 'Data Products'; - - useEntityQuery = useGetDataProductQuery; - - renderProfile = (urn: string) => ( - - ); - - getSidebarSections = () => [ - { - component: SidebarAboutSection, - }, - { - component: SidebarOwnerSection, - properties: { - defaultOwnerType: OwnershipType.TechnicalOwner, - }, - }, - { - component: SidebarViewDefinitionSection, - display: { - // to do - change when we have a GetDataProductQuery - visible: (_, dataset: GetDatasetQuery) => (dataset?.dataset?.viewProperties?.logic && true) || false, - }, - }, - { - component: SidebarTagsSection, - properties: { - hasTags: true, - hasTerms: true, - }, - }, - { - component: SidebarDomainSection, - properties: { - updateOnly: true, - }, - }, - { - component: SidebarStructuredPropsSection, - }, - ]; - - renderPreview = (_: PreviewType, data: DataProduct) => { - return ( - - ); - }; - - renderSearch = (result: SearchResult) => { - const data = result.entity as DataProduct; - return ( - - ); - }; - - displayName = (data: DataProduct) => { - return data?.properties?.name || data.urn; - }; - - getOverridePropertiesFromEntity = (data: DataProduct) => { - const name = data?.properties?.name; - const externalUrl = data?.properties?.externalUrl; - const entityCount = data?.entities?.total || undefined; - return { - name, - externalUrl, - entityCount, - }; - }; - - getGenericEntityProperties = (data: DataProduct) => { - return getDataForEntityType({ - data, - entityType: this.type, - getOverrideProperties: this.getOverridePropertiesFromEntity, - }); - }; - - supportedCapabilities = () => { - return new Set([ - EntityCapabilityType.OWNERS, - EntityCapabilityType.GLOSSARY_TERMS, - EntityCapabilityType.TAGS, - EntityCapabilityType.DOMAINS, - ]); - }; - - getGraphName = () => { - return 'dataProduct'; - }; -} diff --git a/datahub-web-react/src/app/entity/dataProduct/generateUseListDataProductAssets.ts b/datahub-web-react/src/app/entity/dataProduct/generateUseListDataProductAssets.ts deleted file mode 100644 index 931f2fcc87fdb4..00000000000000 --- a/datahub-web-react/src/app/entity/dataProduct/generateUseListDataProductAssets.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { GetSearchResultsParams } from '@app/entity/shared/components/styled/search/types'; - -import { useListDataProductAssetsQuery } from '@graphql/search.generated'; - -export default function generateUseListDataProductAssets({ urn }: { urn: string }) { - return (params: GetSearchResultsParams) => { - const { - variables: { input }, - } = params; - - const { data, loading, error, refetch } = useListDataProductAssetsQuery({ - variables: { urn, input }, - }); - - return { - data: data?.listDataProductAssets, - loading, - error, - refetch: (refetchParams: GetSearchResultsParams['variables']) => { - return refetch({ urn, input: refetchParams.input }).then((res) => res.data.listDataProductAssets); - }, - }; - }; -} diff --git a/datahub-web-react/src/app/entity/dataProduct/preview/Preview.tsx b/datahub-web-react/src/app/entity/dataProduct/preview/Preview.tsx deleted file mode 100644 index 29e07bd0430b94..00000000000000 --- a/datahub-web-react/src/app/entity/dataProduct/preview/Preview.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react'; - -import { IconStyleType } from '@app/entity/Entity'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { Domain, EntityPath, EntityType, GlobalTags, GlossaryTerms, Owner } from '@types'; - -interface Props { - urn: string; - name: string; - description?: string | null; - owners?: Array | null; - domain?: Domain | null; - globalTags?: GlobalTags | null; - glossaryTerms?: GlossaryTerms | null; - entityCount?: number; - externalUrl?: string | null; - degree?: number; - paths?: EntityPath[]; -} - -export const Preview = ({ - urn, - name, - description, - owners, - globalTags, - domain, - glossaryTerms, - entityCount, - externalUrl, - degree, - paths, -}: Props): JSX.Element => { - const entityRegistry = useEntityRegistry(); - - return ( - - ); -}; diff --git a/datahub-web-react/src/app/entity/dataset/DatasetEntity.tsx b/datahub-web-react/src/app/entity/dataset/DatasetEntity.tsx deleted file mode 100644 index 2a473ae405ca28..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/DatasetEntity.tsx +++ /dev/null @@ -1,409 +0,0 @@ -import { DatabaseFilled, DatabaseOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { Preview } from '@app/entity/dataset/preview/Preview'; -import { OperationsTab } from '@app/entity/dataset/profile/OperationsTab'; -import { DatasetStatsSummarySubHeader } from '@app/entity/dataset/profile/stats/stats/DatasetStatsSummarySubHeader'; -import { getLastUpdatedMs } from '@app/entity/dataset/shared/utils'; -import { EntityMenuItems } from '@app/entity/shared/EntityDropdown/EntityDropdown'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import DataProductSection from '@app/entity/shared/containers/profile/sidebar/DataProduct/DataProductSection'; -import { SidebarViewDefinitionSection } from '@app/entity/shared/containers/profile/sidebar/Dataset/View/SidebarViewDefinitionSection'; -import { SidebarDomainSection } from '@app/entity/shared/containers/profile/sidebar/Domain/SidebarDomainSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import { SidebarSiblingsSection } from '@app/entity/shared/containers/profile/sidebar/SidebarSiblingsSection'; -import { SidebarTagsSection } from '@app/entity/shared/containers/profile/sidebar/SidebarTagsSection'; -import SidebarStructuredPropsSection from '@app/entity/shared/containers/profile/sidebar/StructuredProperties/SidebarStructuredPropsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import EmbeddedProfile from '@app/entity/shared/embed/EmbeddedProfile'; -import AccessManagement from '@app/entity/shared/tabs/Dataset/AccessManagement/AccessManagement'; -import { GovernanceTab } from '@app/entity/shared/tabs/Dataset/Governance/GovernanceTab'; -import QueriesTab from '@app/entity/shared/tabs/Dataset/Queries/QueriesTab'; -import { RelationshipsTab } from '@app/entity/shared/tabs/Dataset/Relationship/RelationshipsTab'; -import { SchemaTab } from '@app/entity/shared/tabs/Dataset/Schema/SchemaTab'; -import StatsTab from '@app/entity/shared/tabs/Dataset/Stats/StatsTab'; -import { ValidationsTab } from '@app/entity/shared/tabs/Dataset/Validations/ValidationsTab'; -import ViewDefinitionTab from '@app/entity/shared/tabs/Dataset/View/ViewDefinitionTab'; -import { DocumentationTab } from '@app/entity/shared/tabs/Documentation/DocumentationTab'; -import { EmbedTab } from '@app/entity/shared/tabs/Embed/EmbedTab'; -import { IncidentTab } from '@app/entity/shared/tabs/Incident/IncidentTab'; -import { LineageTab } from '@app/entity/shared/tabs/Lineage/LineageTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; -import { GenericEntityProperties } from '@app/entity/shared/types'; -import { getDataProduct } from '@app/entity/shared/utils'; -import { MatchedFieldList } from '@app/search/matches/MatchedFieldList'; -import { matchedFieldPathsRenderer } from '@app/search/matches/matchedFieldPathsRenderer'; -import { capitalizeFirstLetterOnly } from '@app/shared/textUtil'; -import { useAppConfig } from '@app/useAppConfig'; - -import { GetDatasetQuery, useGetDatasetQuery, useUpdateDatasetMutation } from '@graphql/dataset.generated'; -import { Dataset, DatasetProperties, EntityType, OwnershipType, SearchResult } from '@types'; - -const SUBTYPES = { - VIEW: 'view', -}; - -/** - * Definition of the DataHub Dataset entity. - */ -export class DatasetEntity implements Entity { - type: EntityType = EntityType.Dataset; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - if (styleType === IconStyleType.SVG) { - return ( - - ); - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - appconfig = useAppConfig; - - isBrowseEnabled = () => true; - - isLineageEnabled = () => true; - - getAutoCompleteFieldName = () => 'name'; - - getPathName = () => 'dataset'; - - getGraphName = () => 'dataset'; - - getEntityName = () => 'Dataset'; - - getCollectionName = () => 'Datasets'; - - useEntityQuery = useGetDatasetQuery; - - renderProfile = (urn: string) => ( - false, - enabled: (_, _2) => false, - }, - }, - { - name: 'View Definition', - component: ViewDefinitionTab, - display: { - visible: (_, dataset: GetDatasetQuery) => - !!dataset?.dataset?.viewProperties?.logic || - !!dataset?.dataset?.subTypes?.typeNames - ?.map((t) => t.toLocaleLowerCase()) - .includes(SUBTYPES.VIEW.toLocaleLowerCase()), - enabled: (_, dataset: GetDatasetQuery) => !!dataset?.dataset?.viewProperties?.logic, - }, - }, - { - name: 'Documentation', - component: DocumentationTab, - }, - { - name: 'Preview', - component: EmbedTab, - display: { - visible: (_, dataset: GetDatasetQuery) => !!dataset?.dataset?.embed?.renderUrl, - enabled: (_, dataset: GetDatasetQuery) => !!dataset?.dataset?.embed?.renderUrl, - }, - }, - { - name: 'Lineage', - component: LineageTab, - }, - { - name: 'Access', - component: AccessManagement, - display: { - visible: (_, _1) => this.appconfig().config.featureFlags.showAccessManagement, - enabled: (_, dataset: GetDatasetQuery) => { - const accessAspect = dataset?.dataset?.access; - const rolesList = accessAspect?.roles; - return !!accessAspect && !!rolesList && rolesList.length > 0; - }, - }, - }, - { - name: 'Properties', - component: PropertiesTab, - }, - { - name: 'Queries', - component: QueriesTab, - display: { - visible: (_, _1) => true, - enabled: (_, _2) => true, - }, - }, - { - name: 'Stats', - component: StatsTab, - display: { - visible: (_, _1) => true, - enabled: (_, dataset: GetDatasetQuery) => - (dataset?.dataset?.datasetProfiles?.length || 0) > 0 || - (dataset?.dataset?.usageStats?.buckets?.length || 0) > 0 || - (dataset?.dataset?.operations?.length || 0) > 0, - }, - }, - { - name: 'Quality', - component: ValidationsTab, - display: { - visible: (_, _1) => true, - enabled: (_, dataset: GetDatasetQuery) => { - return (dataset?.dataset?.assertions?.total || 0) > 0; - }, - }, - }, - { - name: 'Governance', - component: GovernanceTab, - display: { - visible: (_, _1) => true, - enabled: (_, dataset: GetDatasetQuery) => { - return dataset?.dataset?.testResults !== null; - }, - }, - }, - { - name: 'Runs', // TODO: Rename this to DatasetRunsTab. - component: OperationsTab, - display: { - visible: (_, dataset: GetDatasetQuery) => { - return (dataset?.dataset?.runs?.total || 0) > 0; - }, - enabled: (_, dataset: GetDatasetQuery) => { - return (dataset?.dataset?.runs?.total || 0) > 0; - }, - }, - }, - { - name: 'Incidents', - component: IncidentTab, - getDynamicName: (_, dataset) => { - const activeIncidentCount = dataset?.dataset?.activeIncidents?.total; - return `Incidents${(activeIncidentCount && ` (${activeIncidentCount})`) || ''}`; - }, - }, - ]} - sidebarSections={this.getSidebarSections()} - isNameEditable - /> - ); - - getSidebarSections = () => [ - { - component: SidebarAboutSection, - }, - { - component: SidebarOwnerSection, - properties: { - defaultOwnerType: OwnershipType.TechnicalOwner, - }, - }, - { - component: SidebarSiblingsSection, - display: { - visible: (_, dataset: GetDatasetQuery) => (dataset?.dataset?.siblingsSearch?.total || 0) > 0, - }, - }, - { - component: SidebarViewDefinitionSection, - display: { - visible: (_, dataset: GetDatasetQuery) => !!dataset?.dataset?.viewProperties?.logic, - }, - }, - { - component: SidebarTagsSection, - properties: { - hasTags: true, - hasTerms: true, - }, - }, - { - component: SidebarDomainSection, - }, - { - component: DataProductSection, - }, - { - component: SidebarStructuredPropsSection, - }, - // TODO: Add back once entity-level recommendations are complete. - // { - // component: SidebarRecommendationsSection, - // }, - ]; - - getOverridePropertiesFromEntity = (dataset?: Dataset | null): GenericEntityProperties => { - // if dataset has subTypes filled out, pick the most specific subtype and return it - const subTypes = dataset?.subTypes; - const extendedProperties: DatasetProperties | undefined | null = dataset?.properties && { - ...dataset?.properties, - qualifiedName: dataset?.properties?.qualifiedName || this.displayName(dataset), - }; - return { - name: dataset && this.displayName(dataset), - externalUrl: dataset?.properties?.externalUrl, - entityTypeOverride: subTypes ? capitalizeFirstLetterOnly(subTypes.typeNames?.[0]) : '', - properties: extendedProperties, - }; - }; - - renderPreview = (_: PreviewType, data: Dataset) => { - const genericProperties = this.getGenericEntityProperties(data); - return ( - - ); - }; - - renderSearch = (result: SearchResult) => { - const data = result.entity as Dataset; - const genericProperties = this.getGenericEntityProperties(data); - - return ( - platform.properties?.displayName || capitalizeFirstLetterOnly(platform.name), - )} - platformLogos={genericProperties?.siblingPlatforms?.map((platform) => platform.properties?.logoUrl)} - owners={data.ownership?.owners} - globalTags={data.globalTags} - domain={data.domain?.domain} - dataProduct={getDataProduct(genericProperties?.dataProduct)} - deprecation={data.deprecation} - glossaryTerms={data.glossaryTerms} - subtype={data.subTypes?.typeNames?.[0]} - container={data.container} - parentContainers={data.parentContainers} - snippet={} - insights={result.insights} - externalUrl={data.properties?.externalUrl} - statsSummary={data.statsSummary} - rowCount={(data as any).lastProfile?.length && (data as any).lastProfile[0].rowCount} - columnCount={(data as any).lastProfile?.length && (data as any).lastProfile[0].columnCount} - sizeInBytes={(data as any).lastProfile?.length && (data as any).lastProfile[0].sizeInBytes} - lastUpdatedMs={getLastUpdatedMs(data.properties, (data as any)?.lastOperation)} - health={data.health} - degree={(result as any).degree} - paths={(result as any).paths} - /> - ); - }; - - getLineageVizConfig = (entity: Dataset) => { - return { - urn: entity?.urn, - name: entity?.properties?.name || entity.name, - expandedName: entity?.properties?.qualifiedName || entity?.properties?.name || entity.name, - type: EntityType.Dataset, - subtype: entity?.subTypes?.typeNames?.[0] || undefined, - icon: entity?.platform?.properties?.logoUrl || undefined, - platform: entity?.platform, - health: entity?.health || undefined, - }; - }; - - displayName = (data: Dataset) => { - return data?.editableProperties?.name || data?.properties?.name || data.name || data.urn; - }; - - platformLogoUrl = (data: Dataset) => { - return data.platform.properties?.logoUrl || undefined; - }; - - getGenericEntityProperties = (data: Dataset) => { - return getDataForEntityType({ - data, - entityType: this.type, - getOverrideProperties: this.getOverridePropertiesFromEntity, - }); - }; - - supportedCapabilities = () => { - return new Set([ - EntityCapabilityType.OWNERS, - EntityCapabilityType.GLOSSARY_TERMS, - EntityCapabilityType.TAGS, - EntityCapabilityType.DOMAINS, - EntityCapabilityType.DEPRECATION, - EntityCapabilityType.SOFT_DELETE, - EntityCapabilityType.DATA_PRODUCTS, - ]); - }; - - renderEmbeddedProfile = (urn: string) => ( - - ); -} diff --git a/datahub-web-react/src/app/entity/dataset/preview/Preview.tsx b/datahub-web-react/src/app/entity/dataset/preview/Preview.tsx deleted file mode 100644 index cf489ada040877..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/preview/Preview.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import React from 'react'; - -import { IconStyleType } from '@app/entity/Entity'; -import { DatasetStatsSummary as DatasetStatsSummaryView } from '@app/entity/dataset/shared/DatasetStatsSummary'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { capitalizeFirstLetterOnly } from '@app/shared/textUtil'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { - Container, - DataProduct, - DatasetStatsSummary, - Deprecation, - Domain, - EntityPath, - EntityType, - FabricType, - GlobalTags, - GlossaryTerms, - Health, - Maybe, - Owner, - ParentContainersResult, - SearchInsight, -} from '@types'; - -export const Preview = ({ - urn, - name, - origin, - description, - platformName, - platformLogo, - platformNames, - platformLogos, - platformInstanceId, - owners, - globalTags, - domain, - dataProduct, - deprecation, - snippet, - insights, - glossaryTerms, - subtype, - externalUrl, - container, - parentContainers, - rowCount, - columnCount, - sizeInBytes, - statsSummary, - lastUpdatedMs, - health, - degree, - paths, -}: { - urn: string; - name: string; - origin: FabricType; - description?: string | null; - platformName?: string; - platformLogo?: string | null; - platformNames?: (Maybe | undefined)[]; - platformLogos?: (Maybe | undefined)[]; - platformInstanceId?: string; - owners?: Array | null; - domain?: Domain | null; - dataProduct?: DataProduct | null; - deprecation?: Deprecation | null; - globalTags?: GlobalTags | null; - snippet?: React.ReactNode | null; - insights?: Array | null; - glossaryTerms?: GlossaryTerms | null; - subtype?: string | null; - externalUrl?: string | null; - container?: Container | null; - parentContainers?: ParentContainersResult | null; - rowCount?: number | null; - columnCount?: number | null; - sizeInBytes?: number | null; - statsSummary?: DatasetStatsSummary | null; - lastUpdatedMs?: number | null; - health?: Health[] | null; - degree?: number; - paths?: EntityPath[]; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - return ( - - } - health={health || undefined} - degree={degree} - paths={paths} - /> - ); -}; diff --git a/datahub-web-react/src/app/entity/dataset/profile/Lineage.tsx b/datahub-web-react/src/app/entity/dataset/profile/Lineage.tsx deleted file mode 100644 index cc6f245dcbf105..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/Lineage.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { Button, List, Space, Typography } from 'antd'; -import React from 'react'; -import { useHistory, useLocation } from 'react-router-dom'; -import styled from 'styled-components'; - -import { PreviewType } from '@app/entity/Entity'; -import { navigateToLineageUrl } from '@app/lineage/utils/navigateToLineageUrl'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { DownstreamEntityRelationships, EntityType, UpstreamEntityRelationships } from '@types'; - -export type Props = { - upstreamLineage?: UpstreamEntityRelationships | null; - downstreamLineage?: DownstreamEntityRelationships | null; -}; - -const ViewRawButtonContainer = styled.div` - display: flex; - justify-content: flex-end; -`; - -export default function Lineage({ upstreamLineage, downstreamLineage }: Props) { - const entityRegistry = useEntityRegistry(); - const history = useHistory(); - const location = useLocation(); - const upstreamEntities = upstreamLineage?.entities?.map((entityRelationship) => entityRelationship?.entity); - const downstreamEntities = downstreamLineage?.entities?.map((entityRelationship) => entityRelationship?.entity); - - return ( - <> -
- - - -
- - Upstream} - renderItem={(item) => ( - - {entityRegistry.renderPreview(item?.type || EntityType.Dataset, PreviewType.PREVIEW, item)} - - )} - /> - Downstream} - renderItem={(item) => ( - - {entityRegistry.renderPreview(item?.type || EntityType.Dataset, PreviewType.PREVIEW, item)} - - )} - /> - - - ); -} diff --git a/datahub-web-react/src/app/entity/dataset/profile/OperationsTab.tsx b/datahub-web-react/src/app/entity/dataset/profile/OperationsTab.tsx deleted file mode 100644 index c443efb704486b..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/OperationsTab.tsx +++ /dev/null @@ -1,243 +0,0 @@ -import { DeliveredProcedureOutlined } from '@ant-design/icons'; -import { Pagination, Table, Tooltip, Typography } from 'antd'; -import React, { useState } from 'react'; -import styled from 'styled-components'; - -import { useEntityData } from '@app/entity/shared/EntityContext'; -import { ANTD_GRAY } from '@app/entity/shared/constants'; -import { notEmpty } from '@app/entity/shared/utils'; -import { - getExecutionRequestStatusDisplayColor, - getExecutionRequestStatusDisplayText, - getExecutionRequestStatusIcon, -} from '@app/ingest/source/utils'; -import { CompactEntityNameList } from '@app/recommendations/renderer/component/CompactEntityNameList'; -import { formatDuration } from '@app/shared/formatDuration'; -import { scrollToTop } from '@app/shared/searchUtils'; - -import { GetDatasetRunsQuery, useGetDatasetRunsQuery } from '@graphql/dataset.generated'; -import { DataProcessInstanceRunResultType, DataProcessRunStatus, EntityType, RelationshipDirection } from '@types'; - -import LoadingSvg from '@images/datahub-logo-color-loading_pendulum.svg?react'; - -const ExternalUrlLink = styled.a` - font-size: 16px; - color: ${ANTD_GRAY[8]}; -`; - -const PaginationControlContainer = styled.div` - padding-top: 16px; - padding-bottom: 16px; - text-align: center; -`; - -const LoadingText = styled.div` - margin-top: 18px; - font-size: 12px; -`; - -const LoadingContainer = styled.div` - padding-top: 40px; - padding-bottom: 40px; - width: 100%; - text-align: center; -`; - -function getStatusForStyling(status: DataProcessRunStatus, resultType: DataProcessInstanceRunResultType) { - if (status === 'COMPLETE') { - if (resultType === 'SKIPPED') { - return 'CANCELLED'; - } - return resultType; - } - return 'RUNNING'; -} - -const columns = [ - { - title: 'Time', - dataIndex: 'time', - key: 'time', - render: (value) => ( - {new Date(Number(value)).toLocaleString()} - ), - }, - { - title: 'Duration', - dataIndex: 'duration', - key: 'duration', - render: (durationMs: number) => formatDuration(durationMs), - }, - { - title: 'Run ID', - dataIndex: 'name', - key: 'name', - }, - { - title: 'Task', - dataIndex: 'parentTemplate', - key: 'parentTemplate', - render: (parentTemplate) => , - }, - { - title: 'Status', - dataIndex: 'status', - key: 'status', - render: (status: any, row) => { - const statusForStyling = getStatusForStyling(status, row?.resultType); - const Icon = getExecutionRequestStatusIcon(statusForStyling); - const text = getExecutionRequestStatusDisplayText(statusForStyling); - const color = getExecutionRequestStatusDisplayColor(statusForStyling); - return ( - <> -
- {Icon && } - - {text || 'N/A'} - -
- - ); - }, - }, - { - title: 'Inputs', - dataIndex: 'inputs', - key: 'inputs', - render: (inputs) => , - }, - { - title: 'Outputs', - dataIndex: 'outputs', - key: 'outputs', - render: (outputs) => , - }, - { - title: '', - dataIndex: 'externalUrl', - key: 'externalUrl', - render: (externalUrl) => - externalUrl && ( - - - - - - ), - }, -]; - -const PAGE_SIZE = 20; - -export const OperationsTab = () => { - const { urn, entityData } = useEntityData(); - const [page, setPage] = useState(1); - - // Fetch data across all siblings. - const allUrns = [ - urn, - ...(entityData?.siblingsSearch?.searchResults || []).map((sibling) => sibling.entity.urn).filter(notEmpty), - ]; - const loadings: boolean[] = []; - const datas: GetDatasetRunsQuery[] = []; - allUrns.forEach((entityUrn) => { - // Because there's a consistent number and order of the urns, - // this usage of a hook within a loop should be safe. - // eslint-disable-next-line react-hooks/rules-of-hooks - const { loading, data } = useGetDatasetRunsQuery({ - variables: { - urn: entityUrn, - start: (page - 1) * PAGE_SIZE, - count: PAGE_SIZE, - direction: RelationshipDirection.Outgoing, - }, - }); - loadings.push(loading); - if (data) { - datas.push(data); - } - }); - - const loading = loadings.some((loadingEntry) => loadingEntry); - - // Merge the runs data from all entities. - // If there's more than one entity contributing to the data, then we can't do pagination. - let canPaginate = true; - let dataRuns: NonNullable['runs'] | undefined; - if (datas.length > 0) { - let numWithRuns = 0; - for (let i = 0; i < datas.length; i++) { - if (datas[i]?.dataset?.runs?.total) { - numWithRuns++; - } - - if (dataRuns && dataRuns.runs) { - dataRuns.runs.push(...(datas[i]?.dataset?.runs?.runs || [])); - dataRuns.total = (dataRuns.total ?? 0) + (datas[i]?.dataset?.runs?.total ?? 0); - } else { - dataRuns = JSON.parse(JSON.stringify(datas[i]?.dataset?.runs)); - } - } - - if (numWithRuns > 1) { - canPaginate = false; - } - } - - // This also sorts the runs data across all entities. - const runs = dataRuns?.runs?.sort((a, b) => (b?.created?.time ?? 0) - (a?.created?.time ?? 0)); - - const tableData = runs - ?.filter((run) => run) - .map((run) => ({ - time: run?.created?.time, - name: run?.name, - status: run?.state?.[0]?.status, - resultType: run?.state?.[0]?.result?.resultType, - duration: run?.state?.[0]?.durationMillis, - inputs: run?.inputs?.relationships?.map((relationship) => relationship.entity), - outputs: run?.outputs?.relationships?.map((relationship) => relationship.entity), - externalUrl: run?.externalUrl, - parentTemplate: run?.parentTemplate?.relationships?.[0]?.entity, - })); - - // If the table contains jobs, we need to show the job-related columns. Otherwise we can simplify the table. - const containsJobs = tableData?.some((run) => run.parentTemplate?.type !== EntityType.Dataset); - const simplifiedColumns = containsJobs - ? columns - : columns.filter((column) => !['name', 'inputs', 'outputs'].includes(column.key)); - - const onChangePage = (newPage: number) => { - scrollToTop(); - setPage(newPage); - }; - - // TODO: Much of this file is duplicated from RunsTab.tsx. We should refactor this to share code. - return ( - <> - {loading && ( - - - Fetching runs... - - )} - {!loading && ( - <> -
- {canPaginate && ( - - - - )} - - )} - - ); -}; diff --git a/datahub-web-react/src/app/entity/dataset/profile/UsageFacepile.tsx b/datahub-web-react/src/app/entity/dataset/profile/UsageFacepile.tsx deleted file mode 100644 index 7cd58d287eac67..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/UsageFacepile.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { Avatar, Tooltip } from 'antd'; -import React, { useMemo } from 'react'; -import styled from 'styled-components'; - -import { SpacedAvatarGroup } from '@app/shared/avatar/SpaceAvatarGroup'; -import getAvatarColor from '@app/shared/avatar/getAvatarColor'; - -import { UserUsageCounts } from '@types'; - -export type Props = { - users?: (UserUsageCounts | null)[] | null; - maxNumberDisplayed?: number; -}; - -const AvatarStyled = styled(Avatar)<{ backgroundColor: string }>` - color: #fff; - background-color: ${(props) => props.backgroundColor}; -`; - -export default function UsageFacepile({ users, maxNumberDisplayed }: Props) { - const sortedUsers = useMemo(() => users?.slice().sort((a, b) => (b?.count || 0) - (a?.count || 0)), [users]); - let displayedUsers = sortedUsers; - if (maxNumberDisplayed) { - displayedUsers = displayedUsers?.slice(0, maxNumberDisplayed); - } - - return ( - - {displayedUsers?.map((user) => ( - - - {user?.userEmail?.charAt(0).toUpperCase()} - - - ))} - - ); -} diff --git a/datahub-web-react/src/app/entity/dataset/profile/__tests__/Lineage.test.tsx b/datahub-web-react/src/app/entity/dataset/profile/__tests__/Lineage.test.tsx deleted file mode 100644 index 2ac0f90864b417..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/__tests__/Lineage.test.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { MockedProvider } from '@apollo/client/testing'; -import { render } from '@testing-library/react'; -import React from 'react'; - -import Lineage from '@app/entity/dataset/profile/Lineage'; -import { sampleDownstreamRelationship, sampleRelationship } from '@app/entity/dataset/profile/stories/lineageEntities'; -import { mocks } from '@src/Mocks'; -import TestPageContainer from '@utils/test-utils/TestPageContainer'; - -describe('Lineage', () => { - it('renders', () => { - const { getByText } = render( - - - , - - , - ); - expect(getByText('Upstream HiveDataset')).toBeInTheDocument(); - expect(getByText('Downstream HiveDataset')).toBeInTheDocument(); - }); -}); diff --git a/datahub-web-react/src/app/entity/dataset/profile/__tests__/Properties.test.tsx b/datahub-web-react/src/app/entity/dataset/profile/__tests__/Properties.test.tsx deleted file mode 100644 index 3936e97f1f2cb8..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/__tests__/Properties.test.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { MockedProvider } from '@apollo/client/testing'; -import { render } from '@testing-library/react'; -import React from 'react'; - -import { sampleProperties } from '@app/entity/dataset/profile/stories/properties'; -import { Properties } from '@app/entity/shared/components/legacy/Properties'; -import { mocks } from '@src/Mocks'; -import TestPageContainer from '@utils/test-utils/TestPageContainer'; - -describe('Properties', () => { - it('renders', () => { - const { getByText } = render( - - - , - - , - ); - expect(getByText('Properties')).toBeInTheDocument(); - expect(getByText('Number of Partitions')).toBeInTheDocument(); - expect(getByText('18')).toBeInTheDocument(); - expect(getByText('Cluster Name')).toBeInTheDocument(); - expect(getByText('Testing')).toBeInTheDocument(); - }); -}); diff --git a/datahub-web-react/src/app/entity/dataset/profile/__tests__/Schema.test.tsx b/datahub-web-react/src/app/entity/dataset/profile/__tests__/Schema.test.tsx deleted file mode 100644 index bdf04fead920eb..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/__tests__/Schema.test.tsx +++ /dev/null @@ -1,399 +0,0 @@ -import { MockedProvider } from '@apollo/client/testing'; -import { fireEvent, render } from '@testing-library/react'; -import React from 'react'; - -import { - sampleSchema, - sampleSchemaWithKeyValueFields, - sampleSchemaWithPkFk, - sampleSchemaWithTags, - sampleSchemaWithoutFields, -} from '@app/entity/dataset/profile/stories/sampleSchema'; -import { EntityContext } from '@app/entity/shared/EntityContext'; -import { SchemaTab } from '@app/entity/shared/tabs/Dataset/Schema/SchemaTab'; -import { SchemaRow } from '@app/entity/shared/tabs/Dataset/Schema/components/SchemaRow'; -import { mocks } from '@src/Mocks'; -import TestPageContainer from '@utils/test-utils/TestPageContainer'; - -import { EntityType, SchemaMetadata } from '@types'; - -vi.mock('virtualizedtableforantd4', async () => { - return { - ...(await vi.importActual('virtualizedtableforantd4')), - useVT: () => [{ body: { row: SchemaRow } }, vi.fn()], - }; -}); - -describe('Schema', () => { - it('renders', () => { - const { getByText } = render( - - - - - - - , - ); - expect(getByText('name')).toBeInTheDocument(); - expect(getByText('the name of the order')).toBeInTheDocument(); - expect(getByText('shipping_address')).toBeInTheDocument(); - expect(getByText('the address the order ships to')).toBeInTheDocument(); - }); - - it('renders raw', () => { - const { getByText, queryAllByTestId } = render( - - - - - - - , - ); - - expect(queryAllByTestId('schema-raw-view')).toHaveLength(0); - - const rawButton = getByText('Raw'); - fireEvent.click(rawButton); - - expect(queryAllByTestId('schema-raw-view')).toHaveLength(1); - - const schemaButton = getByText('Tabular'); - fireEvent.click(schemaButton); - - expect(queryAllByTestId('schema-raw-view')).toHaveLength(0); - }); - - it('renders tags and terms', () => { - const { getByText } = render( - - - - - - - , - ); - expect(getByText('Legacy')).toBeInTheDocument(); - expect(getByText('sample-glossary-term')).toBeInTheDocument(); - }); - - it('renders description', () => { - const { getByText } = render( - - - - - - - , - ); - expect(getByText('order id')).toBeInTheDocument(); - }); - - it('renders field', () => { - const { getByText } = render( - - - - - - - , - ); - expect(getByText('shipping_address')).toBeInTheDocument(); - }); - - it('renders primary keys', () => { - const { getByText } = render( - - - - - - - , - ); - expect(getByText('Primary Key')).toBeInTheDocument(); - }); - - it('renders foreign keys', () => { - const { getByText, getAllByText } = render( - - - - - - - , - ); - expect(getByText('Foreign Key')).toBeInTheDocument(); - - const fkButton = getByText('Foreign Key'); - fireEvent.click(fkButton); - - expect(getByText('Foreign Key to')).toBeInTheDocument(); - expect(getAllByText('Yet Another Dataset')).toHaveLength(2); - }); - - it('renders key/value toggle', () => { - const { getByText, queryByText } = render( - - - - - - - , - ); - expect(getByText('Key')).toBeInTheDocument(); - expect(getByText('Value')).toBeInTheDocument(); - expect(getByText('count')).toBeInTheDocument(); - expect(getByText('cost')).toBeInTheDocument(); - expect(queryByText('id')).not.toBeInTheDocument(); - - const keyButton = getByText('Key'); - fireEvent.click(keyButton); - - expect(getByText('Key')).toBeInTheDocument(); - expect(getByText('Value')).toBeInTheDocument(); - expect(getByText('id')).toBeInTheDocument(); - expect(queryByText('count')).not.toBeInTheDocument(); - expect(queryByText('cost')).not.toBeInTheDocument(); - }); - - it('does not renders key/value toggle when no schema', () => { - const { queryByText } = render( - - - - - - - , - ); - expect(queryByText('Key')).not.toBeInTheDocument(); - expect(queryByText('Value')).not.toBeInTheDocument(); - }); - - it('renders usage column when usage is present', () => { - const usageStats = { - buckets: [ - { - bucket: Date.now(), - metrics: { - totalSqlQueries: 10, - }, - }, - ], - aggregations: { - uniqueUserCount: 2, - totalSqlQueries: 10, - fields: [ - { - fieldName: 'id', - count: 10, - }, - { - fieldName: 'name', - count: 24, - }, - ], - }, - }; - - const { queryByText } = render( - - - - - - - , - ); - expect(queryByText('Usage')).toBeInTheDocument(); - }); -}); diff --git a/datahub-web-react/src/app/entity/dataset/profile/__tests__/SchemaDescriptionField.test.tsx b/datahub-web-react/src/app/entity/dataset/profile/__tests__/SchemaDescriptionField.test.tsx deleted file mode 100644 index f6e0e32a2e4818..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/__tests__/SchemaDescriptionField.test.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { MockedProvider } from '@apollo/client/testing'; -import { fireEvent, render, waitFor } from '@testing-library/react'; -import React from 'react'; - -import SchemaDescriptionField from '@app/entity/dataset/profile/schema/components/SchemaDescriptionField'; -import { mocks } from '@src/Mocks'; -import TestPageContainer from '@utils/test-utils/TestPageContainer'; - -describe('SchemaDescriptionField', () => { - it('renders editable description', async () => { - const { getByText, getByRole, queryByText } = render( - - - {}} - description="test description updated" - isEdited - onUpdate={async () => {}} - />{' '} - - , - ); - expect(getByRole('img')).toBeInTheDocument(); - expect(getByText('test description updated')).toBeInTheDocument(); - expect(queryByText('Update description')).not.toBeInTheDocument(); - }); - - it('renders update description modal', async () => { - const { getByText, getByRole, queryByText } = render( - - - {}} - description="test description" - original="test description" - isEdited - onUpdate={async () => {}} - /> - - , - ); - expect(queryByText('Update description')).not.toBeInTheDocument(); - fireEvent.click(getByRole('img')); - await waitFor(() => expect(getByText('Update description')).toBeInTheDocument()); - expect(getByText('Cancel')).toBeInTheDocument(); - expect(getByText('Update')).toBeInTheDocument(); - expect(getByText('Original:')).toBeInTheDocument(); - fireEvent.click(getByText('Cancel')); - await waitFor(() => expect(queryByText('Update description')).not.toBeInTheDocument()); - }); - - it('renders short messages without show more / show less', () => { - const { getByText, queryByText } = render( - {}} - description="short description" - onUpdate={() => Promise.resolve()} - />, - ); - expect(getByText('short description')).toBeInTheDocument(); - expect(queryByText('Read Less')).not.toBeInTheDocument(); - expect(queryByText('Read More')).not.toBeInTheDocument(); - }); - - describe('renders longer messages with show more / show less', () => { - const longDescription = - 'really long description over 80 characters, really long description over 80 characters, really long description over 80 characters, really long description over 80 characters, really long description over 80 characters'; - it('renders longer messages with show more when not expanded', () => { - const onClick = vi.fn(); - const { getByText, queryByText } = render( - Promise.resolve()} - />, - ); - expect(getByText('Read More')).toBeInTheDocument(); - expect(queryByText(longDescription)).not.toBeInTheDocument(); - fireEvent.click(getByText('Read More')); - expect(onClick).toHaveBeenCalled(); - }); - - it('renders longer messages with show less when expanded', () => { - const onClick = vi.fn(); - const { getByText } = render( - Promise.resolve()} - />, - ); - expect(getByText(longDescription)).toBeInTheDocument(); - expect(getByText('Read Less')).toBeInTheDocument(); - fireEvent.click(getByText('Read Less')); - expect(onClick).toHaveBeenCalled(); - }); - }); -}); diff --git a/datahub-web-react/src/app/entity/dataset/profile/__tests__/Stats.test.tsx b/datahub-web-react/src/app/entity/dataset/profile/__tests__/Stats.test.tsx deleted file mode 100644 index f299a362d392f1..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/__tests__/Stats.test.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import { MockedProvider } from '@apollo/client/testing'; -import { render } from '@testing-library/react'; -import React from 'react'; - -import SnapshotStatsView from '@app/entity/dataset/profile/stats/snapshot/SnapshotStatsView'; -import { - completeSampleProfile, - missingFieldStatsProfile, - missingTableStatsProfile, -} from '@app/entity/dataset/profile/stories/stats'; -import { mocks } from '@src/Mocks'; -import TestPageContainer from '@utils/test-utils/TestPageContainer'; - -describe('SnapshotStatsView', () => { - it('renders complete profile', () => { - const { getByText } = render( - - - - - , - ); - - // Row Count - expect(getByText('1000')).toBeInTheDocument(); - expect(getByText('Rows')).toBeInTheDocument(); - - // Column Count - expect(getByText('2000')).toBeInTheDocument(); - expect(getByText('Columns')).toBeInTheDocument(); - - // Field Profiles - // First column - expect(getByText('testColumn')).toBeInTheDocument(); - expect(getByText('1')).toBeInTheDocument(); - expect(getByText('11.10%')).toBeInTheDocument(); - expect(getByText('2')).toBeInTheDocument(); - expect(getByText('22.20%')).toBeInTheDocument(); - expect(getByText('3')).toBeInTheDocument(); - expect(getByText('4')).toBeInTheDocument(); - expect(getByText('5')).toBeInTheDocument(); - expect(getByText('6')).toBeInTheDocument(); - expect(getByText('value1')).toBeInTheDocument(); - expect(getByText('value2')).toBeInTheDocument(); - expect(getByText('value3')).toBeInTheDocument(); - - // Second column - expect(getByText('testColumn2')).toBeInTheDocument(); - expect(getByText('8')).toBeInTheDocument(); - expect(getByText('33.30%')).toBeInTheDocument(); - expect(getByText('9')).toBeInTheDocument(); - expect(getByText('44.40%')).toBeInTheDocument(); - expect(getByText('10')).toBeInTheDocument(); - expect(getByText('11')).toBeInTheDocument(); - expect(getByText('12')).toBeInTheDocument(); - expect(getByText('13')).toBeInTheDocument(); - expect(getByText('14')).toBeInTheDocument(); - expect(getByText('value4')).toBeInTheDocument(); - expect(getByText('value5')).toBeInTheDocument(); - expect(getByText('value6')).toBeInTheDocument(); - }); - - it('renders profile without field stats', () => { - const { getByText, queryByText } = render( - - - - - , - ); - - // Row Count - expect(getByText('1000')).toBeInTheDocument(); - expect(getByText('Rows')).toBeInTheDocument(); - - // Column Count - expect(getByText('2000')).toBeInTheDocument(); - expect(getByText('Columns')).toBeInTheDocument(); - - // Field Profiles - // First column - expect(queryByText('testColumn')).toBeNull(); - expect(queryByText('1')).toBeNull(); - expect(queryByText('11.10%')).toBeNull(); - expect(queryByText('2')).toBeNull(); - expect(queryByText('22.20%')).toBeNull(); - expect(queryByText('3')).toBeNull(); - expect(queryByText('4')).toBeNull(); - expect(queryByText('5')).toBeNull(); - expect(queryByText('6')).toBeNull(); - expect(queryByText('value1')).toBeNull(); - expect(queryByText('value2')).toBeNull(); - expect(queryByText('value3')).toBeNull(); - - // Second column - expect(queryByText('testColumn2')).toBeNull(); - expect(queryByText('8')).toBeNull(); - expect(queryByText('33.30%')).toBeNull(); - expect(queryByText('9')).toBeNull(); - expect(queryByText('44.40%')).toBeNull(); - expect(queryByText('10')).toBeNull(); - expect(queryByText('11')).toBeNull(); - expect(queryByText('12')).toBeNull(); - expect(queryByText('13')).toBeNull(); - expect(queryByText('14')).toBeNull(); - expect(queryByText('value4')).toBeNull(); - expect(queryByText('value5')).toBeNull(); - expect(queryByText('value6')).toBeNull(); - }); - - it('renders profile without table stats', () => { - const { getByText, queryByText } = render( - - - - - , - ); - - // Row Count - expect(queryByText('1000')).toBeNull(); - expect(queryByText('Rows')).toBeNull(); - expect(queryByText('Row Count Unknown')).toBeInTheDocument(); - - // Column Count - expect(queryByText('2000')).toBeNull(); - expect(queryByText('Columns')).toBeNull(); - expect(queryByText('Column Count Unknown')).toBeInTheDocument(); - - // Field Profiles - // First column - expect(getByText('testColumn')).toBeInTheDocument(); - expect(getByText('1')).toBeInTheDocument(); - expect(getByText('11.10%')).toBeInTheDocument(); - expect(getByText('2')).toBeInTheDocument(); - expect(getByText('22.20%')).toBeInTheDocument(); - expect(getByText('3')).toBeInTheDocument(); - expect(getByText('4')).toBeInTheDocument(); - expect(getByText('5')).toBeInTheDocument(); - expect(getByText('6')).toBeInTheDocument(); - expect(getByText('value1')).toBeInTheDocument(); - expect(getByText('value2')).toBeInTheDocument(); - expect(getByText('value3')).toBeInTheDocument(); - - // Second column - expect(getByText('testColumn2')).toBeInTheDocument(); - expect(getByText('8')).toBeInTheDocument(); - expect(getByText('33.30%')).toBeInTheDocument(); - expect(getByText('9')).toBeInTheDocument(); - expect(getByText('44.40%')).toBeInTheDocument(); - expect(getByText('10')).toBeInTheDocument(); - expect(getByText('11')).toBeInTheDocument(); - expect(getByText('12')).toBeInTheDocument(); - expect(getByText('13')).toBeInTheDocument(); - expect(getByText('14')).toBeInTheDocument(); - expect(getByText('value4')).toBeInTheDocument(); - expect(getByText('value5')).toBeInTheDocument(); - expect(getByText('value6')).toBeInTheDocument(); - }); -}); diff --git a/datahub-web-react/src/app/entity/dataset/profile/__tests__/schema/translateFieldPath.test.tsx b/datahub-web-react/src/app/entity/dataset/profile/__tests__/schema/translateFieldPath.test.tsx deleted file mode 100644 index 3e03f28fb605d4..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/__tests__/schema/translateFieldPath.test.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import translateFieldPath from '@app/entity/dataset/profile/schema/utils/translateFieldPath'; - -describe('translateFieldPath', () => { - it('translates qualified unions', () => { - expect(translateFieldPath('[type=union].[type=QualifyingStruct].struct.[type=long].field')).toEqual( - '(QualifyingStruct) struct.field', - ); - }); - - it('translates nested arrays', () => { - expect(translateFieldPath('[type=array].[type=array].my_array.[type=long].field')).toEqual( - 'my_array[][].field', - ); - }); - - it('removes non-qualifying structs', () => { - expect( - translateFieldPath('[type=array].[type=array].MyArray.[type=Struct].field.[type=long].nested_field'), - ).toEqual('MyArray[][].field.nested_field'); - }); - - it('cleans the [key=true] prefix', () => { - expect( - translateFieldPath( - '[key=True].[type=array].[type=array].MyArray.[type=Struct].field.[type=long].nested_field', - ), - ).toEqual('MyArray[][].field.nested_field'); - }); - - it('leaves old fieldpaths as is', () => { - expect(translateFieldPath('a.b.c')).toEqual('a.b.c'); - }); -}); diff --git a/datahub-web-react/src/app/entity/dataset/profile/__tests__/schema/translateFieldPathSegment.test.tsx b/datahub-web-react/src/app/entity/dataset/profile/__tests__/schema/translateFieldPathSegment.test.tsx deleted file mode 100644 index b6291ae1c01e06..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/__tests__/schema/translateFieldPathSegment.test.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import translateFieldPathSegment from '@app/entity/dataset/profile/schema/utils/translateFieldPathSegment'; - -describe('translateFieldPathSegment', () => { - it('translates unions', () => { - expect(translateFieldPathSegment('MyUnion', 1, ['[type=union]', 'MyUnion'])).toEqual('MyUnion.'); - }); - - it('translates arrays', () => { - expect(translateFieldPathSegment('MyArray', 1, ['[type=array]', 'MyArray'])).toEqual('MyArray[].'); - }); - - it('translates qualifying structs in the middle', () => { - expect( - translateFieldPathSegment('[type=QualifyingStruct]', 1, [ - '[type=union]', - '[type=QualifyingStruct]', - 'MyUnion', - ]), - ).toEqual('(QualifyingStruct) '); - }); - - it('translates qualifying structs in the end', () => { - expect( - translateFieldPathSegment('[type=QualifyingStruct]', 1, ['[type=union]', '[type=QualifyingStruct]']), - ).toEqual(' QualifyingStruct'); - }); - - it('translates primitives', () => { - expect( - translateFieldPathSegment('field', 4, [ - '[type=union]', - 'MyUnion', - '[type=QualifyingStruct]', - '[type=long]', - 'field', - ]), - ).toEqual('field.'); - }); -}); diff --git a/datahub-web-react/src/app/entity/dataset/profile/__tests__/schema/utils.test.tsx b/datahub-web-react/src/app/entity/dataset/profile/__tests__/schema/utils.test.tsx deleted file mode 100644 index c3a32a67cb497a..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/__tests__/schema/utils.test.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { filterKeyFieldPath } from '@app/entity/dataset/profile/schema/utils/utils'; - -import { SchemaFieldDataType } from '@types'; - -describe('utils', () => { - describe('filterKeyFieldPath', () => { - it('allows keys when looking for keys', () => { - expect( - filterKeyFieldPath(true, { - fieldPath: '[version=2.0].[key=True].[type=long].field', - nullable: false, - type: SchemaFieldDataType.Number, - recursive: false, - }), - ).toEqual(true); - }); - it('blocks non-keys when looking for keys', () => { - expect( - filterKeyFieldPath(true, { - fieldPath: '[version=2.0].[type=long].field', - nullable: false, - type: SchemaFieldDataType.Number, - recursive: false, - }), - ).toEqual(false); - }); - - it('allows non-keys when looking for non-keys', () => { - expect( - filterKeyFieldPath(false, { - fieldPath: '[version=2.0].[type=long].field', - nullable: false, - type: SchemaFieldDataType.Number, - recursive: false, - }), - ).toEqual(true); - }); - - it('blocks keys when looking for non-keys', () => { - expect( - filterKeyFieldPath(false, { - fieldPath: '[version=2.0].[key=True].[type=long].field', - nullable: false, - type: SchemaFieldDataType.Number, - recursive: false, - }), - ).toEqual(false); - }); - }); -}); diff --git a/datahub-web-react/src/app/entity/dataset/profile/schema/components/CustomPagination.tsx b/datahub-web-react/src/app/entity/dataset/profile/schema/components/CustomPagination.tsx deleted file mode 100644 index 68fcefbb79c6d6..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/schema/components/CustomPagination.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import { LeftOutlined, RightOutlined } from '@ant-design/icons'; -import { Button, Dropdown, Typography } from 'antd'; -import React, { useState } from 'react'; -import styled from 'styled-components'; - -import { MenuItemStyle } from '@app/entity/view/menu/item/styledComponent'; - -const CustomPaginationContainer = styled.div` - display: flex; - flex-direction: row; - height: 32px; -`; -const NavButton = styled(Button)` - margin: 4px 6px; - cursor: pointer; -`; -const DescriptionText = styled(Typography.Text)` - line-height: 32px; -`; -const VersionText = styled(Typography.Text)` - padding: 0 4px; - line-height: 32px; - cursor: pointer; -`; -const VersionRightText = styled(Typography.Text)` - padding-left: 4px; - line-height: 32px; - cursor: pointer; -`; - -type Props = { - onChange: (version1: number, version2: number) => void; - maxVersion: number; -}; - -export default function CustomPagination({ onChange, maxVersion }: Props) { - const [version1, setVersion1] = useState(maxVersion || 1); // current version - first dropdown selected - const [version2, setVersion2] = useState(maxVersion ? maxVersion - 1 : 0); // past version comparing with current - second dropdown - - const onNextClick = () => { - setVersion1((v) => v - 1); - setVersion2(version1 - 2); - onChange(version1 - 1, version1 - 2); - }; - const onPrevClick = () => { - setVersion1((v) => v + 1); - setVersion2(version1); - onChange(version1 + 1, version1); - }; - const onVersion1Click = ({ key }) => { - const newVersion1 = parseInt(key, 10); - setVersion1(newVersion1); - if (version2 >= newVersion1) { - setVersion2(newVersion1 - 1); - onChange(newVersion1, newVersion1 - 1); - return; - } - onChange(newVersion1, version2); - }; - const onVersion2Click = ({ key }) => { - setVersion2(parseInt(key, 10)); - onChange(version1, parseInt(key, 10)); - }; - - const items1 = [...Array(maxVersion)].map((_, i) => { - // eslint-disable-next-line react/no-array-index-key - return { - key: maxVersion - i, - label: ( - onVersion1Click({ key: maxVersion - i })}> - {i === 0 ? 'latest' : `version ${maxVersion + 1 - i}`} - - ), - }; - }); - - const items2 = [...Array(version1)].map((_, i) => { - // eslint-disable-next-line react/no-array-index-key - return { - key: version1 - i - 1, - label: ( - onVersion2Click({ key: version1 - i - 1 })}> - {`version ${version1 - i}`} - - ), - }; - }); - - return ( - - } - onClick={onPrevClick} - disabled={version1 >= maxVersion} - /> - Comparing - - - {version1 === maxVersion ? 'latest' : `version ${version1 + 1}`} - - - to - - {`version ${version2 + 1}`} - - } - onClick={onNextClick} - disabled={version1 <= 1} - /> - - ); -} diff --git a/datahub-web-react/src/app/entity/dataset/profile/schema/components/SchemaHeader.tsx b/datahub-web-react/src/app/entity/dataset/profile/schema/components/SchemaHeader.tsx deleted file mode 100644 index 33fa35787fe26c..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/schema/components/SchemaHeader.tsx +++ /dev/null @@ -1,304 +0,0 @@ -import { - AuditOutlined, - CaretDownOutlined, - FileTextOutlined, - QuestionCircleOutlined, - TableOutlined, -} from '@ant-design/icons'; -import { Icon } from '@components'; -import { Button, Input, Popover, Select, Tooltip, Typography } from 'antd'; -import { debounce } from 'lodash'; -import React from 'react'; -import { useHistory, useLocation } from 'react-router-dom'; -import styled from 'styled-components/macro'; - -import CustomPagination from '@app/entity/dataset/profile/schema/components/CustomPagination'; -import TabToolbar from '@app/entity/shared/components/styled/TabToolbar'; -import { ANTD_GRAY, REDESIGN_COLORS } from '@app/entity/shared/constants'; -import getSchemaFilterFromQueryString from '@app/entity/shared/tabs/Dataset/Schema/utils/getSchemaFilterFromQueryString'; -import { navigateToVersionedDatasetUrl } from '@app/entity/shared/tabs/Dataset/Schema/utils/navigateToVersionedDatasetUrl'; -import { toRelativeTimeString } from '@app/shared/time/timeUtils'; - -import { SemanticVersionStruct } from '@types'; - -const SchemaHeaderContainer = styled.div` - display: flex; - justify-content: space-between; - width: 100%; -`; - -// TODO(Gabe): undo display: none when dbt/bigquery flickering has been resolved -const ShowVersionButton = styled(Button)` - display: inline-block; - margin-right: 10px; - display: none; -`; - -// Below styles are for buttons on the left side of the Schema Header -const LeftButtonsGroup = styled.div` - &&& { - display: flex; - justify-content: left; - width: 100%; - } -`; - -const RawButton = styled(Button)` - &&& { - display: flex; - margin-right: 10px; - justify-content: left; - align-items: center; - } -`; - -const RawButtonTitleContainer = styled.span` - display: flex; - align-items: center; -`; - -const RawButtonTitle = styled(Typography.Text)` - margin-left: 6px; -`; - -const KeyButton = styled(Button)<{ $highlighted: boolean }>` - border-radius: 8px 0px 0px 8px; - font-weight: ${(props) => (props.$highlighted ? '600' : '400')}; -`; - -const ValueButton = styled(Button)<{ $highlighted: boolean }>` - border-radius: 0px 8px 8px 0px; - font-weight: ${(props) => (props.$highlighted ? '600' : '400')}; -`; - -const KeyValueButtonGroup = styled.div` - margin-right: 10px; - display: flex; -`; - -// Below styles are for buttons on the right side of the Schema Header -const RightButtonsGroup = styled.div` - padding-left: 5px; - &&& { - display: flex; - justify-content: right; - margin-top: -6px; - width: 100%; - row-gap: 12px; - } -`; - -const SchemaBlameSelector = styled(Select)` - &&& { - font-weight: 400; - margin-top: 6px; - min-width: 30px; - margin-right: 10px; - border-radius: 0px 8px 8px 0px; - } -`; - -const SchemaBlameSelectorOption = styled(Select.Option)` - &&& { - overflow: visible; - margin-top: 6px; - } -`; - -const SchemaAuditButton = styled(Button)` - &&& { - margin-top: 6px; - } -`; - -const StyledQuestionCircleOutlined = styled(QuestionCircleOutlined)` - &&& { - margin-top: 14px; - font-size: 16px; - color: ${ANTD_GRAY[6]}; - } -`; - -const StyledInput = styled(Input)` - border-radius: 70px; - max-width: 300px; -`; - -const MAX_ROWS_BEFORE_DEBOUNCE = 50; -const HALF_SECOND_IN_MS = 500; - -type Props = { - maxVersion?: number; - fetchVersions?: (version1: number, version2: number) => void; - editMode: boolean; - setEditMode?: (mode: boolean) => void; - hasRaw: boolean; - showRaw: boolean; - setShowRaw: (show: boolean) => void; - hasKeySchema: boolean; - showKeySchema: boolean; - setShowKeySchema: (show: boolean) => void; - selectedVersion: string; - versionList: Array; - showSchemaAuditView: boolean; - setShowSchemaAuditView: any; - setFilterText: (text: string) => void; - numRows: number; -}; - -export default function SchemaHeader({ - maxVersion = 0, - fetchVersions, - editMode, - setEditMode, - hasRaw, - showRaw, - setShowRaw, - hasKeySchema, - showKeySchema, - setShowKeySchema, - selectedVersion, - versionList, - showSchemaAuditView, - setShowSchemaAuditView, - setFilterText, - numRows, -}: Props) { - const history = useHistory(); - const location = useLocation(); - const onVersionChange = (version1, version2) => { - if (version1 === null || version2 === null) { - return; - } - fetchVersions?.(version1 - maxVersion, version2 - maxVersion); - }; - - const semanticVersionDisplayString = (semanticVersion: SemanticVersionStruct) => { - const semanticVersionTimestampString = - (semanticVersion?.semanticVersionTimestamp && - toRelativeTimeString(semanticVersion?.semanticVersionTimestamp)) || - 'unknown'; - return `${semanticVersion.semanticVersion} - ${semanticVersionTimestampString}`; - }; - const numVersions = versionList.length; - - const renderOptions = () => { - return versionList.map( - (semanticVersionStruct) => - semanticVersionStruct?.semanticVersion && - semanticVersionStruct?.semanticVersionTimestamp && ( - - {semanticVersionDisplayString(semanticVersionStruct)} - - ), - ); - }; - const schemaAuditToggleText = showSchemaAuditView ? 'Close column history' : 'View column history'; - - const debouncedSetFilterText = debounce( - (e: React.ChangeEvent) => setFilterText(e.target.value), - numRows > MAX_ROWS_BEFORE_DEBOUNCE ? HALF_SECOND_IN_MS : 0, - ); - const schemaFilter = getSchemaFilterFromQueryString(location); - - const docLink = 'https://docs.datahub.com/docs/dev-guides/timeline/'; - return ( - - - {maxVersion > 0 && !editMode && } - - {hasRaw && ( - setShowRaw(!showRaw)}> - {showRaw ? ( - - - Tabular - - ) : ( - - - Raw - - )} - - )} - {hasKeySchema && ( - - setShowKeySchema(true)}> - Key - - setShowKeySchema(false)}> - Value - - - )} - {maxVersion > 0 && - (editMode ? ( - setEditMode?.(false)}>Version Blame - ) : ( - setEditMode?.(true)}>Back - ))} - {!showRaw && ( - } - /> - )} - - - - setShowSchemaAuditView(!showSchemaAuditView)} - style={{ color: showSchemaAuditView ? REDESIGN_COLORS.BLUE : ANTD_GRAY[7] }} - > - - - - {numVersions > 1 && ( - <> - { - const datasetVersion: string = e as string; - navigateToVersionedDatasetUrl({ - location, - history, - datasetVersion, - }); - }} - data-testid="schema-version-selector-dropdown" - suffixIcon={} - > - {renderOptions()} - - - Semantic versions for this view were computed using Technical Schema. You can - find more info about how DataHub computes versions - - {' '} - here.{' '} - - - } - > - - - - )} - - - - ); -} diff --git a/datahub-web-react/src/app/entity/dataset/profile/schema/components/SchemaRawView.tsx b/datahub-web-react/src/app/entity/dataset/profile/schema/components/SchemaRawView.tsx deleted file mode 100644 index ac063d43fa9ee1..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/schema/components/SchemaRawView.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -import { diffJson, formatRawSchema, getRawSchema } from '@app/entity/dataset/profile/schema/utils/utils'; - -import { Schema, SchemaMetadata } from '@types'; - -type Props = { - schemaDiff: { - current?: SchemaMetadata | Schema | null; - previous?: SchemaMetadata | null; - }; - editMode: boolean; - showKeySchema: boolean; -}; - -const SchemaContainer = styled.div` - padding: 12px; -`; - -export default function SchemaRawView({ schemaDiff, editMode, showKeySchema }: Props) { - const currentSchemaRaw = formatRawSchema(getRawSchema(schemaDiff.current?.platformSchema, showKeySchema)); - - const schemaRawDiff = editMode - ? currentSchemaRaw - : diffJson(formatRawSchema(getRawSchema(schemaDiff.previous?.platformSchema, showKeySchema)), currentSchemaRaw); - - return ( - - -
-                    {schemaRawDiff}
-                
-
-
- ); -} diff --git a/datahub-web-react/src/app/entity/dataset/profile/schema/components/SchemaVersionSummary.tsx b/datahub-web-react/src/app/entity/dataset/profile/schema/components/SchemaVersionSummary.tsx deleted file mode 100644 index 2a27a41a8cf387..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/schema/components/SchemaVersionSummary.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -const SummaryContainer = styled.div` - margin-bottom: 16px; - padding-left: 10px; - & ul { - padding-inline-start: 30px; - margin-top: 5px; - } -`; - -export interface SchemaDiffSummary { - added: number; - removed: number; - updated: number; -} - -type Props = { - diffSummary: SchemaDiffSummary; -}; - -export default function SchemaVersionSummary({ diffSummary }: Props) { - return ( - -
    - {diffSummary.added ? ( -
  • - {`${diffSummary.added} column${ - diffSummary.added > 1 ? 's were' : ' was' - } added`} -
  • - ) : null} - {diffSummary.removed ? ( -
  • - {`${diffSummary.removed} column${ - diffSummary.removed > 1 ? 's were' : ' was' - } removed`} -
  • - ) : null} - {diffSummary.updated ? ( -
  • - {`${diffSummary.updated} description${ - diffSummary.updated > 1 ? 's were' : ' was' - } updated`} -
  • - ) : null} -
-
- ); -} diff --git a/datahub-web-react/src/app/entity/dataset/profile/schema/components/StructuredPropValues.tsx b/datahub-web-react/src/app/entity/dataset/profile/schema/components/StructuredPropValues.tsx deleted file mode 100644 index 727159eb17e93b..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/schema/components/StructuredPropValues.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { Tooltip } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -import StructuredPropertyValue from '@src/app/entity/shared/tabs/Properties/StructuredPropertyValue'; -import { mapStructuredPropertyToPropertyRow } from '@src/app/entity/shared/tabs/Properties/useStructuredProperties'; -import { useEntityRegistry } from '@src/app/useEntityRegistry'; -import { SchemaFieldEntity, SearchResult, StdDataType } from '@src/types.generated'; - -const ValuesContainer = styled.span` - max-width: 120px; - display: flex; -`; - -const MoreIndicator = styled.span` - float: right; -`; - -interface Props { - schemaFieldEntity: SchemaFieldEntity | undefined; - propColumn: SearchResult | undefined; -} - -const StructuredPropValues = ({ schemaFieldEntity, propColumn }: Props) => { - const entityRegistry = useEntityRegistry(); - - const property = schemaFieldEntity?.structuredProperties?.properties?.find( - (prop) => prop.structuredProperty.urn === propColumn?.entity?.urn, - ); - const propRow = property ? mapStructuredPropertyToPropertyRow(property) : undefined; - const values = propRow?.values; - const isRichText = propRow?.dataType?.info?.type === StdDataType.RichText; - - const hasMoreValues = values && values.length > 2; - const displayedValues = hasMoreValues ? values.slice(0, 1) : values; - const tooltipContent = values?.map((value) => { - const title = value.entity - ? entityRegistry.getDisplayName(value.entity.type, value.entity) - : value.value?.toString(); - return
{title}
; - }); - - return ( - <> - {values && ( - <> - {displayedValues?.map((val) => { - return ( - - - - ); - })} - {hasMoreValues && ( - - ... - - )} - - )} - - ); -}; - -export default StructuredPropValues; diff --git a/datahub-web-react/src/app/entity/dataset/profile/schema/components/TypeIcon.tsx b/datahub-web-react/src/app/entity/dataset/profile/schema/components/TypeIcon.tsx deleted file mode 100644 index df54db34b8b5c8..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/schema/components/TypeIcon.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { - CalendarOutlined, - FieldBinaryOutlined, - FieldTimeOutlined, - NumberOutlined, - QuestionCircleOutlined, - UnderlineOutlined, - UnorderedListOutlined, -} from '@ant-design/icons'; -import { Tooltip, Typography } from 'antd'; -import React, { FC } from 'react'; -import { VscFileBinary, VscSymbolString } from 'react-icons/vsc'; -import styled from 'styled-components'; - -import { capitalizeFirstLetter } from '@app/shared/textUtil'; - -import { SchemaFieldDataType } from '@types'; - -const TypeIconContainer = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - text-align: center; - margin-top: 2.5px; - width: 40px; -`; - -const TypeSubtitle = styled(Typography.Text)<{ hasicon?: string }>` - font-size: 8px; - text-align: center; - ${(props) => (props.hasicon ? '' : 'margin-top: 4px;')} -`; - -const IconSpan = styled.span` - font-size: 18px; -`; - -const DATA_TYPE_ICON_MAP: Record | null; size: number; text: string }> = - { - [SchemaFieldDataType.Boolean]: { - icon: FieldBinaryOutlined, - size: 18, - text: 'Boolean', - }, - [SchemaFieldDataType.Fixed]: { icon: FieldBinaryOutlined, size: 18, text: 'Fixed' }, - [SchemaFieldDataType.String]: { - icon: () => ( - - - - ), - size: 20, - text: 'String', - }, - [SchemaFieldDataType.Bytes]: { - icon: () => ( - - - - ), - size: 18, - text: 'Bytes', - }, - [SchemaFieldDataType.Number]: { icon: NumberOutlined, size: 14, text: 'Number' }, - [SchemaFieldDataType.Date]: { icon: CalendarOutlined, size: 18, text: 'Date' }, - [SchemaFieldDataType.Time]: { icon: FieldTimeOutlined, size: 18, text: 'Time' }, - [SchemaFieldDataType.Enum]: { icon: UnorderedListOutlined, size: 18, text: 'Enum' }, - [SchemaFieldDataType.Null]: { icon: QuestionCircleOutlined, size: 16, text: '' }, - [SchemaFieldDataType.Map]: { icon: null, size: 0, text: 'Map' }, - [SchemaFieldDataType.Array]: { icon: UnorderedListOutlined, size: 14, text: 'Array' }, - [SchemaFieldDataType.Union]: { icon: UnderlineOutlined, size: 14, text: 'Union' }, - [SchemaFieldDataType.Struct]: { icon: null, size: 0, text: 'Struct' }, - }; - -const truncate = (length: number, input?: string | null) => { - if (!input) return ''; - if (input.length > length) { - return `${input.substring(0, length)}...`; - } - return input; -}; - -type Props = { - type: SchemaFieldDataType; - nativeDataType: string | null | undefined; -}; - -export default function TypeIcon({ type, nativeDataType }: Props) { - const { icon: Icon, size, text } = DATA_TYPE_ICON_MAP[type]; - - // if unable to match type to DataHub, display native type info by default - const nativeFallback = type === SchemaFieldDataType.Null; - - // eslint-disable-next-line react/prop-types - const NativeDataTypeTooltip = ({ children }) => - nativeDataType ? ( - - {children} - - ) : ( - <>{children} - ); - - return ( - - - {Icon && } - - {nativeFallback ? truncate(250, nativeDataType) : text} - - - - ); -} diff --git a/datahub-web-react/src/app/entity/dataset/profile/schema/utils/schemaTitleRenderer.tsx b/datahub-web-react/src/app/entity/dataset/profile/schema/utils/schemaTitleRenderer.tsx deleted file mode 100644 index 93234a88bff79f..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/schema/utils/schemaTitleRenderer.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { Typography } from 'antd'; -import React, { useState } from 'react'; -import Highlight from 'react-highlighter'; -import styled from 'styled-components'; - -import translateFieldPath from '@app/entity/dataset/profile/schema/utils/translateFieldPath'; -import { ExtendedSchemaFields } from '@app/entity/dataset/profile/schema/utils/types'; -import ForeignKeyLabel from '@app/entity/shared/tabs/Dataset/Schema/components/ForeignKeyLabel'; -import NullableLabel from '@app/entity/shared/tabs/Dataset/Schema/components/NullableLabel'; -import PartitioningKeyLabel from '@app/entity/shared/tabs/Dataset/Schema/components/PartitioningKeyLabel'; -import PrimaryKeyLabel from '@app/entity/shared/tabs/Dataset/Schema/components/PrimaryKeyLabel'; -import TypeLabel from '@app/entity/shared/tabs/Dataset/Schema/components/TypeLabel'; - -import { ForeignKeyConstraint, SchemaMetadata } from '@types'; - -const MAX_FIELD_PATH_LENGTH = 200; - -// const LighterText = styled(Typography.Text)` -// color: rgba(0, 0, 0, 0.45); -// `; - -const FieldPathContainer = styled.div` - vertical-align: top; - display: inline-block; - width: 250px; - margin-top: 16px; - margin-bottom: 16px; -`; -const FieldPathText = styled(Typography.Text)` - font-size: 12px; - line-height: 22px; - font-family: 'Roboto Mono', monospace; - font-weight: 500; -`; - -// ex: [type=MetadataAuditEvent].[type=union]oldSnapshot.[type=CorpUserSnapshot].[type=array]aspects.[type=union].[type=CorpUserInfo].[type=boolean]active -export default function useSchemaTitleRenderer( - schemaMetadata: SchemaMetadata | undefined | null, - setSelectedFkFieldPath: (params: { fieldPath: string; constraint?: ForeignKeyConstraint | null } | null) => void, - filterText: string, -) { - const [highlightedConstraint, setHighlightedConstraint] = useState(null); - - return (fieldPath: string, record: ExtendedSchemaFields): JSX.Element => { - const fieldPathWithoutAnnotations = translateFieldPath(fieldPath); - const parentPathWithoutAnnotations = translateFieldPath(record.parent?.fieldPath || ''); - let pathToDisplay = fieldPathWithoutAnnotations; - - // if the parent path is a prefix of the field path, remove it for display purposes - if (parentPathWithoutAnnotations && fieldPathWithoutAnnotations.indexOf(parentPathWithoutAnnotations) === 0) { - // parent length + 1 because of the trailing `.` of the parent - pathToDisplay = fieldPathWithoutAnnotations.slice(parentPathWithoutAnnotations.length + 1); - } - - // if the field path is too long, truncate it - if (pathToDisplay.length > MAX_FIELD_PATH_LENGTH) { - pathToDisplay = `..${pathToDisplay.substring(pathToDisplay.length - MAX_FIELD_PATH_LENGTH)}`; - } - - return ( - <> - - - {pathToDisplay} - - - {(schemaMetadata?.primaryKeys?.includes(fieldPath) || record.isPartOfKey) && } - {record.isPartitioningKey && } - {record.nullable && } - {schemaMetadata?.foreignKeys - ?.filter( - (constraint) => - (constraint?.sourceFields?.filter( - (sourceField) => sourceField?.fieldPath?.trim() === fieldPath.trim(), - ).length || 0) > 0, - ) - .map((constraint) => ( - - ))} - - - ); - }; -} diff --git a/datahub-web-react/src/app/entity/dataset/profile/schema/utils/types.ts b/datahub-web-react/src/app/entity/dataset/profile/schema/utils/types.ts deleted file mode 100644 index 0b43ef2f113ea7..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/schema/utils/types.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { GlobalTags, SchemaField } from '@types'; - -export interface ExtendedSchemaFields extends SchemaField { - children?: Array; - depth?: number; - previousDescription?: string | null; - pastGlobalTags?: GlobalTags | null; - isNewRow?: boolean; - isDeletedRow?: boolean; - parent?: ExtendedSchemaFields; -} - -export enum SchemaViewType { - NORMAL, - BLAME, -} diff --git a/datahub-web-react/src/app/entity/dataset/profile/schema/utils/utils.ts b/datahub-web-react/src/app/entity/dataset/profile/schema/utils/utils.ts index 1d90ee772529dd..7b833b5f41afde 100644 --- a/datahub-web-react/src/app/entity/dataset/profile/schema/utils/utils.ts +++ b/datahub-web-react/src/app/entity/dataset/profile/schema/utils/utils.ts @@ -1,54 +1,5 @@ -import * as diff from 'diff'; -import { SchemaDiffSummary } from '@app/entity/dataset/profile/schema/components/SchemaVersionSummary'; -import { KEY_SCHEMA_PREFIX, UNION_TOKEN, VERSION_PREFIX } from '@app/entity/dataset/profile/schema/utils/constants'; -import { ExtendedSchemaFields } from '@app/entity/dataset/profile/schema/utils/types'; -import { convertTagsForUpdate } from '@app/shared/tags/utils/convertTagsForUpdate'; - -import { - EditableSchemaFieldInfo, - EditableSchemaMetadata, - EditableSchemaMetadataUpdate, - PlatformSchema, - SchemaField, -} from '@types'; - -export function convertEditableSchemaMeta( - editableSchemaMeta?: Array, - fields?: Array, -): Array { - const updatedFields = [...(fields || [])] as Array; - if (editableSchemaMeta && editableSchemaMeta.length > 0) { - editableSchemaMeta.forEach((updatedField) => { - const originalFieldIndex = updatedFields.findIndex((f) => f.fieldPath === updatedField.fieldPath); - if (originalFieldIndex > -1) { - updatedFields[originalFieldIndex] = { - ...updatedFields[originalFieldIndex], - description: updatedField.description, - globalTags: { ...updatedField.globalTags }, - }; - } - }); - } - return updatedFields; -} - -export function convertEditableSchemaMetadataForUpdate( - editableSchemaMetadata: EditableSchemaMetadata | null | undefined, -): EditableSchemaMetadataUpdate { - return { - editableSchemaFieldInfo: - editableSchemaMetadata?.editableSchemaFieldInfo?.map((editableSchemaFieldInfo) => ({ - fieldPath: editableSchemaFieldInfo?.fieldPath, - description: editableSchemaFieldInfo?.description, - globalTags: { tags: convertTagsForUpdate(editableSchemaFieldInfo?.globalTags?.tags || []) }, - })) || [], - }; -} - -export function filterKeyFieldPath(showKeySchema: boolean, field: SchemaField) { - return field.fieldPath.indexOf(KEY_SCHEMA_PREFIX) > -1 ? showKeySchema : !showKeySchema; -} +import { KEY_SCHEMA_PREFIX, VERSION_PREFIX } from '@app/entity/dataset/profile/schema/utils/constants'; export function downgradeV2FieldPath(fieldPath?: string | null) { if (!fieldPath) { @@ -64,170 +15,3 @@ export function downgradeV2FieldPath(fieldPath?: string | null) { .filter(Boolean) .join('.'); } - -export function pathMatchesNewPath(fieldPathA?: string | null, fieldPathB?: string | null) { - return fieldPathA === fieldPathB || fieldPathA === downgradeV2FieldPath(fieldPathB); -} - -// group schema fields by fieldPath and grouping for hierarchy in schema table -export function groupByFieldPath( - schemaRows?: Array, - options: { showKeySchema: boolean } = { showKeySchema: false }, -): Array { - const rows = [ - ...(schemaRows?.filter(filterKeyFieldPath.bind({}, options.showKeySchema)) || []), - ] as Array; - - const outputRows: Array = []; - const outputRowByPath = {}; - - for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) { - let parentRow: null | ExtendedSchemaFields = null; - const row = { children: undefined, ...rows[rowIndex], depth: 0 }; - - for (let j = rowIndex - 1; j >= 0; j--) { - const rowTokens = row.fieldPath.split('.'); - const isQualifyingUnionField = rowTokens[rowTokens.length - 3] === UNION_TOKEN; - if (isQualifyingUnionField) { - // in the case of unions, parent will not be a subset of the child - rowTokens.splice(rowTokens.length - 2, 1); - const parentPath = rowTokens.join('.'); - - if (rows[j].fieldPath === parentPath) { - parentRow = outputRowByPath[rows[j].fieldPath]; - break; - } - } else { - // In the case of structs, arrays, etc, parent will be the first token from - // the left of this field's name(last token of the path) that does not enclosed in []. - let parentPath: null | string = null; - for ( - let lastParentTokenIndex = rowTokens.length - 2; - lastParentTokenIndex >= 0; - --lastParentTokenIndex - ) { - const lastParentToken: string = rowTokens[lastParentTokenIndex]; - if (lastParentToken && lastParentToken[0] !== '[') { - parentPath = rowTokens.slice(0, lastParentTokenIndex + 1).join('.'); - break; - } - } - if (parentPath && rows[j].fieldPath === parentPath) { - parentRow = outputRowByPath[rows[j].fieldPath]; - break; - } - } - } - - // if the parent field exists in the ouput, add the current row as a child - if (parentRow) { - row.depth = (parentRow.depth || 0) + 1; - row.parent = parentRow; - parentRow.children = [...(parentRow.children || []), row]; - } else { - outputRows.push(row); - } - outputRowByPath[row.fieldPath] = row; - } - return outputRows; -} - -export function diffMarkdown(oldStr: string, newStr: string) { - const diffArray = diff.diffChars(oldStr || '', newStr || ''); - return diffArray - .map((diffOne) => { - if (diffOne.added) { - return `${diffOne.value}`; - } - if (diffOne.removed) { - return `${diffOne.value}`; - } - return diffOne.value; - }) - .join(''); -} - -export function diffJson(oldStr: string, newStr: string) { - const diffArray = diff.diffJson(oldStr || '', newStr || ''); - return diffArray - .map((diffOne) => { - if (diffOne.added) { - return `+${diffOne.value}`; - } - if (diffOne.removed) { - return `-${diffOne.value}`; - } - return diffOne.value; - }) - .join(''); -} - -export function formatRawSchema(schemaValue?: string | null): string { - try { - if (!schemaValue) { - return schemaValue || ''; - } - return JSON.stringify(JSON.parse(schemaValue), null, 2); - } catch (e) { - return schemaValue || ''; - } -} - -export function getRawSchema(schema: PlatformSchema | undefined | null, showKeySchema: boolean): string { - if (!schema) { - return ''; - } - - if (schema.__typename === 'TableSchema') { - return schema.schema; - } - if (schema.__typename === 'KeyValueSchema') { - return showKeySchema ? schema.keySchema : schema.valueSchema; - } - return ''; -} - -// Get diff summary between two versions and prepare to visualize description diff changes -export function getDiffSummary( - currentVersionRows?: Array, - previousVersionRows?: Array, - options: { showKeySchema: boolean } = { showKeySchema: false }, -): { rows: Array; diffSummary: SchemaDiffSummary } { - let rows = [ - ...(currentVersionRows?.filter(filterKeyFieldPath.bind({}, options.showKeySchema)) || []), - ] as Array; - const previousRows = [ - ...(previousVersionRows?.filter(filterKeyFieldPath.bind({}, options.showKeySchema)) || []), - ] as Array; - - const diffSummary: SchemaDiffSummary = { - added: 0, - removed: 0, - updated: 0, - }; - - if (previousVersionRows && previousVersionRows.length > 0) { - rows.forEach((field, rowIndex) => { - const relevantPastFieldIndex = previousRows.findIndex( - (pf) => pf.type === rows[rowIndex].type && pf.fieldPath === rows[rowIndex].fieldPath, - ); - if (relevantPastFieldIndex > -1) { - if (previousRows[relevantPastFieldIndex].description !== rows[rowIndex].description) { - rows[rowIndex] = { - ...rows[rowIndex], - previousDescription: previousRows[relevantPastFieldIndex].description, - }; - diffSummary.updated++; // Increase updated row number in diff summary - } - previousRows.splice(relevantPastFieldIndex, 1); - } else { - rows[rowIndex] = { ...rows[rowIndex], isNewRow: true }; - diffSummary.added++; // Increase added row number in diff summary - } - }); - rows = [...rows, ...previousRows.map((pf) => ({ ...pf, isDeletedRow: true }))]; - diffSummary.removed = previousRows.length; // removed row number in diff summary - } - - return { rows, diffSummary }; -} diff --git a/datahub-web-react/src/app/entity/dataset/profile/stats/Stats.tsx b/datahub-web-react/src/app/entity/dataset/profile/stats/Stats.tsx deleted file mode 100644 index 73e507ce58c7b1..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/stats/Stats.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Radio } from 'antd'; -import React, { useState } from 'react'; - -import HistoricalStatsView from '@app/entity/dataset/profile/stats/historical/HistoricalStatsView'; -import LatestStatsView from '@app/entity/dataset/profile/stats/snapshot/LatestStatsView'; - -import { DatasetProfile } from '@types'; - -export type Props = { - urn: string; - profile: DatasetProfile; -}; - -enum ViewType { - LATEST, - HISTORICAL, -} - -export default function Stats({ urn, profile }: Props) { - /** - * Determines which view should be visible: latest or historical. - */ - const [view, setView] = useState(ViewType.LATEST); - - const onChangeView = (e) => { - setView(e.target.value); - }; - - const toggleView = ( - - Latest - Historical - - ); - - return ( - <> - {view === ViewType.LATEST && } - {view === ViewType.HISTORICAL && } - - ); -} diff --git a/datahub-web-react/src/app/entity/dataset/profile/stats/StatsSection.tsx b/datahub-web-react/src/app/entity/dataset/profile/stats/StatsSection.tsx deleted file mode 100644 index 32244ee67cc374..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/stats/StatsSection.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Divider, Row, Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -const Section = styled.div` - padding-top: 24px; - padding-bottom: 40px; - margin-bottom: 20px; - width: 100%; -`; - -const ThinDivider = styled(Divider)` - margin-top: 8px; - margin-bottom: 8px; -`; - -export type Props = { - children: React.ReactNode; - title: string; - rightFloatView?: React.ReactNode; -}; - -export default function StatsSection({ children, title, rightFloatView }: Props) { - return ( -
- - {title} - {rightFloatView || } - - - {children} -
- ); -} diff --git a/datahub-web-react/src/app/entity/dataset/profile/stats/historical/HistoricalStatsView.tsx b/datahub-web-react/src/app/entity/dataset/profile/stats/historical/HistoricalStatsView.tsx deleted file mode 100644 index c1ec9ff63b5c68..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/stats/historical/HistoricalStatsView.tsx +++ /dev/null @@ -1,291 +0,0 @@ -import { Affix, Row, Select, Typography } from 'antd'; -import React, { ReactNode, useEffect, useState } from 'react'; -import styled from 'styled-components'; - -import StatsSection from '@app/entity/dataset/profile/stats/StatsSection'; -import ProfilingRunsChart from '@app/entity/dataset/profile/stats/historical/charts/ProfilingRunsChart'; -import StatChart from '@app/entity/dataset/profile/stats/historical/charts/StatChart'; -import { Message } from '@app/shared/Message'; -import { TimeWindowSize, getFixedLookbackWindow } from '@app/shared/time/timeUtils'; - -import { useGetDataProfilesLazyQuery } from '@graphql/dataset.generated'; -import { DatasetProfile, DateInterval } from '@types'; - -const HeaderRow = styled(Row)` - padding-top: 24px; - padding-bottom: 28px; - background-color: white; -`; - -const SubHeaderText = styled(Typography.Text)` - color: gray; - font-size: 16px; -`; - -const EmbeddedSelect = styled(Select)` - padding-left: 8px; -`; - -const isPresent = (val: any) => { - return val !== undefined && val !== null; -}; - -/** - * Extracts a set of points used to render charts from a list of Dataset Profiles + - * a particular numeric statistic name to extract. Note that the stat *must* be numeric for this utility to work. - */ -const extractChartValuesFromTableProfiles = (profiles: Array, statName: string) => { - return profiles - .filter((profile) => isPresent(profile[statName])) - .map((profile) => ({ - timeMs: profile.timestampMillis, - value: profile[statName] as number, - })); -}; - -/** - * Extracts a set of field-specific points used to render charts from a list of Dataset Profiles + - * a particular numeric statistic name to extract. Note that the stat *must* be numeric for this utility to work. - */ -const extractChartValuesFromFieldProfiles = (profiles: Array, fieldPath: string, statName: string) => { - return profiles - .filter((profile) => profile.fieldProfiles) - .map((profile) => { - const fieldProfiles = profile.fieldProfiles - ?.filter((field) => field.fieldPath === fieldPath) - .filter((field) => field[statName] !== null && field[statName] !== undefined); - - if (fieldProfiles?.length === 1) { - const fieldProfile = fieldProfiles[0]; - return { - timeMs: profile.timestampMillis, - value: fieldProfile[statName], - }; - } - return null; - }) - .filter((value) => value !== null); -}; - -const computeChartTickInterval = (windowSize: TimeWindowSize): DateInterval => { - switch (windowSize.interval) { - case DateInterval.Day: - return DateInterval.Hour; - case DateInterval.Week: - return DateInterval.Day; - case DateInterval.Month: - return DateInterval.Week; - case DateInterval.Year: - return DateInterval.Month; - default: - throw new Error(`Unrecognized DateInterval provided ${windowSize.interval}`); - } -}; - -const computeAllFieldPaths = (profiles: Array): Set => { - const uniqueFieldPaths = new Set(); - profiles.forEach((profile) => { - const fieldProfiles = profile.fieldProfiles || []; - fieldProfiles.forEach((fieldProfile) => { - uniqueFieldPaths.add(fieldProfile.fieldPath); - }); - }); - return uniqueFieldPaths; -}; - -/** - * Change this to add or modify the lookback windows that are selectable via the UI. - */ -const LOOKBACK_WINDOWS = [ - { text: '1 day', windowSize: { interval: DateInterval.Day, count: 1 } }, - { text: '1 week', windowSize: { interval: DateInterval.Week, count: 1 } }, - { text: '1 month', windowSize: { interval: DateInterval.Month, count: 1 } }, - { text: '3 months', windowSize: { interval: DateInterval.Month, count: 3 } }, - { text: '1 year', windowSize: { interval: DateInterval.Year, count: 1 } }, -]; - -const DEFAULT_LOOKBACK_WINDOW = '3 months'; - -const getLookbackWindowSize = (text: string) => { - for (let i = 0; i < LOOKBACK_WINDOWS.length; i++) { - const window = LOOKBACK_WINDOWS[i]; - if (window.text === text) { - return window.windowSize; - } - } - throw new Error(`Unrecognized lookback window size ${text} provided`); -}; - -export type Props = { - urn: string; - toggleView: ReactNode; -}; - -export default function HistoricalStatsView({ urn, toggleView }: Props) { - const [getDataProfiles, { data: profilesData, loading: profilesLoading }] = useGetDataProfilesLazyQuery({ - fetchPolicy: 'cache-first', - }); - - /** - * Perform initial fetch of default lookback window stats. - */ - useEffect(() => { - getDataProfiles({ - variables: { urn, ...getFixedLookbackWindow(getLookbackWindowSize(DEFAULT_LOOKBACK_WINDOW)) }, - }); - }, [urn, getDataProfiles]); - - /** - * Determines which fixed lookback window is used to display historical statistics. See above for valid options. - */ - const [selectedLookbackWindow, setSelectedLookbackWindow] = useState(DEFAULT_LOOKBACK_WINDOW); - const selectedWindowSize = getLookbackWindowSize(selectedLookbackWindow); - const selectedWindow = getFixedLookbackWindow(selectedWindowSize); - - /** - * Determines which field path is highlighted in column stats. Defaults to none. - */ - const [selectedFieldPath, setSelectedFieldPath] = useState(''); - - /** - * Change handlers. - */ - const onChangeSelectedLookbackWindow = (text) => { - const newWindowSize = getLookbackWindowSize(text); - const newTimeWindow = getFixedLookbackWindow(newWindowSize); - getDataProfiles({ - variables: { urn, ...newTimeWindow }, - }); - setSelectedLookbackWindow(text); - }; - - const onChangeSelectedFieldPath = (value) => { - setSelectedFieldPath(value); - }; - - const graphTickInterval = computeChartTickInterval(selectedWindowSize); - const graphDateRange = { - start: selectedWindow.startTime.toString(), - end: selectedWindow.endTime.toString(), - }; - - const profiles = profilesData?.dataset?.datasetProfiles || []; - const allFieldPaths = Array.from(computeAllFieldPaths(profiles)); - - if (selectedFieldPath === '' && allFieldPaths.length > 0) { - // Set initially selected field path. - setSelectedFieldPath(allFieldPaths[0]); - } - - const columnSelectView = ( - - Viewing stats for column - - {allFieldPaths.map((fieldPath) => ( - {fieldPath} - ))} - - - ); - - /** - * Compute Table Stat chart data. - */ - const rowCountChartValues = extractChartValuesFromTableProfiles(profiles, 'rowCount'); - const columnCountChartValues = extractChartValuesFromTableProfiles(profiles, 'columnCount'); - - /** - * Compute Column Stat chart data. - */ - const nullCountChartValues: Array = extractChartValuesFromFieldProfiles( - profiles, - selectedFieldPath, - 'nullCount', - ); - const nullPercentageChartValues: Array = extractChartValuesFromFieldProfiles( - profiles, - selectedFieldPath, - 'nullProportion', - ); - const distinctCountChartValues: Array = extractChartValuesFromFieldProfiles( - profiles, - selectedFieldPath, - 'uniqueCount', - ); - const distinctPercentageChartValues: Array = extractChartValuesFromFieldProfiles( - profiles, - selectedFieldPath, - 'uniqueProportion', - ); - - return ( - <> - {profilesLoading && } - - -
- Profiling History - - Viewing profiling history for the past - - {LOOKBACK_WINDOWS.map((lookbackWindow) => ( - {lookbackWindow.text} - ))} - - -
- {toggleView} -
-
- - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/datahub-web-react/src/app/entity/dataset/profile/stats/historical/charts/ProfilingRunsChart.tsx b/datahub-web-react/src/app/entity/dataset/profile/stats/historical/charts/ProfilingRunsChart.tsx deleted file mode 100644 index f88454c30756df..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/stats/historical/charts/ProfilingRunsChart.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { Button, Col, Modal, Table, Typography } from 'antd'; -import React, { useState } from 'react'; -import styled from 'styled-components'; - -import DataProfileView from '@app/entity/dataset/profile/stats/snapshot/SnapshotStatsView'; - -import { DatasetProfile } from '@types'; - -export const ChartTable = styled(Table)` - margin: 12px; - box-shadow: ${(props) => props.theme.styles['box-shadow']}; -`; - -export type Props = { - profiles: Array; -}; - -export default function ProfilingRunsChart({ profiles }: Props) { - const [showModal, setShowModal] = useState(false); - const [selectedProfileIndex, setSelectedProfileIndex] = useState(-1); - - const showProfileModal = (index: number) => { - setSelectedProfileIndex(index); - setShowModal(true); - }; - - const onClose = () => { - setShowModal(false); - setSelectedProfileIndex(-1); - }; - - const tableData = profiles.map((profile) => { - const profileDate = new Date(profile.timestampMillis); - return { - timestamp: `${profileDate.toLocaleDateString()} at ${profileDate.toLocaleTimeString()}`, - rowCount: profile.rowCount?.toString() || 'unknown', - columnCount: profile.columnCount?.toString() || 'unknown', - }; - }); - - const tableColumns = [ - { - title: 'Recent Profiles', - key: 'Recent Profiles', - dataIndex: 'timestamp', - render: (title, record, index) => { - return ( - - ); - }, - }, - { - title: 'Row Count', - key: 'Row Count', - dataIndex: 'rowCount', - }, - { - title: 'Column Count', - key: 'Column Count', - dataIndex: 'columnCount', - }, - ]; - - const selectedProfile = (selectedProfileIndex >= 0 && profiles[selectedProfileIndex]) || undefined; - const profileModalTitle = - selectedProfile && - `Showing profile from ${new Date(selectedProfile?.timestampMillis).toLocaleDateString()} at ${new Date( - selectedProfile?.timestampMillis, - ).toLocaleTimeString()}`; - - return ( - <> - {selectedProfile && ( - - - - )} -
- - - - ); -} diff --git a/datahub-web-react/src/app/entity/dataset/profile/stats/historical/charts/StatChart.tsx b/datahub-web-react/src/app/entity/dataset/profile/stats/historical/charts/StatChart.tsx deleted file mode 100644 index 3a8866ad62d651..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/stats/historical/charts/StatChart.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { Col, Divider, Typography } from 'antd'; -import React, { useMemo } from 'react'; -import styled from 'styled-components'; - -import { ChartCard } from '@app/analyticsDashboard/components/ChartCard'; -import { ChartContainer } from '@app/analyticsDashboard/components/ChartContainer'; -import { TimeSeriesChart } from '@app/analyticsDashboard/components/TimeSeriesChart'; - -import { DateInterval, DateRange } from '@types'; - -const ChartTitle = styled(Typography.Title)` - && { - margin-bottom: 20px; - text-align: left; - width: 100%; - } -`; - -const ThinDivider = styled(Divider)` - margin: 0px; - padding: 0px; -`; - -type Point = { - timeMs: number; - value: number; -}; - -export type Props = { - title: string; - values: Array; - tickInterval: DateInterval; - dateRange: DateRange; -}; - -/** - * Change these to change the chart axis & line colors - * TODO: Add this to the theme config. - */ -const DEFAULT_LINE_COLOR = '#20d3bd'; -const DEFAULT_AXIS_COLOR = '#D8D8D8'; -const DEFAULT_AXIS_WIDTH = 2; - -/** - * Time Series Chart with a single line. - */ -export default function StatChart({ title, values, tickInterval: interval, dateRange }: Props) { - const timeSeriesData = useMemo( - () => - values - .sort((a, b) => a.timeMs - b.timeMs) - .map((value) => { - const dateStr = new Date(value.timeMs).toISOString(); - return { - x: dateStr, - y: value.value, - }; - }), - [values], - ); - - const chartData = { - title, - lines: [ - { - name: 'line_1', - data: timeSeriesData, - }, - ], - interval, - dateRange, - }; - - return ( - <> - - - - {chartData.title} - - - - - - - ); -} diff --git a/datahub-web-react/src/app/entity/dataset/profile/stats/snapshot/LatestStatsView.tsx b/datahub-web-react/src/app/entity/dataset/profile/stats/snapshot/LatestStatsView.tsx deleted file mode 100644 index 3b422ca47ce79b..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/stats/snapshot/LatestStatsView.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { Affix, Row, Typography } from 'antd'; -import React, { ReactNode } from 'react'; -import styled from 'styled-components'; - -import DataProfileView from '@app/entity/dataset/profile/stats/snapshot/SnapshotStatsView'; - -import { DatasetProfile } from '@types'; - -const HeaderRow = styled(Row)` - padding-top: 24px; - padding-bottom: 28px; - background-color: white; -`; - -export type Props = { - profile: DatasetProfile; - toggleView: ReactNode; -}; - -export default function LatestStatsView({ profile, toggleView }: Props) { - const reportedAtDate = new Date(profile.timestampMillis); - return ( - <> - - -
- Latest Stats - - Reported on {reportedAtDate.toLocaleDateString()} at {reportedAtDate.toLocaleTimeString()} - -
- {toggleView} -
-
- - - ); -} diff --git a/datahub-web-react/src/app/entity/dataset/profile/stats/snapshot/SnapshotStatsView.tsx b/datahub-web-react/src/app/entity/dataset/profile/stats/snapshot/SnapshotStatsView.tsx deleted file mode 100644 index 2b44f340117356..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/stats/snapshot/SnapshotStatsView.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import { Row, Table, Tag, Typography } from 'antd'; -import { ColumnType, ColumnsType } from 'antd/lib/table'; -import React, { useMemo } from 'react'; -import styled from 'styled-components'; - -import { Highlight } from '@app/analyticsDashboard/components/Highlight'; -import StatsSection from '@app/entity/dataset/profile/stats/StatsSection'; - -import { DatasetProfile } from '@types'; - -const ColumnStatsTable = styled(Table)` - margin-top: 24px; -`; - -const isPresent = (val: any) => { - return val !== undefined && val !== null; -}; - -const decimalToPercentStr = (decimal: number, precision: number): string => { - return `${(decimal * 100).toFixed(precision)}%`; -}; - -export type Props = { - profile: DatasetProfile; -}; - -export default function DataProfileView({ profile }: Props) { - const columnStatsTableData = useMemo( - () => - profile.fieldProfiles?.map((doc) => ({ - name: doc.fieldPath, - min: doc.min, - max: doc.max, - mean: doc.mean, - median: doc.median, - stdev: doc.stdev, - nullCount: isPresent(doc.nullCount) && doc.nullCount!.toString(), - nullPercentage: isPresent(doc.nullProportion) && decimalToPercentStr(doc.nullProportion!, 2), - distinctCount: isPresent(doc.uniqueCount) && doc.uniqueCount!.toString(), - distinctPercentage: isPresent(doc.uniqueProportion) && decimalToPercentStr(doc.uniqueProportion!, 2), - sampleValues: doc.sampleValues, - })) || [], - [profile], - ); - - /** - * Returns a placeholder value to show in the column data table when data is null. - */ - const unknownValue = () => { - return unknown; - }; - - /** - * Computes a set of the object keys across all items in a given array. - */ - const getItemKeySet = (items: Array) => { - const keySet = new Set(); - items.forEach((item) => { - Object.keys(item).forEach((key) => { - keySet.add(key); - }); - }); - return keySet; - }; - - /** - * Dynamically builds column stat table columns based on the fields present in the dataset profile data. - */ - const buildColumnStatsColumns = (tableData: Array) => { - // Optional columns. Defines how to render a column given a value exists somewhere in the profile. - const optionalColumns: ColumnsType = [ - { - title: 'Min', - dataIndex: 'min', - render: (value) => value || unknownValue(), - }, - { - title: 'Max', - dataIndex: 'max', - render: (value) => value || unknownValue(), - }, - { - title: 'Mean', - dataIndex: 'mean', - render: (value) => value || unknownValue(), - }, - { - title: 'Median', - dataIndex: 'median', - render: (value) => value || unknownValue(), - }, - { - title: 'Null Count', - dataIndex: 'nullCount', - render: (value) => value || unknownValue(), - }, - { - title: 'Null %', - dataIndex: 'nullPercentage', - render: (value) => value || unknownValue(), - }, - { - title: 'Distinct Count', - dataIndex: 'distinctCount', - render: (value) => value || unknownValue(), - }, - { - title: 'Distinct %', - dataIndex: 'distinctPercentage', - render: (value) => value || unknownValue(), - }, - { - title: 'Std. Dev', - dataIndex: 'stdev', - render: (value) => value || unknownValue(), - }, - { - title: 'Sample Values', - dataIndex: 'sampleValues', - render: (sampleValues: Array) => { - return ( - (sampleValues && - sampleValues - .slice(0, sampleValues.length < 3 ? sampleValues?.length : 3) - .map((value) => {value})) || - unknownValue() - ); - }, - }, - ]; - - // Name column always required. - const requiredColumns: ColumnsType = [ - { - title: 'Name', - dataIndex: 'name', - }, - ]; - - // Retrieves a set of names of columns that should be shown based on their presence in the data profile. - const columnsPresent: Set = getItemKeySet(tableData); - - // Compute the final columns to render. - const columns = [ - ...requiredColumns, - ...optionalColumns.filter((column: ColumnType) => columnsPresent.has(column.dataIndex as string)), - ]; - - // TODO: Support Quantiles && Distinct Value Frequencies. - return columns; - }; - - const columnStatsColumns = buildColumnStatsColumns(columnStatsTableData); - - const rowCount = (isPresent(profile?.rowCount) ? profile?.rowCount : -1) as number; - const rowCountTitle = (rowCount >= 0 && 'Rows') || 'Row Count Unknown'; - - const columnCount = (isPresent(profile?.columnCount) ? profile?.columnCount : -1) as number; - const columnCountTitle = (columnCount >= 0 && 'Columns') || 'Column Count Unknown'; - - return ( - <> - - - - - - - - record.name} - /> - - - ); -} diff --git a/datahub-web-react/src/app/entity/dataset/profile/stories/documentation.ts b/datahub-web-react/src/app/entity/dataset/profile/stories/documentation.ts deleted file mode 100644 index 345d33faddfec0..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/stories/documentation.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { EntityType } from '@types'; - -export const sampleDocs = [ - { - url: 'https://www.google.com', - description: 'This doc spans the internet web', - author: { urn: 'urn:li:corpuser:1', username: '1', type: EntityType.CorpUser }, - created: { - time: 0, - actor: 'urn:li:corpuser:1', - }, - }, -]; diff --git a/datahub-web-react/src/app/entity/dataset/profile/stories/lineageEntities.ts b/datahub-web-react/src/app/entity/dataset/profile/stories/lineageEntities.ts deleted file mode 100644 index 3583ca1a8e2dc0..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/stories/lineageEntities.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { EntityType, FabricType, PlatformNativeType } from '@types'; - -export const sampleUpstreamEntities = [ - { - name: 'Upstream HiveDataset', - type: EntityType.Dataset, - urn: 'abc', - platform: { - urn: 'urn:li:dataPlatform:hive', - name: 'Hive', - type: EntityType.DataPlatform, - }, - origin: FabricType.Prod, - description: 'this is a dataset', - platformNativeType: PlatformNativeType.Table, - tags: [], - created: { - time: 0, - }, - lastModified: { - time: 0, - }, - }, - { - name: 'Upstream KafkaDataset', - type: EntityType.Dataset, - urn: 'abc', - platform: { - urn: 'urn:li:dataPlatform:hive', - name: 'Hive', - type: EntityType.DataPlatform, - }, - origin: FabricType.Prod, - description: 'this is a dataset', - platformNativeType: PlatformNativeType.Table, - tags: [], - created: { - time: 0, - }, - lastModified: { - time: 0, - }, - }, -]; - -export const sampleDownstreamEntities = [ - { - name: 'Downstream HiveDataset', - type: EntityType.Dataset, - urn: 'abc', - platform: { - urn: 'urn:li:dataPlatform:hive', - name: 'Hive', - type: EntityType.DataPlatform, - }, - origin: FabricType.Prod, - description: 'this is a dataset', - platformNativeType: PlatformNativeType.Table, - tags: [], - created: { - time: 0, - }, - lastModified: { - time: 0, - }, - }, - { - name: 'Downstream KafkaDataset', - type: EntityType.Dataset, - urn: 'abc', - platform: { - urn: 'urn:li:dataPlatform:hive', - name: 'Hive', - type: EntityType.DataPlatform, - }, - origin: FabricType.Prod, - description: 'this is a dataset', - platformNativeType: PlatformNativeType.Table, - tags: [], - created: { - time: 0, - }, - lastModified: { - time: 0, - }, - }, -]; - -export const sampleRelationship = { - entities: sampleUpstreamEntities.map((entity) => ({ - entity, - created: { time: 0 }, - })), -}; - -export const sampleDownstreamRelationship = { - entities: sampleDownstreamEntities.map((entity) => ({ - entity, - created: { time: 0 }, - })), -}; diff --git a/datahub-web-react/src/app/entity/dataset/profile/stories/properties.ts b/datahub-web-react/src/app/entity/dataset/profile/stories/properties.ts deleted file mode 100644 index dc74984113df14..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/stories/properties.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const sampleProperties = [ - { - key: 'Number of Partitions', - value: '18', - }, - { - key: 'Cluster Name', - value: 'Testing', - }, -]; diff --git a/datahub-web-react/src/app/entity/dataset/profile/stories/sampleSchema.ts b/datahub-web-react/src/app/entity/dataset/profile/stories/sampleSchema.ts deleted file mode 100644 index 9ce31ac0469f91..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/stories/sampleSchema.ts +++ /dev/null @@ -1,367 +0,0 @@ -import { dataset3 } from '@src/Mocks'; - -import { EntityType, Schema, SchemaField, SchemaFieldDataType, SchemaMetadata } from '@types'; - -// Extending the schema type with an option for tags -export type TaggedSchemaField = { - tags: Tag[]; -} & SchemaField; - -export type Tag = { - name: string; - value?: string; - color: string; - category: string; - descriptor?: boolean; -}; - -export const sampleSchema: SchemaMetadata | Schema | null = { - name: 'MockSchema', - platformUrn: 'mock:urn', - version: 1, - hash: '', - fields: [ - { - fieldPath: 'id', - nullable: false, - description: 'order id', - type: SchemaFieldDataType.Number, - nativeDataType: 'number', - recursive: false, - }, - { - fieldPath: 'name', - nullable: true, - description: 'the name of the order', - type: SchemaFieldDataType.String, - nativeDataType: 'string', - recursive: false, - }, - { - fieldPath: 'shipping_address', - nullable: true, - description: 'the address the order ships to', - type: SchemaFieldDataType.String, - nativeDataType: 'string', - recursive: false, - }, - { - fieldPath: 'count', - nullable: true, - description: 'the number of items in the order', - type: SchemaFieldDataType.Number, - nativeDataType: 'number', - recursive: false, - }, - { - fieldPath: 'cost', - nullable: true, - description: 'the dollar value of the order', - type: SchemaFieldDataType.Number, - nativeDataType: 'number', - recursive: false, - }, - { - fieldPath: 'was_returned', - nullable: true, - description: 'if the order was sent back', - type: SchemaFieldDataType.Boolean, - nativeDataType: 'boolean', - recursive: false, - }, - { - fieldPath: 'payload', - nullable: true, - description: 'payload attached to the order', - type: SchemaFieldDataType.Bytes, - nativeDataType: 'bytes', - recursive: false, - }, - { - fieldPath: 'payment_information', - nullable: true, - description: 'struct representing the payment information', - type: SchemaFieldDataType.Struct, - nativeDataType: 'struct', - recursive: false, - }, - ], - platformSchema: { - __typename: 'TableSchema', - schema: '{ "type": "record", "name": "SampleHdfsSchema", "namespace": "com.linkedin.dataset", "doc": "Sample HDFS dataset", "fields": [ { "name": "field_foo", "type": [ "string" ] }, { "name": "field_bar", "type": [ "boolean" ] } ] }', - }, -}; - -export const sampleSchemaWithTags: Schema = { - name: 'MockSchema', - platformUrn: 'mock:urn', - version: 1, - hash: '', - fields: [ - { - fieldPath: 'id', - nullable: false, - description: 'order id', - type: SchemaFieldDataType.Number, - nativeDataType: 'number', - recursive: false, - globalTags: { - tags: [ - { - tag: { - urn: 'urn:li:tag:Legacy', - name: 'Legacy', - description: 'this is a legacy dataset', - type: EntityType.Tag, - }, - associatedUrn: 'mock:urn', - }, - ], - }, - glossaryTerms: { - terms: [ - { - term: { - type: EntityType.GlossaryTerm, - name: 'sample-glossary-term', - urn: 'urn:li:glossaryTerm:sample-glossary-term', - hierarchicalName: 'example.sample-glossary-term', - properties: { - name: 'sample-glossary-term', - description: 'sample definition', - definition: 'sample definition', - termSource: 'sample term source', - }, - }, - associatedUrn: 'mock:urn', - }, - ], - }, - }, - { - fieldPath: 'name', - nullable: true, - description: 'the name of the order', - type: SchemaFieldDataType.String, - nativeDataType: 'string', - recursive: false, - } as SchemaField, - { - fieldPath: 'shipping_address', - nullable: true, - description: 'the address the order ships to', - type: SchemaFieldDataType.String, - nativeDataType: 'string', - recursive: false, - } as SchemaField, - { - fieldPath: 'count', - nullable: true, - description: 'the number of items in the order', - type: SchemaFieldDataType.Number, - nativeDataType: 'number', - recursive: false, - }, - { - fieldPath: 'cost', - nullable: true, - description: 'the dollar value of the order', - type: SchemaFieldDataType.Number, - nativeDataType: 'number', - recursive: false, - } as SchemaField, - { - fieldPath: 'was_returned', - nullable: true, - description: 'if the order was sent back', - type: SchemaFieldDataType.Boolean, - nativeDataType: 'boolean', - recursive: false, - }, - { - fieldPath: 'payload', - nullable: true, - description: 'payload attached to the order', - type: SchemaFieldDataType.Bytes, - nativeDataType: 'bytes', - recursive: false, - }, - { - fieldPath: 'payment_information', - nullable: true, - description: 'struct representing the payment information', - type: SchemaFieldDataType.Struct, - nativeDataType: 'struct', - recursive: false, - } as SchemaField, - ], -}; - -export const sampleSchemaWithPkFk: SchemaMetadata = { - primaryKeys: ['name'], - foreignKeys: [ - { - name: 'constraint', - sourceFields: [ - { - urn: 'datasetUrn', - type: EntityType.Dataset, - parent: { urn: 'test', type: EntityType.Dataset }, - fieldPath: 'shipping_address', - }, - ], - foreignFields: [ - { - urn: dataset3.urn, - type: EntityType.Dataset, - parent: { urn: dataset3.name, type: EntityType.Dataset }, - fieldPath: 'address', - }, - ], - foreignDataset: dataset3, - }, - ], - name: 'MockSchema', - platformUrn: 'mock:urn', - version: 1, - hash: '', - fields: [ - { - fieldPath: 'id', - nullable: false, - description: 'order id', - type: SchemaFieldDataType.Number, - nativeDataType: 'number', - recursive: false, - globalTags: { - tags: [ - { - tag: { - urn: 'urn:li:tag:Legacy', - name: 'Legacy', - description: 'this is a legacy dataset', - type: EntityType.Tag, - }, - associatedUrn: 'mock:urn', - }, - ], - }, - glossaryTerms: { - terms: [ - { - term: { - type: EntityType.GlossaryTerm, - urn: 'urn:li:glossaryTerm:sample-glossary-term', - name: 'sample-glossary-term', - hierarchicalName: 'example.sample-glossary-term', - properties: { - name: 'sample-glossary-term', - description: 'sample definition', - definition: 'sample definition', - termSource: 'sample term source', - }, - }, - associatedUrn: 'mock:urn', - }, - ], - }, - }, - { - fieldPath: 'name', - nullable: true, - description: 'the name of the order', - type: SchemaFieldDataType.String, - nativeDataType: 'string', - recursive: false, - } as SchemaField, - { - fieldPath: 'shipping_address', - nullable: true, - description: 'the address the order ships to', - type: SchemaFieldDataType.String, - nativeDataType: 'string', - recursive: false, - } as SchemaField, - { - fieldPath: 'count', - nullable: true, - description: 'the number of items in the order', - type: SchemaFieldDataType.Number, - nativeDataType: 'number', - recursive: false, - }, - { - fieldPath: 'cost', - nullable: true, - description: 'the dollar value of the order', - type: SchemaFieldDataType.Number, - nativeDataType: 'number', - recursive: false, - } as SchemaField, - { - fieldPath: 'was_returned', - nullable: true, - description: 'if the order was sent back', - type: SchemaFieldDataType.Boolean, - nativeDataType: 'boolean', - recursive: false, - }, - { - fieldPath: 'payload', - nullable: true, - description: 'payload attached to the order', - type: SchemaFieldDataType.Bytes, - nativeDataType: 'bytes', - recursive: false, - }, - { - fieldPath: 'payment_information', - nullable: true, - description: 'struct representing the payment information', - type: SchemaFieldDataType.Struct, - nativeDataType: 'struct', - recursive: false, - } as SchemaField, - ], -}; - -export const sampleSchemaWithoutFields: SchemaMetadata | Schema | null = { - name: 'MockSchema', - platformUrn: 'mock:urn', - version: 1, - hash: '', - fields: [], -}; - -export const sampleSchemaWithKeyValueFields: SchemaMetadata | Schema | null = { - name: 'MockSchema', - platformUrn: 'mock:urn', - version: 1, - hash: '', - fields: [ - { - fieldPath: '[key=True].[version=2.0].id', - nullable: true, - description: 'the number of items in the order', - type: SchemaFieldDataType.Number, - nativeDataType: 'number', - recursive: false, - }, - { - fieldPath: 'count', - nullable: true, - description: 'the number of items in the order', - type: SchemaFieldDataType.Number, - nativeDataType: 'number', - recursive: false, - }, - { - fieldPath: 'cost', - nullable: true, - description: 'the dollar value of the order', - type: SchemaFieldDataType.Number, - nativeDataType: 'number', - recursive: false, - } as SchemaField, - ], -}; diff --git a/datahub-web-react/src/app/entity/dataset/profile/stories/stats.ts b/datahub-web-react/src/app/entity/dataset/profile/stories/stats.ts deleted file mode 100644 index a4afa38ec7090f..00000000000000 --- a/datahub-web-react/src/app/entity/dataset/profile/stories/stats.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { DatasetProfile } from '@types'; - -export const completeSampleProfile: DatasetProfile = { - rowCount: 1000, - columnCount: 2000, - timestampMillis: 0, - fieldProfiles: [ - { - fieldPath: 'testColumn', - uniqueCount: 1, - uniqueProportion: 0.111, - nullCount: 2, - nullProportion: 0.222, - min: '3', - max: '4', - mean: '5', - median: '6', - stdev: '7', - sampleValues: ['value1', 'value2', 'value3'], - }, - { - fieldPath: 'testColumn2', - uniqueCount: 8, - uniqueProportion: 0.333, - nullCount: 9, - nullProportion: 0.444, - min: '10', - max: '11', - mean: '12', - median: '13', - stdev: '14', - sampleValues: ['value4', 'value5', 'value6'], - }, - ], -}; - -export const missingFieldStatsProfile: DatasetProfile = { - rowCount: 1000, - columnCount: 2000, - timestampMillis: 0, -}; - -export const missingTableStatsProfile: DatasetProfile = { - timestampMillis: 0, - fieldProfiles: [ - { - fieldPath: 'testColumn', - uniqueCount: 1, - uniqueProportion: 0.111, - nullCount: 2, - nullProportion: 0.222, - min: '3', - max: '4', - mean: '5', - median: '6', - stdev: '7', - sampleValues: ['value1', 'value2', 'value3'], - }, - { - fieldPath: 'testColumn2', - uniqueCount: 8, - uniqueProportion: 0.333, - nullCount: 9, - nullProportion: 0.444, - min: '10', - max: '11', - mean: '12', - median: '13', - stdev: '14', - sampleValues: ['value4', 'value5', 'value6'], - }, - ], -}; diff --git a/datahub-web-react/src/app/entity/domain/DataProductsTab/CreateDataProductModal.tsx b/datahub-web-react/src/app/entity/domain/DataProductsTab/CreateDataProductModal.tsx deleted file mode 100644 index 97e922c65e535c..00000000000000 --- a/datahub-web-react/src/app/entity/domain/DataProductsTab/CreateDataProductModal.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { Button, Modal, message } from 'antd'; -import React, { useState } from 'react'; - -import DataProductBuilderForm from '@app/entity/domain/DataProductsTab/DataProductBuilderForm'; -import { DataProductBuilderState } from '@app/entity/domain/DataProductsTab/types'; - -import { useCreateDataProductMutation } from '@graphql/dataProduct.generated'; -import { DataProduct, Domain } from '@types'; - -export const MODAL_WIDTH = '75vw'; - -export const MODAL_BODY_STYLE = { - overflow: 'auto', - width: '80vw', - maxWidth: 800, -}; - -const DEFAULT_STATE = { - name: '', -}; - -type Props = { - domain: Domain; - onClose: () => void; - onCreateDataProduct: (dataProduct: DataProduct) => void; -}; - -export default function CreateDataProductModal({ domain, onCreateDataProduct, onClose }: Props) { - const [builderState, updateBuilderState] = useState(DEFAULT_STATE); - const [createDataProductMutation] = useCreateDataProductMutation(); - - function createDataProduct() { - createDataProductMutation({ - variables: { - input: { - domainUrn: domain.urn, - id: builderState.id, - properties: { - name: builderState.name, - description: builderState.description || undefined, - }, - }, - }, - }) - .then(({ data, errors }) => { - if (!errors) { - message.success('Created Data Product!'); - if (data?.createDataProduct) { - const updateDataProduct = { ...data.createDataProduct, domain: { domain } }; - onCreateDataProduct(updateDataProduct as DataProduct); - } - onClose(); - } - }) - .catch((error) => { - onClose(); - message.destroy(); - message.error({ content: `Failed to create Data Product: ${error.message}.` }); - }); - } - - return ( - - - - - } - > - - - ); -} diff --git a/datahub-web-react/src/app/entity/domain/DataProductsTab/DataProductAdvancedOption.tsx b/datahub-web-react/src/app/entity/domain/DataProductsTab/DataProductAdvancedOption.tsx deleted file mode 100644 index 70907084ee7934..00000000000000 --- a/datahub-web-react/src/app/entity/domain/DataProductsTab/DataProductAdvancedOption.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { Collapse, Form, Input, Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -import { DataProductBuilderFormProps } from '@app/entity/domain/DataProductsTab/types'; -import { validateCustomUrnId } from '@app/shared/textUtil'; - -const FormItem = styled(Form.Item)` - .ant-form-item-label { - padding-bottom: 2px; - } -`; - -const FormItemWithMargin = styled(FormItem)` - margin-bottom: 16px; -`; - -const FormItemNoMargin = styled(FormItem)` - margin-bottom: 0; -`; - -const AdvancedLabel = styled(Typography.Text)` - color: #373d44; -`; - -export function DataProductAdvancedOption({ builderState, updateBuilderState }: DataProductBuilderFormProps) { - function updateDataProductId(id: string) { - updateBuilderState({ - ...builderState, - id, - }); - } - - return ( - - Advanced Options} key="1"> - Data Product Id} - help="By default, a random UUID will be generated to uniquely identify this data product. If - you'd like to provide a custom id instead to more easily keep track of this data product, - you may provide it here. Be careful, you cannot easily change the data product id after - creation." - > - ({ - validator(_, value) { - if (value && validateCustomUrnId(value)) { - return Promise.resolve(); - } - return Promise.reject(new Error('Please enter a valid Data product id')); - }, - }), - ]} - > - updateDataProductId(e.target.value)} - /> - - - - - ); -} diff --git a/datahub-web-react/src/app/entity/domain/DataProductsTab/DataProductBuilderForm.tsx b/datahub-web-react/src/app/entity/domain/DataProductsTab/DataProductBuilderForm.tsx deleted file mode 100644 index 758942bf9366eb..00000000000000 --- a/datahub-web-react/src/app/entity/domain/DataProductsTab/DataProductBuilderForm.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { Form, Input, Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -import { DataProductAdvancedOption } from '@app/entity/domain/DataProductsTab/DataProductAdvancedOption'; -import { DataProductBuilderFormProps } from '@app/entity/domain/DataProductsTab/types'; -import { ANTD_GRAY } from '@app/entity/shared/constants'; -import { Editor as MarkdownEditor } from '@app/entity/shared/tabs/Documentation/components/editor/Editor'; - -const StyledEditor = styled(MarkdownEditor)` - border: 1px solid ${ANTD_GRAY[4]}; -`; - -export default function DataProductBuilderForm({ builderState, updateBuilderState }: DataProductBuilderFormProps) { - function updateName(name: string) { - updateBuilderState({ - ...builderState, - name, - }); - } - - function updateDescription(description: string) { - updateBuilderState({ - ...builderState, - description, - }); - } - - return ( -
- Name} - required - > - updateName(e.target.value)} - placeholder="Revenue Dashboards" - /> - - Description}> - - - - - ); -} diff --git a/datahub-web-react/src/app/entity/domain/DataProductsTab/DataProductResult.tsx b/datahub-web-react/src/app/entity/domain/DataProductsTab/DataProductResult.tsx deleted file mode 100644 index b0170d7b064753..00000000000000 --- a/datahub-web-react/src/app/entity/domain/DataProductsTab/DataProductResult.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import { DeleteOutlined, EditOutlined } from '@ant-design/icons'; -import { Button, Dropdown, Modal, message } from 'antd'; -import React, { useState } from 'react'; -import styled from 'styled-components'; - -import { PreviewType } from '@app/entity/Entity'; -import EditDataProductModal from '@app/entity/domain/DataProductsTab/EditDataProductModal'; -import { MenuIcon } from '@app/entity/shared/EntityDropdown/EntityDropdown'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { useDeleteDataProductMutation } from '@graphql/dataProduct.generated'; -import { DataProduct, EntityType } from '@types'; - -const ResultWrapper = styled.div` - background-color: white; - border-radius: 8px; - max-width: 1200px; - margin: 0 auto 8px auto; - padding: 8px 16px; - display: flex; - width: 100%; -`; - -const StyledButton = styled(Button)` - border: none; - box-shadow: none; - outline: none; - height: 18px; - width: 18px; - padding: 0; - - svg { - height: 14px; - width: 14px; - } -`; - -const ButtonsWrapper = styled.div` - margin-left: 16px; - display: flex; -`; - -const StyledMenuIcon = styled(MenuIcon)` - margin-left: 8px; - height: 18px; - width: 18px; -`; - -const PreviewWrapper = styled.div` - max-width: 94%; - flex: 1; -`; - -const MenuItem = styled.div``; - -interface Props { - dataProduct: DataProduct; - onUpdateDataProduct: (dataProduct: DataProduct) => void; - setDeletedDataProductUrns: React.Dispatch>; -} - -export default function DataProductResult({ dataProduct, onUpdateDataProduct, setDeletedDataProductUrns }: Props) { - const entityRegistry = useEntityRegistry(); - const [isEditModalVisible, setIsEditModalVisible] = useState(false); - const [deleteDataProductMutation] = useDeleteDataProductMutation(); - - function deleteDataProduct() { - deleteDataProductMutation({ variables: { urn: dataProduct.urn } }) - .then(() => { - message.success('Deleted Data Product'); - setDeletedDataProductUrns((currentUrns) => [...currentUrns, dataProduct.urn]); - }) - .catch(() => { - message.destroy(); - message.error({ content: 'Failed to delete Data Product. An unexpected error occurred' }); - }); - } - - function onRemove() { - Modal.confirm({ - title: `Delete ${entityRegistry.getDisplayName(EntityType.DataProduct, dataProduct)}`, - content: `Are you sure you want to delete this Data Product?`, - onOk() { - deleteDataProduct(); - }, - onCancel() {}, - okText: 'Yes', - maskClosable: true, - closable: true, - }); - } - - const items = [ - { - key: '0', - label: ( - -  Delete - - ), - }, - ]; - - return ( - <> - - - {entityRegistry.renderPreview(EntityType.DataProduct, PreviewType.SEARCH, dataProduct)} - - - } onClick={() => setIsEditModalVisible(true)} /> - - - - - - {isEditModalVisible && ( - setIsEditModalVisible(false)} - onUpdateDataProduct={onUpdateDataProduct} - /> - )} - - ); -} diff --git a/datahub-web-react/src/app/entity/domain/DataProductsTab/DataProductsTab.tsx b/datahub-web-react/src/app/entity/domain/DataProductsTab/DataProductsTab.tsx deleted file mode 100644 index 148b5642bbadc5..00000000000000 --- a/datahub-web-react/src/app/entity/domain/DataProductsTab/DataProductsTab.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import { LoadingOutlined, PlusOutlined } from '@ant-design/icons'; -import { Button, Empty, Pagination } from 'antd'; -import * as QueryString from 'query-string'; -import React, { useState } from 'react'; -import { useLocation } from 'react-router'; -import styled from 'styled-components'; - -import { DomainsPaginationContainer } from '@app/domain/DomainsList'; -import CreateDataProductModal from '@app/entity/domain/DataProductsTab/CreateDataProductModal'; -import DataProductResult from '@app/entity/domain/DataProductsTab/DataProductResult'; -import { useEntityData } from '@app/entity/shared/EntityContext'; -import TabToolbar from '@app/entity/shared/components/styled/TabToolbar'; -import { ANTD_GRAY } from '@app/entity/shared/constants'; -import { SearchBar } from '@app/search/SearchBar'; -import { DOMAINS_FILTER_NAME } from '@app/search/utils/constants'; -import { scrollToTop } from '@app/shared/searchUtils'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { useGetSearchResultsForMultipleQuery } from '@graphql/search.generated'; -import { DataProduct, Domain, EntityType } from '@types'; - -const DataProductsPaginationWrapper = styled(DomainsPaginationContainer)` - justify-content: center; -`; - -const ResultsWrapper = styled.div` - flex: 1; - background-color: ${ANTD_GRAY[2]}; - padding: 16px; - overflow: auto; -`; - -const StyledLoading = styled(LoadingOutlined)` - font-size: 32px; -`; - -const LoadingWrapper = styled.div` - display: flex; - justify-content: center; - margin-top: 25%; -`; - -const DEFAULT_PAGE_SIZE = 10; - -export default function DataProductsTab() { - const { entityData } = useEntityData(); - const entityRegistry = useEntityRegistry(); - const location = useLocation(); - const params = QueryString.parse(location.search, { arrayFormat: 'comma' }); - const paramsQuery = (params?.query as string) || undefined; - const [query, setQuery] = useState(paramsQuery); - const [page, setPage] = useState(1); - const [isCreateModalVisible, setIsCreateModalVisible] = useState(false); - const [createdDataProducts, setCreatedDataProducts] = useState([]); - const [editedDataProducts, setEditedDataProducts] = useState([]); - const [deletedDataProductUrns, setDeletedDataProductUrns] = useState([]); - - const start = (page - 1) * DEFAULT_PAGE_SIZE; - const domainUrn = entityData?.urn || ''; - - const { data, loading } = useGetSearchResultsForMultipleQuery({ - skip: !domainUrn, - variables: { - input: { - types: [EntityType.DataProduct], - query: query || '', - start, - count: DEFAULT_PAGE_SIZE, - orFilters: [{ and: [{ field: DOMAINS_FILTER_NAME, values: [domainUrn] }] }], - searchFlags: { skipCache: true }, - }, - }, - fetchPolicy: 'no-cache', - }); - const totalResults = data?.searchAcrossEntities?.total || 0; - const searchResults = data?.searchAcrossEntities?.searchResults?.map((r) => r.entity) || []; - const dataProducts = [...createdDataProducts, ...searchResults]; - const displayedDataProducts = dataProducts - .map( - (dataProduct) => - editedDataProducts.find((editedDataProduct) => editedDataProduct.urn === dataProduct.urn) || - dataProduct, - ) - .filter((dataProduct) => !deletedDataProductUrns.includes(dataProduct.urn)); - - const onChangePage = (newPage: number) => { - scrollToTop(); - setPage(newPage); - }; - - function onCreateDataProduct(dataProduct: DataProduct) { - setCreatedDataProducts([dataProduct, ...createdDataProducts]); - } - - function onUpdateDataProduct(dataProduct: DataProduct) { - setEditedDataProducts([dataProduct, ...editedDataProducts]); - } - - return ( - <> - - - null} - onQueryChange={(q) => setQuery(q && q.length > 0 ? q : undefined)} - entityRegistry={entityRegistry} - hideRecommendations - /> - - - {!loading && !displayedDataProducts.length && ( - - )} - {loading && ( - - - - )} - {!loading && - displayedDataProducts.map((dataProduct) => ( - - ))} - - - - - {isCreateModalVisible && ( - setIsCreateModalVisible(false)} - /> - )} - - ); -} diff --git a/datahub-web-react/src/app/entity/domain/DataProductsTab/EditDataProductModal.tsx b/datahub-web-react/src/app/entity/domain/DataProductsTab/EditDataProductModal.tsx deleted file mode 100644 index 57f21a8bcd0724..00000000000000 --- a/datahub-web-react/src/app/entity/domain/DataProductsTab/EditDataProductModal.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { Button, Modal, message } from 'antd'; -import React, { useState } from 'react'; - -import { MODAL_BODY_STYLE, MODAL_WIDTH } from '@app/entity/domain/DataProductsTab/CreateDataProductModal'; -import DataProductBuilderForm from '@app/entity/domain/DataProductsTab/DataProductBuilderForm'; -import { DataProductBuilderState } from '@app/entity/domain/DataProductsTab/types'; - -import { useUpdateDataProductMutation } from '@graphql/dataProduct.generated'; -import { DataProduct } from '@types'; - -type Props = { - dataProduct: DataProduct; - onClose: () => void; - onUpdateDataProduct: (dataProduct: DataProduct) => void; -}; - -export default function EditDataProductModal({ dataProduct, onUpdateDataProduct, onClose }: Props) { - const [builderState, updateBuilderState] = useState({ - name: dataProduct.properties?.name || '', - description: dataProduct.properties?.description || '', - }); - const [updateDataProductMutation] = useUpdateDataProductMutation(); - - function updateDataProduct() { - updateDataProductMutation({ - variables: { - urn: dataProduct.urn, - input: { - name: builderState.name, - description: builderState.description || undefined, - }, - }, - }) - .then(({ data, errors }) => { - if (!errors) { - message.success('Updates Data Product!'); - if (data?.updateDataProduct) { - onUpdateDataProduct(data.updateDataProduct as DataProduct); - } - onClose(); - } - }) - .catch(() => { - onClose(); - message.destroy(); - message.error({ content: 'Failed to update Data Product. An unexpected error occurred' }); - }); - } - - return ( - - - - - } - > - - - ); -} diff --git a/datahub-web-react/src/app/entity/domain/DataProductsTab/types.ts b/datahub-web-react/src/app/entity/domain/DataProductsTab/types.ts deleted file mode 100644 index 2015b97f1433b7..00000000000000 --- a/datahub-web-react/src/app/entity/domain/DataProductsTab/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -export type DataProductBuilderState = { - name: string; - id?: string; - description?: string; -}; - -export type DataProductBuilderFormProps = { - builderState: DataProductBuilderState; - updateBuilderState: (newState: DataProductBuilderState) => void; -}; diff --git a/datahub-web-react/src/app/entity/domain/DomainEntitiesTab.tsx b/datahub-web-react/src/app/entity/domain/DomainEntitiesTab.tsx deleted file mode 100644 index 213f407d4e6d5f..00000000000000 --- a/datahub-web-react/src/app/entity/domain/DomainEntitiesTab.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; - -import { useEntityData } from '@app/entity/shared/EntityContext'; -import { EmbeddedListSearchSection } from '@app/entity/shared/components/styled/search/EmbeddedListSearchSection'; -import { UnionType } from '@app/search/utils/constants'; - -import { EntityType } from '@types'; - -export const DomainEntitiesTab = () => { - const { urn, entityType } = useEntityData(); - - let fixedFilter; - // Set a fixed filter corresponding to the current entity urn. - if (entityType === EntityType.Domain) { - fixedFilter = { - field: 'domains', - values: [urn], - }; - } - - return ( - - ); -}; diff --git a/datahub-web-react/src/app/entity/domain/DomainEntity.tsx b/datahub-web-react/src/app/entity/domain/DomainEntity.tsx deleted file mode 100644 index b20b9f45e0c225..00000000000000 --- a/datahub-web-react/src/app/entity/domain/DomainEntity.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import * as React from 'react'; - -import DomainIcon from '@app/domain/DomainIcon'; -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import DataProductsTab from '@app/entity/domain/DataProductsTab/DataProductsTab'; -import { DomainEntitiesTab } from '@app/entity/domain/DomainEntitiesTab'; -import { Preview } from '@app/entity/domain/preview/Preview'; -import { EntityMenuItems } from '@app/entity/shared/EntityDropdown/EntityDropdown'; -import { EntityProfileTab } from '@app/entity/shared/constants'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import SidebarStructuredPropsSection from '@app/entity/shared/containers/profile/sidebar/StructuredProperties/SidebarStructuredPropsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { EntityActionItem } from '@app/entity/shared/entity/EntityActions'; -import { DocumentationTab } from '@app/entity/shared/tabs/Documentation/DocumentationTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; - -import { useGetDomainQuery } from '@graphql/domain.generated'; -import { Domain, EntityType, SearchResult } from '@types'; - -/** - * Definition of the DataHub Domain entity. - */ -export class DomainEntity implements Entity { - type: EntityType = EntityType.Domain; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - if (styleType === IconStyleType.SVG) { - return ( - - ); - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => false; - - isLineageEnabled = () => false; - - getAutoCompleteFieldName = () => 'name'; - - getGraphName = () => 'domain'; - - getPathName = () => 'domain'; - - getEntityName = () => 'Domain'; - - getCollectionName = () => 'Domains'; - - useEntityQuery = useGetDomainQuery; - - renderProfile = (urn: string) => ( - - ); - - getSidebarSections = () => [ - { - component: SidebarAboutSection, - }, - { - component: SidebarOwnerSection, - }, - { - component: SidebarStructuredPropsSection, - }, - ]; - - renderPreview = (_: PreviewType, data: Domain) => { - return ( - - ); - }; - - renderSearch = (result: SearchResult) => { - const data = result.entity as Domain; - return ( - - ); - }; - - displayName = (data: Domain) => { - return data?.properties?.name || data?.id || data.urn; - }; - - getOverridePropertiesFromEntity = (data: Domain) => { - return { - name: data.properties?.name, - }; - }; - - getGenericEntityProperties = (data: Domain) => { - return getDataForEntityType({ - data, - entityType: this.type, - getOverrideProperties: this.getOverridePropertiesFromEntity, - }); - }; - - supportedCapabilities = () => { - // TODO.. Determine whether SOFT_DELETE should go into here. - return new Set([EntityCapabilityType.OWNERS]); - }; -} diff --git a/datahub-web-react/src/app/entity/domain/preview/DomainEntitiesSnippet.tsx b/datahub-web-react/src/app/entity/domain/preview/DomainEntitiesSnippet.tsx deleted file mode 100644 index 2d46e05db3743b..00000000000000 --- a/datahub-web-react/src/app/entity/domain/preview/DomainEntitiesSnippet.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { DatabaseOutlined, FileDoneOutlined } from '@ant-design/icons'; -import { VerticalDivider } from '@remirror/react'; -import React from 'react'; -import styled from 'styled-components'; - -import DomainIcon from '@app/domain/DomainIcon'; -import { ANTD_GRAY_V2 } from '@app/entity/shared/constants'; -import { pluralize } from '@app/shared/textUtil'; -import { useHoverEntityTooltipContext } from '@src/app/recommendations/HoverEntityTooltipContext'; - -import { SearchResultFields_Domain_Fragment } from '@graphql/search.generated'; - -const Wrapper = styled.div` - color: ${ANTD_GRAY_V2[8]}; - font-size: 12px; - display: flex; - align-items: center; - line-height: 20px; - - svg { - margin-right: 4px; - } -`; - -const StyledDivider = styled(VerticalDivider)` - &&& { - margin: 0 8px; - } -`; - -interface Props { - domain: SearchResultFields_Domain_Fragment; -} - -export default function DomainEntitiesSnippet({ domain }: Props) { - const { entityCount } = useHoverEntityTooltipContext(); - const subDomainCount = domain.children?.total || 0; - const dataProductCount = domain.dataProducts?.total || 0; - - return ( - - {!!entityCount && ( - <> - {entityCount} {entityCount === 1 ? 'entity' : 'entities'} - - - )} - {subDomainCount} {pluralize(subDomainCount, 'sub-domain')} - - {dataProductCount} {pluralize(dataProductCount, 'data product')} - - ); -} diff --git a/datahub-web-react/src/app/entity/domain/preview/Preview.tsx b/datahub-web-react/src/app/entity/domain/preview/Preview.tsx deleted file mode 100644 index 01d6e7c90c8de8..00000000000000 --- a/datahub-web-react/src/app/entity/domain/preview/Preview.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; - -import DomainIcon from '@app/domain/DomainIcon'; -import DomainEntitiesSnippet from '@app/entity/domain/preview/DomainEntitiesSnippet'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { Domain, EntityType, Owner, SearchInsight } from '@types'; - -export const Preview = ({ - domain, - urn, - name, - description, - owners, - insights, - logoComponent, -}: { - domain: Domain; - urn: string; - name: string; - description?: string | null; - owners?: Array | null; - insights?: Array | null; - logoComponent?: JSX.Element; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - return ( - - } - owners={owners} - insights={insights} - logoComponent={logoComponent} - parentEntities={domain.parentDomains?.domains} - snippet={} - /> - ); -}; diff --git a/datahub-web-react/src/app/entity/ermodelrelationships/ERModelRelationshipEntity.tsx b/datahub-web-react/src/app/entity/ermodelrelationships/ERModelRelationshipEntity.tsx deleted file mode 100644 index d2217acebc57be..00000000000000 --- a/datahub-web-react/src/app/entity/ermodelrelationships/ERModelRelationshipEntity.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import '@app/entity/ermodelrelationships/preview/ERModelRelationshipAction.less'; - -import { DatabaseFilled, DatabaseOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { ERModelRelationshipPreviewCard } from '@app/entity/ermodelrelationships/preview/ERModelRelationshipPreviewCard'; -import ERModelRelationshipSidebarCardinality from '@app/entity/ermodelrelationships/profile/sidebar/ERModelRelationshipSidebarCardinality'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import { SidebarTagsSection } from '@app/entity/shared/containers/profile/sidebar/SidebarTagsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { DocumentationTab } from '@app/entity/shared/tabs/Documentation/DocumentationTab'; -import { ERModelRelationshipTab } from '@app/entity/shared/tabs/ERModelRelationship/ERModelRelationshipTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; -import { GenericEntityProperties } from '@app/entity/shared/types'; - -import { - useGetErModelRelationshipQuery, - useUpdateErModelRelationshipMutation, -} from '@graphql/ermodelrelationship.generated'; -import { EntityType, ErModelRelationship, OwnershipType, SearchResult } from '@types'; - -import ermodelrelationshipIcon from '@images/ermodelrelationshipIcon.svg'; - -/** - * Definition of the DataHub ErModelRelationship entity. - */ - -export class ERModelRelationshipEntity implements Entity { - type: EntityType = EntityType.ErModelRelationship; - - icon = (fontSize: number, styleType: IconStyleType) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - if (styleType === IconStyleType.SVG) { - return ( - - ); - } - - return ; - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => false; - - isLineageEnabled = () => false; - - getAutoCompleteFieldName = () => 'name'; - - getPathName = () => 'erModelRelationship'; - - getCollectionName = () => 'ER Model Relationships'; - - getEntityName = () => 'ER-Model-Relationship'; - - getGraphName = () => 'erModelRelationship'; - - renderProfile = (urn: string) => ( - - ); - - getOverridePropertiesFromEntity = (_ermodelrelation?: ErModelRelationship | null): GenericEntityProperties => { - return {}; - }; - - renderPreview = (_: PreviewType, data: ErModelRelationship) => { - return ( - <> - - {data.properties?.name || data.editableProperties?.name || ''} - - } - description={data?.editableProperties?.description || ''} - owners={data.ownership?.owners} - glossaryTerms={data?.glossaryTerms || undefined} - globalTags={data?.tags} - cardinality={data?.properties?.cardinality} - /> - - ); - }; - - renderSearch = (result: SearchResult) => { - return this.renderPreview(PreviewType.SEARCH, result.entity as ErModelRelationship); - }; - - displayName = (data: ErModelRelationship) => { - return data.properties?.name || data.editableProperties?.name || data.urn; - }; - - getGenericEntityProperties = (data: ErModelRelationship) => { - return getDataForEntityType({ - data, - entityType: this.type, - getOverrideProperties: this.getOverridePropertiesFromEntity, - }); - }; - - supportedCapabilities = () => { - return new Set([]); - }; -} diff --git a/datahub-web-react/src/app/entity/ermodelrelationships/_tests_/ERModelRelationshipSidebarCardinality.test.tsx b/datahub-web-react/src/app/entity/ermodelrelationships/_tests_/ERModelRelationshipSidebarCardinality.test.tsx deleted file mode 100644 index 0d04e625d32601..00000000000000 --- a/datahub-web-react/src/app/entity/ermodelrelationships/_tests_/ERModelRelationshipSidebarCardinality.test.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client'; -import { render, screen } from '@testing-library/react'; -import React from 'react'; -import { vi } from 'vitest'; - -import ERModelRelationshipSidebarCardinality from '@app/entity/ermodelrelationships/profile/sidebar/ERModelRelationshipSidebarCardinality'; -import { EntityContext } from '@app/entity/shared/EntityContext'; -import { GenericEntityProperties } from '@app/entity/shared/types'; -import { Dataset, EntityType, ErModelRelationshipCardinality } from '@src/types.generated'; -import TestPageContainer from '@utils/test-utils/TestPageContainer'; - -// Create a mock Apollo Client -const mockApolloClient = new ApolloClient({ - cache: new InMemoryCache(), - uri: 'https://mock-api.com/graphql', // Mock URI -}); - -const mockEntityData: GenericEntityProperties = { - urn: 'urn:li:ermodelrelationship:123', - properties: { - cardinality: ErModelRelationshipCardinality.OneN, - destination: { - urn: 'urn:li:dataset:test-dataset-destination', - name: 'Test Dataset Destination', - } as Dataset, - source: { - urn: 'urn:li:dataset:test-dataset-source', - name: 'Test Dataset Source', - } as Dataset, - name: 'Test Relationship', - }, - type: EntityType.ErModelRelationship, -} as GenericEntityProperties; - -describe('ERModelRelationshipSidebarCardinality', () => { - it('renders the sidebar header', () => { - render( - - - - - - - , - ); - - expect(screen.getByText('Cardinality')).toBeInTheDocument(); - expect(screen.getByText(ErModelRelationshipCardinality.OneN)).toBeInTheDocument(); - }); -}); diff --git a/datahub-web-react/src/app/entity/ermodelrelationships/preview/ERModelRelationshipPreviewCard.tsx b/datahub-web-react/src/app/entity/ermodelrelationships/preview/ERModelRelationshipPreviewCard.tsx deleted file mode 100644 index deecd613e5fe0d..00000000000000 --- a/datahub-web-react/src/app/entity/ermodelrelationships/preview/ERModelRelationshipPreviewCard.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { Card, Collapse } from 'antd'; -import React from 'react'; - -import { IconStyleType } from '@app/entity/Entity'; -import ERModelSidebarPreviewCard from '@app/preview/ERModelSidebarPreviewCard'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { EntityType, ErModelRelationshipCardinality, GlobalTags, GlossaryTerms, Owner } from '@types'; - -import ermodelrelationshipIcon from '@images/ermodelrelationshipIcon.svg'; - -const { Panel } = Collapse; - -export const ERModelRelationshipPreviewCard = ({ - urn, - name, - owners, - description, - globalTags, - glossaryTerms, - cardinality, -}: { - urn: string; - name: string | any; - description: string | any; - globalTags?: GlobalTags | null; - glossaryTerms?: GlossaryTerms | null; - owners?: Array | null; - cardinality?: ErModelRelationshipCardinality | null; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - const getERModelRelationHeader = (): JSX.Element => { - return ( -
- - } - tags={globalTags || undefined} - glossaryTerms={glossaryTerms || undefined} - owners={owners} - type={entityRegistry.getEntityName(EntityType.ErModelRelationship)} - typeIcon={entityRegistry.getIcon(EntityType.ErModelRelationship, 14, IconStyleType.ACCENT)} - titleSizePx={18} - cardinality={cardinality} - /> -
- ); - }; - - return ( - <> - - - - - ); -}; diff --git a/datahub-web-react/src/app/entity/ermodelrelationships/profile/sidebar/ERModelRelationshipSidebarCardinality.tsx b/datahub-web-react/src/app/entity/ermodelrelationships/profile/sidebar/ERModelRelationshipSidebarCardinality.tsx deleted file mode 100644 index 183a1b7e9a8321..00000000000000 --- a/datahub-web-react/src/app/entity/ermodelrelationships/profile/sidebar/ERModelRelationshipSidebarCardinality.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; -import styled from 'styled-components/macro'; - -import { useEntityData } from '@app/entity/shared/EntityContext'; -import { SidebarHeader } from '@app/entity/shared/containers/profile/sidebar/SidebarHeader'; -import { ErModelRelationship } from '@src/types.generated'; - -const CardinalitySidebar = styled.div` - color: #000000; - font-weight: 400; - display: flex; - flex-flow: column nowrap; -`; - -const ERModelRelationshipCardinality = styled.span` - display: flex; - align-items: center; - gap: 10px; -`; - -const ERModelRelationshipSidebarCardinality = () => { - const { entityData } = useEntityData(); - return ( - - - - <>{(entityData as ErModelRelationship)?.properties?.cardinality} - - - ); -}; - -export default ERModelRelationshipSidebarCardinality; diff --git a/datahub-web-react/src/app/entity/glossaryNode/ChildrenTab.tsx b/datahub-web-react/src/app/entity/glossaryNode/ChildrenTab.tsx deleted file mode 100644 index 816d5558acc9d3..00000000000000 --- a/datahub-web-react/src/app/entity/glossaryNode/ChildrenTab.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React from 'react'; -import styled from 'styled-components'; - -import { sortGlossaryNodes } from '@app/entity/glossaryNode/utils'; -import { sortGlossaryTerms } from '@app/entity/glossaryTerm/utils'; -import { useEntityData } from '@app/entity/shared/EntityContext'; -import useGlossaryChildren from '@app/entityV2/glossaryNode/useGlossaryChildren'; -import EmptyGlossarySection from '@app/glossary/EmptyGlossarySection'; -import GlossaryEntitiesList from '@app/glossary/GlossaryEntitiesList'; -import { useEntityRegistry } from '@app/useEntityRegistry'; -import Loading from '@src/app/shared/Loading'; - -import { EntityType, GlossaryNode, GlossaryTerm } from '@types'; - -const ChildrenTabWrapper = styled.div` - height: 100%; - overflow: auto; - padding-bottom: 10px; -`; - -const LoadingWrapper = styled.div` - height: 100px; - display: flex; - align-items: center; - justify-content: center; -`; - -function ChildrenTab() { - const { entityData } = useEntityData(); - const entityRegistry = useEntityRegistry(); - const entityUrn = entityData?.urn; - const { scrollRef, data, loading } = useGlossaryChildren({ entityUrn }); - - if (!entityData) return <>; - - const childNodes = data - .filter((child) => child.type === EntityType.GlossaryNode) - .sort((nodeA, nodeB) => sortGlossaryNodes(entityRegistry, nodeA, nodeB)); - const childTerms = data - .filter((child) => child.type === EntityType.GlossaryTerm) - .sort((termA, termB) => sortGlossaryTerms(entityRegistry, termA, termB)); - - const hasTermsOrNodes = !!childNodes?.length || !!childTerms?.length; - - if (hasTermsOrNodes) { - return ( - - - {loading && ( - - - - )} -
- - ); - } - - return ; -} - -export default ChildrenTab; diff --git a/datahub-web-react/src/app/entity/glossaryNode/GlossaryNodeEntity.tsx b/datahub-web-react/src/app/entity/glossaryNode/GlossaryNodeEntity.tsx deleted file mode 100644 index b2e3a3dca0b5ec..00000000000000 --- a/datahub-web-react/src/app/entity/glossaryNode/GlossaryNodeEntity.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import { FolderFilled, FolderOutlined } from '@ant-design/icons'; -import React from 'react'; - -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import ChildrenTab from '@app/entity/glossaryNode/ChildrenTab'; -import { Preview } from '@app/entity/glossaryNode/preview/Preview'; -import { EntityMenuItems } from '@app/entity/shared/EntityDropdown/EntityDropdown'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import SidebarStructuredPropsSection from '@app/entity/shared/containers/profile/sidebar/StructuredProperties/SidebarStructuredPropsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { DocumentationTab } from '@app/entity/shared/tabs/Documentation/DocumentationTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; -import SummaryTab from '@app/entityV2/summary/SummaryTab'; -import { useShowAssetSummaryPage } from '@app/entityV2/summary/useShowAssetSummaryPage'; - -import { useGetGlossaryNodeQuery } from '@graphql/glossaryNode.generated'; -import { EntityType, GlossaryNode, SearchResult } from '@types'; - -class GlossaryNodeEntity implements Entity { - type: EntityType = EntityType.GlossaryNode; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => false; - - getAutoCompleteFieldName = () => 'name'; - - isLineageEnabled = () => false; - - getPathName = () => 'glossaryNode'; - - getCollectionName = () => 'Term Groups'; - - getEntityName = () => 'Term Group'; - - useEntityQuery = useGetGlossaryNodeQuery; - - renderProfile = (urn: string) => { - return ( - - ); - }; - - getSidebarSections = () => [ - { - component: SidebarAboutSection, - properties: { - hideLinksButton: true, - }, - }, - { - component: SidebarOwnerSection, - }, - { - component: SidebarStructuredPropsSection, - }, - ]; - - getProfileTabs = () => { - const showSummaryTab = useShowAssetSummaryPage(); - - return [ - ...(showSummaryTab - ? [ - { - name: 'Summary', - component: SummaryTab, - }, - ] - : []), - { - name: 'Contents', - component: ChildrenTab, - }, - ...(!showSummaryTab - ? [ - { - name: 'Documentation', - component: DocumentationTab, - properties: { - hideLinksButton: true, - }, - }, - ] - : []), - { - name: 'Properties', - component: PropertiesTab, - }, - ]; - }; - - displayName = (data: GlossaryNode) => { - return data.properties?.name || data.urn; - }; - - getOverridePropertiesFromEntity = (data: GlossaryNode) => { - return { - name: this.displayName(data), - }; - }; - - renderSearch = (result: SearchResult) => { - return this.renderPreview(PreviewType.SEARCH, result.entity as GlossaryNode); - }; - - renderPreview = (_: PreviewType, data: GlossaryNode) => { - return ( - - ); - }; - - platformLogoUrl = (_: GlossaryNode) => { - return undefined; - }; - - getGenericEntityProperties = (glossaryNode: GlossaryNode) => { - return getDataForEntityType({ - data: glossaryNode, - entityType: this.type, - getOverrideProperties: (data) => data, - }); - }; - - supportedCapabilities = () => { - return new Set([ - EntityCapabilityType.OWNERS, - EntityCapabilityType.DEPRECATION, - EntityCapabilityType.SOFT_DELETE, - ]); - }; - - getGraphName = () => this.getPathName(); -} - -export default GlossaryNodeEntity; diff --git a/datahub-web-react/src/app/entity/glossaryNode/preview/Preview.tsx b/datahub-web-react/src/app/entity/glossaryNode/preview/Preview.tsx deleted file mode 100644 index 90fd8bc6e8270d..00000000000000 --- a/datahub-web-react/src/app/entity/glossaryNode/preview/Preview.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { FolderOutlined } from '@ant-design/icons'; -import React from 'react'; - -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { EntityType, Owner, ParentNodesResult } from '@types'; - -export const Preview = ({ - urn, - name, - description, - owners, - parentNodes, -}: { - urn: string; - name: string; - description?: string | null; - owners?: Array | null; - parentNodes?: ParentNodesResult | null; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - return ( - } - type={entityRegistry.getEntityName(EntityType.GlossaryNode)} - parentEntities={parentNodes?.nodes} - /> - ); -}; diff --git a/datahub-web-react/src/app/entity/glossaryTerm/GlossaryTermEntity.tsx b/datahub-web-react/src/app/entity/glossaryTerm/GlossaryTermEntity.tsx deleted file mode 100644 index d33836b26ddb5f..00000000000000 --- a/datahub-web-react/src/app/entity/glossaryTerm/GlossaryTermEntity.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import { BookFilled, BookOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { Preview } from '@app/entity/glossaryTerm/preview/Preview'; -import GlossaryRelatedEntity from '@app/entity/glossaryTerm/profile/GlossaryRelatedEntity'; -import GlossayRelatedTerms from '@app/entity/glossaryTerm/profile/GlossaryRelatedTerms'; -import { EntityMenuItems } from '@app/entity/shared/EntityDropdown/EntityDropdown'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import { SidebarDomainSection } from '@app/entity/shared/containers/profile/sidebar/Domain/SidebarDomainSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import SidebarStructuredPropsSection from '@app/entity/shared/containers/profile/sidebar/StructuredProperties/SidebarStructuredPropsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { EntityActionItem } from '@app/entity/shared/entity/EntityActions'; -import { SchemaTab } from '@app/entity/shared/tabs/Dataset/Schema/SchemaTab'; -import { DocumentationTab } from '@app/entity/shared/tabs/Documentation/DocumentationTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; -import { GenericEntityProperties } from '@app/entity/shared/types'; -import { PageRoutes } from '@conf/Global'; - -import { GetGlossaryTermQuery, useGetGlossaryTermQuery } from '@graphql/glossaryTerm.generated'; -import { EntityType, GlossaryTerm, SearchResult } from '@types'; - -/** - * Definition of the DataHub Dataset entity. - */ -export class GlossaryTermEntity implements Entity { - type: EntityType = EntityType.GlossaryTerm; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => true; - - getAutoCompleteFieldName = () => 'name'; - - isLineageEnabled = () => false; - - getPathName = () => 'glossaryTerm'; - - getCollectionName = () => 'Glossary Terms'; - - getEntityName = () => 'Glossary Term'; - - useEntityQuery = useGetGlossaryTermQuery; - - getCustomCardUrlPath = () => PageRoutes.GLOSSARY; - - renderProfile = (urn) => { - return ( - - glossaryTerm?.glossaryTerm?.schemaMetadata !== null, - enabled: (_, glossaryTerm: GetGlossaryTermQuery) => - glossaryTerm?.glossaryTerm?.schemaMetadata !== null, - }, - }, - { - name: 'Related Terms', - component: GlossayRelatedTerms, - }, - { - name: 'Properties', - component: PropertiesTab, - }, - ]} - sidebarSections={this.getSidebarSections()} - getOverrideProperties={this.getOverridePropertiesFromEntity} - /> - ); - }; - - getSidebarSections = () => [ - { - component: SidebarAboutSection, - }, - { - component: SidebarOwnerSection, - }, - { - component: SidebarDomainSection, - properties: { - hideOwnerType: true, - }, - }, - { - component: SidebarStructuredPropsSection, - }, - ]; - - getOverridePropertiesFromEntity = (glossaryTerm?: GlossaryTerm | null): GenericEntityProperties => { - // if dataset has subTypes filled out, pick the most specific subtype and return it - return { - customProperties: glossaryTerm?.properties?.customProperties, - }; - }; - - renderSearch = (result: SearchResult) => { - return this.renderPreview(PreviewType.SEARCH, result.entity as GlossaryTerm); - }; - - renderPreview = (previewType: PreviewType, data: GlossaryTerm) => { - return ( - - ); - }; - - displayName = (data: GlossaryTerm) => { - return data.properties?.name || data.name || data.urn; - }; - - platformLogoUrl = (_: GlossaryTerm) => { - return undefined; - }; - - getGenericEntityProperties = (glossaryTerm: GlossaryTerm) => { - return getDataForEntityType({ - data: glossaryTerm, - entityType: this.type, - getOverrideProperties: (data) => data, - }); - }; - - supportedCapabilities = () => { - return new Set([ - EntityCapabilityType.OWNERS, - EntityCapabilityType.DEPRECATION, - EntityCapabilityType.SOFT_DELETE, - ]); - }; - - getGraphName = () => this.getPathName(); -} diff --git a/datahub-web-react/src/app/entity/glossaryTerm/preview/Preview.tsx b/datahub-web-react/src/app/entity/glossaryTerm/preview/Preview.tsx deleted file mode 100644 index 7d8577153d329b..00000000000000 --- a/datahub-web-react/src/app/entity/glossaryTerm/preview/Preview.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { BookOutlined } from '@ant-design/icons'; -import React from 'react'; - -import { IconStyleType, PreviewType } from '@app/entity/Entity'; -import { getRelatedEntitiesUrl } from '@app/entity/glossaryTerm/utils'; -import UrlButton from '@app/entity/shared/UrlButton'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { Deprecation, Domain, EntityType, Owner, ParentNodesResult } from '@types'; - -export const Preview = ({ - urn, - name, - description, - owners, - deprecation, - parentNodes, - previewType, - domain, -}: { - urn: string; - name: string; - description?: string | null; - owners?: Array | null; - deprecation?: Deprecation | null; - parentNodes?: ParentNodesResult | null; - previewType: PreviewType; - domain?: Domain | undefined; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - return ( - } - type="Glossary Term" - typeIcon={entityRegistry.getIcon(EntityType.GlossaryTerm, 14, IconStyleType.ACCENT)} - deprecation={deprecation} - parentEntities={parentNodes?.nodes} - domain={domain} - entityTitleSuffix={ - View Related Entities - } - /> - ); -}; diff --git a/datahub-web-react/src/app/entity/glossaryTerm/preview/__tests__/Preview.test.tsx b/datahub-web-react/src/app/entity/glossaryTerm/preview/__tests__/Preview.test.tsx deleted file mode 100644 index 02b9508c3b02e0..00000000000000 --- a/datahub-web-react/src/app/entity/glossaryTerm/preview/__tests__/Preview.test.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { MockedProvider } from '@apollo/client/testing'; -import { render } from '@testing-library/react'; -import React from 'react'; - -import { PreviewType } from '@app/entity/Entity'; -import { Preview } from '@app/entity/glossaryTerm/preview/Preview'; -import { mocks } from '@src/Mocks'; -import TestPageContainer from '@utils/test-utils/TestPageContainer'; - -describe('Preview', () => { - it('renders', () => { - const { getByText } = render( - - - - - , - ); - expect(getByText('definition')).toBeInTheDocument(); - }); -}); diff --git a/datahub-web-react/src/app/entity/glossaryTerm/profile/AddRelatedTermsModal.tsx b/datahub-web-react/src/app/entity/glossaryTerm/profile/AddRelatedTermsModal.tsx deleted file mode 100644 index 19a18060cf457a..00000000000000 --- a/datahub-web-react/src/app/entity/glossaryTerm/profile/AddRelatedTermsModal.tsx +++ /dev/null @@ -1,221 +0,0 @@ -import { Button, Modal, Select, Tag, message } from 'antd'; -import React, { useState } from 'react'; -import styled from 'styled-components/macro'; - -import { useEntityData, useRefetch } from '@app/entity/shared/EntityContext'; -import GlossaryBrowser from '@app/glossary/GlossaryBrowser/GlossaryBrowser'; -import ParentEntities from '@app/search/filters/ParentEntities'; -import { getParentEntities } from '@app/search/filters/utils'; -import ClickOutside from '@app/shared/ClickOutside'; -import TermLabel from '@app/shared/TermLabel'; -import { BrowserWrapper } from '@app/shared/tags/AddTagsTermsModal'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { useAddRelatedTermsMutation } from '@graphql/glossaryTerm.generated'; -import { useGetSearchResultsLazyQuery } from '@graphql/search.generated'; -import { EntityType, SearchResult, TermRelationshipType } from '@types'; - -const StyledSelect = styled(Select)` - width: 480px; -`; - -const SearchResultContainer = styled.div` - display: flex; - flex-direction: column; - justify-content: center; -`; - -interface Props { - onClose: () => void; - relationshipType: TermRelationshipType; -} - -function AddRelatedTermsModal(props: Props) { - const { onClose, relationshipType } = props; - - const [inputValue, setInputValue] = useState(''); - const [selectedUrns, setSelectedUrns] = useState([]); - const [selectedTerms, setSelectedTerms] = useState([]); - const [isFocusedOnInput, setIsFocusedOnInput] = useState(false); - const entityRegistry = useEntityRegistry(); - const { urn: entityDataUrn } = useEntityData(); - const refetch = useRefetch(); - - const [AddRelatedTerms] = useAddRelatedTermsMutation(); - - function addTerms() { - AddRelatedTerms({ - variables: { - input: { - urn: entityDataUrn, - termUrns: selectedUrns, - relationshipType, - }, - }, - }) - .catch((e) => { - message.destroy(); - message.error({ content: `Failed to move: \n ${e.message || ''}`, duration: 3 }); - }) - .finally(() => { - message.loading({ content: 'Adding...', duration: 2 }); - setTimeout(() => { - message.success({ - content: 'Added Related Terms!', - duration: 2, - }); - refetch(); - }, 2000); - }); - onClose(); - } - - const [termSearch, { data: termSearchData }] = useGetSearchResultsLazyQuery(); - const termSearchResults = termSearchData?.search?.searchResults || []; - - const tagSearchOptions = termSearchResults.map((result: SearchResult) => { - const displayName = entityRegistry.getDisplayName(result.entity.type, result.entity); - - return ( - - - - - - - ); - }); - - const handleSearch = (text: string) => { - if (text.length > 0) { - termSearch({ - variables: { - input: { - type: EntityType.GlossaryTerm, - query: text, - start: 0, - count: 20, - }, - }, - }); - } - }; - - // When a Tag or term search result is selected, add the urn to the Urns - const onSelectValue = (urn: string) => { - const newUrns = [...selectedUrns, urn]; - setSelectedUrns(newUrns); - const selectedSearchOption = tagSearchOptions.find((option) => option.props.value === urn); - setSelectedTerms([ - ...selectedTerms, - { urn, component: }, - ]); - }; - - // When a Tag or term search result is deselected, remove the urn from the Owners - const onDeselectValue = (urn: string) => { - const newUrns = selectedUrns.filter((u) => u !== urn); - setSelectedUrns(newUrns); - setInputValue(''); - setIsFocusedOnInput(true); - setSelectedTerms(selectedTerms.filter((term) => term.urn !== urn)); - }; - - function selectTermFromBrowser(urn: string, displayName: string) { - setIsFocusedOnInput(false); - const newUrns = [...selectedUrns, urn]; - setSelectedUrns(newUrns); - setSelectedTerms([...selectedTerms, { urn, component: }]); - } - - function clearInput() { - setInputValue(''); - setTimeout(() => setIsFocusedOnInput(true), 0); // call after click outside - } - - function handleBlur() { - setInputValue(''); - } - - const tagRender = (properties) => { - // eslint-disable-next-line react/prop-types - const { closable, onClose: close, value } = properties; - const onPreventMouseDown = (event) => { - event.preventDefault(); - event.stopPropagation(); - }; - const selectedItem = selectedTerms.find((term) => term.urn === value).component; - - return ( - - {selectedItem} - - ); - }; - - const isShowingGlossaryBrowser = !inputValue && isFocusedOnInput; - - return ( - - - - - } - > - setIsFocusedOnInput(false)}> - onSelectValue(asset)} - onDeselect={(asset: any) => onDeselectValue(asset)} - onSearch={(value: string) => { - // eslint-disable-next-line react/prop-types - handleSearch(value.trim()); - // eslint-disable-next-line react/prop-types - setInputValue(value.trim()); - }} - tagRender={tagRender} - value={selectedUrns} - onClear={clearInput} - onFocus={() => setIsFocusedOnInput(true)} - onBlur={handleBlur} - dropdownStyle={isShowingGlossaryBrowser || !inputValue ? { display: 'none' } : {}} - > - {tagSearchOptions} - - - - - - - ); -} - -export default AddRelatedTermsModal; diff --git a/datahub-web-react/src/app/entity/glossaryTerm/profile/GlossaryRelatedEntity.tsx b/datahub-web-react/src/app/entity/glossaryTerm/profile/GlossaryRelatedEntity.tsx deleted file mode 100644 index 2dd49439b90d1a..00000000000000 --- a/datahub-web-react/src/app/entity/glossaryTerm/profile/GlossaryRelatedEntity.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import * as React from 'react'; - -import { useEntityData } from '@app/entity/shared/EntityContext'; -import { EmbeddedListSearchSection } from '@app/entity/shared/components/styled/search/EmbeddedListSearchSection'; -import { UnionType } from '@app/search/utils/constants'; - -export default function GlossaryRelatedEntity() { - const { entityData } = useEntityData(); - - const entityUrn = entityData?.urn; - - const fixedOrFilters = - (entityUrn && [ - { - field: 'glossaryTerms', - values: [entityUrn], - }, - { - field: 'fieldGlossaryTerms', - values: [entityUrn], - }, - ]) || - []; - - entityData?.isAChildren?.relationships?.forEach((term) => { - const childUrn = term.entity?.urn; - - if (childUrn) { - fixedOrFilters.push({ - field: 'glossaryTerms', - values: [childUrn], - }); - - fixedOrFilters.push({ - field: 'fieldGlossaryTerms', - values: [childUrn], - }); - } - }); - - return ( - - ); -} diff --git a/datahub-web-react/src/app/entity/glossaryTerm/profile/GlossaryRelatedTerms.tsx b/datahub-web-react/src/app/entity/glossaryTerm/profile/GlossaryRelatedTerms.tsx deleted file mode 100644 index 2ab9531ffc6849..00000000000000 --- a/datahub-web-react/src/app/entity/glossaryTerm/profile/GlossaryRelatedTerms.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { Menu } from 'antd'; -import React, { useEffect, useState } from 'react'; -import styled from 'styled-components/macro'; - -import GlossaryRelatedTermsResult, { - RelatedTermTypes, -} from '@app/entity/glossaryTerm/profile/GlossaryRelatedTermsResult'; -import { useEntityData } from '@app/entity/shared/EntityContext'; - -const DetailWrapper = styled.div` - display: inline-flex; - flex: 1; - width: 100%; -`; - -const MenuWrapper = styled.div` - border-right: 2px solid #f5f5f5; -`; - -const Content = styled.div` - flex-grow: 1; - max-width: 100%; - overflow: hidden; -`; - -export default function GlossayRelatedTerms() { - const { entityData } = useEntityData(); - const [selectedKey, setSelectedKey] = useState(''); - const menuOptionsArray = Object.keys(RelatedTermTypes); - - useEffect(() => { - if (menuOptionsArray && menuOptionsArray.length > 0 && selectedKey.length === 0) { - setSelectedKey(menuOptionsArray[0]); - } - }, [menuOptionsArray, selectedKey]); - - const onMenuClick = ({ key }) => { - setSelectedKey(key); - }; - - return ( - - - { - onMenuClick(key); - }} - > - {menuOptionsArray.map((option) => ( - - {RelatedTermTypes[option]} - - ))} - - - - {selectedKey && entityData && ( - - )} - - - ); -} diff --git a/datahub-web-react/src/app/entity/glossaryTerm/profile/GlossaryRelatedTermsResult.tsx b/datahub-web-react/src/app/entity/glossaryTerm/profile/GlossaryRelatedTermsResult.tsx deleted file mode 100644 index 52a08e44bb56bc..00000000000000 --- a/datahub-web-react/src/app/entity/glossaryTerm/profile/GlossaryRelatedTermsResult.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { PlusOutlined } from '@ant-design/icons'; -import { Typography } from 'antd'; -import React, { useState } from 'react'; -import styled from 'styled-components/macro'; - -import AddRelatedTermsModal from '@app/entity/glossaryTerm/profile/AddRelatedTermsModal'; -import RelatedTerm from '@app/entity/glossaryTerm/profile/RelatedTerm'; -import { EmptyTab } from '@app/entity/shared/components/styled/EmptyTab'; -import { ANTD_GRAY } from '@app/entity/shared/constants'; -import { Message } from '@app/shared/Message'; -import { Button } from '@src/alchemy-components'; - -import { TermRelationshipType } from '@types'; - -export enum RelatedTermTypes { - hasRelatedTerms = 'Contains', - isRelatedTerms = 'Inherits', - containedBy = 'Contained by', - isAChildren = 'Inherited by', -} - -export type Props = { - glossaryRelatedTermType: string; - glossaryRelatedTermResult: Array; -}; - -const ListContainer = styled.div` - width: 100%; -`; - -const TitleContainer = styled.div` - align-items: center; - border-bottom: solid 1px ${ANTD_GRAY[4]}; - display: flex; - justify-content: space-between; - padding: 15px 20px; - margin-bottom: 30px; -`; - -const messageStyle = { marginTop: '10%' }; - -export default function GlossaryRelatedTermsResult({ glossaryRelatedTermType, glossaryRelatedTermResult }: Props) { - const [isShowingAddModal, setIsShowingAddModal] = useState(false); - const glossaryRelatedTermUrns: Array = []; - glossaryRelatedTermResult.forEach((item: any) => { - glossaryRelatedTermUrns.push(item?.entity?.urn); - }); - const contentLoading = false; - const relationshipType = - glossaryRelatedTermType === RelatedTermTypes.hasRelatedTerms || - glossaryRelatedTermType === RelatedTermTypes.containedBy - ? TermRelationshipType.HasA - : TermRelationshipType.IsA; - const canEditRelatedTerms = - glossaryRelatedTermType === RelatedTermTypes.isRelatedTerms || - glossaryRelatedTermType === RelatedTermTypes.hasRelatedTerms; - - return ( - <> - {contentLoading ? ( - - ) : ( - - - - {glossaryRelatedTermType} - - {canEditRelatedTerms && ( - - )} - - {glossaryRelatedTermUrns.map((urn) => ( - - ))} - {glossaryRelatedTermUrns.length === 0 && ( - - )} - - )} - {isShowingAddModal && ( - setIsShowingAddModal(false)} relationshipType={relationshipType} /> - )} - - ); -} diff --git a/datahub-web-react/src/app/entity/glossaryTerm/profile/GlossarySidebarAboutSection.tsx b/datahub-web-react/src/app/entity/glossaryTerm/profile/GlossarySidebarAboutSection.tsx deleted file mode 100644 index 0565cbf95b70ef..00000000000000 --- a/datahub-web-react/src/app/entity/glossaryTerm/profile/GlossarySidebarAboutSection.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -import { useEntityData, useRouteToTab } from '@app/entity/shared/EntityContext'; -import StripMarkdownText from '@app/entity/shared/components/styled/StripMarkdownText'; -import { SidebarHeader } from '@app/entity/shared/containers/profile/sidebar/SidebarHeader'; - -const DescriptionTypography = styled(Typography.Paragraph)` - max-width: 65ch; -`; - -export default function GlossarySidebarAboutSection() { - const { entityData }: any = useEntityData(); - const description = entityData?.glossaryTermInfo?.definition; - const source = entityData?.glossaryTermInfo?.sourceRef; - const sourceUrl = entityData?.glossaryTermInfo?.sourceUrl; - const routeToTab = useRouteToTab(); - - return ( -
- - {description && ( - - routeToTab({ tabName: 'Documentation' })}> - Read More - - } - > - {description} - - - )} - - - {source && ( - - {sourceUrl ? ( - - {source} - - ) : ( - { - source, - } - )} - - )} -
- ); -} diff --git a/datahub-web-react/src/app/entity/glossaryTerm/profile/GlossaryTermHeader.tsx b/datahub-web-react/src/app/entity/glossaryTerm/profile/GlossaryTermHeader.tsx deleted file mode 100644 index 104ae35f356436..00000000000000 --- a/datahub-web-react/src/app/entity/glossaryTerm/profile/GlossaryTermHeader.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { Divider, Space, Typography } from 'antd'; -import React from 'react'; - -import { AvatarsGroup } from '@app/shared/avatar'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -type Props = { - definition: string; - sourceRef: string; - sourceUrl: string; - ownership?: any; -}; -export default function GlossaryTermHeader({ definition, sourceRef, sourceUrl, ownership }: Props) { - const entityRegistry = useEntityRegistry(); - return ( - <> - - {definition} - }> - Source - {sourceRef} - {sourceUrl && ( - - view source - - )} - - - - - ); -} diff --git a/datahub-web-react/src/app/entity/glossaryTerm/profile/RelatedTerm.tsx b/datahub-web-react/src/app/entity/glossaryTerm/profile/RelatedTerm.tsx deleted file mode 100644 index 51f82ffae5964d..00000000000000 --- a/datahub-web-react/src/app/entity/glossaryTerm/profile/RelatedTerm.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { DeleteOutlined, MoreOutlined } from '@ant-design/icons'; -import { Divider, Dropdown } from 'antd'; -import React from 'react'; -import styled from 'styled-components/macro'; - -import { PreviewType } from '@app/entity/Entity'; -import useRemoveRelatedTerms from '@app/entity/glossaryTerm/profile/useRemoveRelatedTerms'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { useGetGlossaryTermQuery } from '@graphql/glossaryTerm.generated'; -import { EntityType, TermRelationshipType } from '@types'; - -const ListItem = styled.div` - margin: 0 20px; -`; - -const Profile = styled.div` - display: felx; - marging-bottom: 20px; -`; - -const MenuIcon = styled(MoreOutlined)` - display: flex; - justify-content: center; - align-items: center; - font-size: 20px; - height: 32px; - margin-left: -10px; -`; - -const MenuItem = styled.div` - font-size: 12px; - padding: 0 4px; - color: #262626; -`; - -interface Props { - urn: string; - relationshipType: TermRelationshipType; - isEditable: boolean; -} - -function RelatedTerm(props: Props) { - const { urn, relationshipType, isEditable } = props; - - const entityRegistry = useEntityRegistry(); - const { data, loading } = useGetGlossaryTermQuery({ variables: { urn } }); - let displayName = ''; - if (data) { - displayName = entityRegistry.getDisplayName(EntityType.GlossaryTerm, data.glossaryTerm); - } - const { onRemove } = useRemoveRelatedTerms(urn, relationshipType, displayName); - - if (loading) return null; - - const items = [ - { - key: '0', - label: ( - -   Remove Term - - ), - }, - ]; - - return ( - - - {entityRegistry.renderPreview(EntityType.GlossaryTerm, PreviewType.PREVIEW, data?.glossaryTerm)} - {isEditable && ( - - - - )} - - - - ); -} - -export default RelatedTerm; diff --git a/datahub-web-react/src/app/entity/glossaryTerm/profile/SchemaView.tsx b/datahub-web-react/src/app/entity/glossaryTerm/profile/SchemaView.tsx deleted file mode 100644 index e004c9d1c597a3..00000000000000 --- a/datahub-web-react/src/app/entity/glossaryTerm/profile/SchemaView.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Empty, Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -export type Props = { - rawSchema: string | null; -}; - -const Content = styled.div` - margin-left: 32px; - flex-grow: 1; -`; - -export default function SchemaView({ rawSchema }: Props) { - return ( - <> - {rawSchema && rawSchema.length > 0 ? ( - -
-                        {rawSchema}
-                    
-
- ) : ( - - - - )} - - ); -} diff --git a/datahub-web-react/src/app/entity/glossaryTerm/profile/__tests__/GlossaryRelatedTerms.test.tsx b/datahub-web-react/src/app/entity/glossaryTerm/profile/__tests__/GlossaryRelatedTerms.test.tsx deleted file mode 100644 index 86b6b19c75c1c0..00000000000000 --- a/datahub-web-react/src/app/entity/glossaryTerm/profile/__tests__/GlossaryRelatedTerms.test.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { MockedProvider } from '@apollo/client/testing'; -import { render } from '@testing-library/react'; -import React from 'react'; - -import GlossaryRelatedTerms from '@app/entity/glossaryTerm/profile/GlossaryRelatedTerms'; -import { mocks } from '@src/Mocks'; -import TestPageContainer from '@utils/test-utils/TestPageContainer'; - -describe('Glossary Related Terms', () => { - it('renders and print hasRelatedTerms detail by default', async () => { - const { getByText } = render( - - - - - , - ); - expect(getByText('Contains')).toBeInTheDocument(); - expect(getByText('Inherits')).toBeInTheDocument(); - }); -}); diff --git a/datahub-web-react/src/app/entity/glossaryTerm/profile/__tests__/GlossaryTermHeader.test.tsx b/datahub-web-react/src/app/entity/glossaryTerm/profile/__tests__/GlossaryTermHeader.test.tsx deleted file mode 100644 index 79560ab5de0487..00000000000000 --- a/datahub-web-react/src/app/entity/glossaryTerm/profile/__tests__/GlossaryTermHeader.test.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { MockedProvider } from '@apollo/client/testing'; -import { render } from '@testing-library/react'; -import React from 'react'; - -import GlossaryTermHeader from '@app/entity/glossaryTerm/profile/GlossaryTermHeader'; -import { mocks } from '@src/Mocks'; -import TestPageContainer from '@utils/test-utils/TestPageContainer'; - -const glossaryTermHeaderData = { - definition: 'this is sample definition', - sourceUrl: 'sourceUrl', - sourceRef: 'Source ref', - fqdn: 'fqdn', -}; - -describe('Glossary Term Header', () => { - it('renders', () => { - const { getByText } = render( - - - - - , - ); - expect(getByText(glossaryTermHeaderData.definition)).toBeInTheDocument(); - }); -}); diff --git a/datahub-web-react/src/app/entity/glossaryTerm/profile/useRemoveRelatedTerms.tsx b/datahub-web-react/src/app/entity/glossaryTerm/profile/useRemoveRelatedTerms.tsx deleted file mode 100644 index 6d425ef32b4001..00000000000000 --- a/datahub-web-react/src/app/entity/glossaryTerm/profile/useRemoveRelatedTerms.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { Modal, message } from 'antd'; - -import { useEntityData, useRefetch } from '@app/entity/shared/EntityContext'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { useRemoveRelatedTermsMutation } from '@graphql/glossaryTerm.generated'; -import { TermRelationshipType } from '@types'; - -function useRemoveRelatedTerms(termUrn: string, relationshipType: TermRelationshipType, displayName: string) { - const { urn, entityType } = useEntityData(); - const entityRegistry = useEntityRegistry(); - const refetch = useRefetch(); - - const [removeRelatedTerms] = useRemoveRelatedTermsMutation(); - - function handleRemoveRelatedTerms() { - removeRelatedTerms({ - variables: { - input: { - urn, - termUrns: [termUrn], - relationshipType, - }, - }, - }) - .catch((e) => { - message.destroy(); - message.error({ content: `Failed to remove: \n ${e.message || ''}`, duration: 3 }); - }) - .finally(() => { - message.loading({ - content: 'Removing...', - duration: 2, - }); - setTimeout(() => { - refetch(); - message.success({ - content: `Removed Glossary Term!`, - duration: 2, - }); - }, 2000); - }); - } - - function onRemove() { - Modal.confirm({ - title: `Remove ${displayName}`, - content: `Are you sure you want to remove this ${entityRegistry.getEntityName(entityType)}?`, - onOk() { - handleRemoveRelatedTerms(); - }, - onCancel() {}, - okText: 'Yes', - maskClosable: true, - closable: true, - }); - } - - return { onRemove }; -} - -export default useRemoveRelatedTerms; diff --git a/datahub-web-react/src/app/entity/glossaryTerm/utils.ts b/datahub-web-react/src/app/entity/glossaryTerm/utils.ts index 654126961f5fc6..1c2e0a884b49b3 100644 --- a/datahub-web-react/src/app/entity/glossaryTerm/utils.ts +++ b/datahub-web-react/src/app/entity/glossaryTerm/utils.ts @@ -7,7 +7,3 @@ export function sortGlossaryTerms(entityRegistry: EntityRegistry, nodeA?: Entity const nodeBName = entityRegistry.getDisplayName(EntityType.GlossaryTerm, nodeB) || ''; return nodeAName.localeCompare(nodeBName); } - -export function getRelatedEntitiesUrl(entityRegistry: EntityRegistry, urn: string) { - return `${entityRegistry.getEntityUrl(EntityType.GlossaryTerm, urn)}/${encodeURIComponent('Related Entities')}`; -} diff --git a/datahub-web-react/src/app/entity/group/EditGroupDescriptionModal.tsx b/datahub-web-react/src/app/entity/group/EditGroupDescriptionModal.tsx deleted file mode 100644 index e477588c373c32..00000000000000 --- a/datahub-web-react/src/app/entity/group/EditGroupDescriptionModal.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { Button, Form, Modal } from 'antd'; -import React, { useState } from 'react'; -import styled from 'styled-components'; - -import { ANTD_GRAY } from '@app/entity/shared/constants'; -import { Editor } from '@app/entity/shared/tabs/Documentation/components/editor/Editor'; - -type Props = { - onClose: () => void; - onSaveAboutMe: () => void; - setStagedDescription: (des: string) => void; - stagedDescription: string | undefined; -}; -const StyledEditor = styled(Editor)` - border: 1px solid ${ANTD_GRAY[4]}; -`; - -export default function EditGroupDescriptionModal({ - onClose, - onSaveAboutMe, - setStagedDescription, - stagedDescription, -}: Props) { - const [form] = Form.useForm(); - const [aboutText, setAboutText] = useState(stagedDescription); - - function updateDescription(description: string) { - setAboutText(aboutText); - setStagedDescription(description); - } - - const saveDescription = () => { - onSaveAboutMe(); - onClose(); - }; - - return ( - - - - - } - > -
- -
- -
-
- -
- ); -} diff --git a/datahub-web-react/src/app/entity/group/Group.tsx b/datahub-web-react/src/app/entity/group/Group.tsx deleted file mode 100644 index 26bc5fcc885a01..00000000000000 --- a/datahub-web-react/src/app/entity/group/Group.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { TeamOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, IconStyleType, PreviewType } from '@app/entity/Entity'; -import GroupProfile from '@app/entity/group/GroupProfile'; -import { Preview } from '@app/entity/group/preview/Preview'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; - -import { CorpGroup, EntityType, SearchResult } from '@types'; - -/** - * Definition of the DataHub CorpGroup entity. - */ -export class GroupEntity implements Entity { - type: EntityType = EntityType.CorpGroup; - - // TODO: update icons for UserGroup - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => false; - - isLineageEnabled = () => false; - - getAutoCompleteFieldName = () => 'name'; - - getGraphName: () => string = () => 'corpGroup'; - - getPathName: () => string = () => 'group'; - - getEntityName = () => 'Group'; - - getCollectionName: () => string = () => 'Groups'; - - renderProfile: (urn: string) => JSX.Element = (_) => ; - - renderPreview = (_: PreviewType, data: CorpGroup) => ( - - ); - - renderSearch = (result: SearchResult) => { - return this.renderPreview(PreviewType.SEARCH, result.entity as CorpGroup); - }; - - displayName = (data: CorpGroup) => { - return data.properties?.displayName || data.info?.displayName || data.name || data.urn; - }; - - getGenericEntityProperties = (group: CorpGroup) => { - return getDataForEntityType({ data: group, entityType: this.type, getOverrideProperties: (data) => data }); - }; - - supportedCapabilities = () => { - return new Set([]); - }; -} diff --git a/datahub-web-react/src/app/entity/group/GroupAssets.tsx b/datahub-web-react/src/app/entity/group/GroupAssets.tsx deleted file mode 100644 index 0951cd2a05f56d..00000000000000 --- a/datahub-web-react/src/app/entity/group/GroupAssets.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import styled from 'styled-components'; - -import { EmbeddedListSearchSection } from '@app/entity/shared/components/styled/search/EmbeddedListSearchSection'; -import { UnionType } from '@app/search/utils/constants'; - -const GroupAssetsWrapper = styled.div` - height: calc(100vh - 114px); -`; - -type Props = { - urn: string; -}; - -export const GroupAssets = ({ urn }: Props) => { - return ( - - - - ); -}; diff --git a/datahub-web-react/src/app/entity/group/GroupEditModal.tsx b/datahub-web-react/src/app/entity/group/GroupEditModal.tsx deleted file mode 100644 index b74c565e017b4b..00000000000000 --- a/datahub-web-react/src/app/entity/group/GroupEditModal.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import { Button, Form, Input, Modal, Typography, message } from 'antd'; -import React, { useEffect, useState } from 'react'; - -import { useEnterKeyListener } from '@app/shared/useEnterKeyListener'; - -import { useUpdateCorpGroupPropertiesMutation } from '@graphql/group.generated'; - -type PropsData = { - email: string | undefined; - slack: string | undefined; - urn: string | undefined; - photoUrl: string | undefined; -}; - -type Props = { - open: boolean; - onClose: () => void; - onSave: () => void; - editModalData: PropsData; -}; -/** Regex Validations */ -export const USER_NAME_REGEX = new RegExp('^[a-zA-Z ]*$'); - -export default function GroupEditModal({ open, onClose, onSave, editModalData }: Props) { - const [updateCorpGroupPropertiesMutation] = useUpdateCorpGroupPropertiesMutation(); - const [form] = Form.useForm(); - - const [saveButtonEnabled, setSaveButtonEnabled] = useState(true); - const [data, setData] = useState({ - slack: editModalData.slack, - email: editModalData.email, - urn: editModalData.urn, - photoUrl: editModalData.photoUrl, - }); - - useEffect(() => { - setData({ ...editModalData }); - }, [editModalData]); - - // save changes function - const onSaveChanges = () => { - updateCorpGroupPropertiesMutation({ - variables: { - urn: editModalData?.urn || '', - input: { - email: data.email, - slack: data.slack, - pictureLink: data.photoUrl, - }, - }, - }) - .then(() => { - message.success({ - content: `Changes saved.`, - duration: 3, - }); - onSave(); // call the refetch function once save - // clear the values from edit profile form - setData({ - email: '', - slack: '', - urn: '', - photoUrl: '', - }); - }) - .catch((e) => { - message.destroy(); - message.error({ content: `Failed to Save changes!: \n ${e.message || ''}`, duration: 3 }); - }); - onClose(); - }; - - // Handle the Enter press - useEnterKeyListener({ - querySelectorToExecuteClick: '#editGroupButton', - }); - - return ( - - - - - } - > -
- setSaveButtonEnabled(form.getFieldsError().some((field) => field.errors.length > 0)) - } - > - Email} - rules={[ - { - type: 'email', - message: 'Please enter valid email', - }, - { whitespace: true }, - { min: 2, max: 50 }, - ]} - hasFeedback - > - setData({ ...data, email: event.target.value })} - /> - - Slack Channel} - rules={[{ whitespace: true }, { min: 2, max: 50 }]} - hasFeedback - > - setData({ ...data, slack: event.target.value })} - /> - - - Image URL} - rules={[{ whitespace: true }, { type: 'url', message: 'not valid url' }]} - hasFeedback - > - setData({ ...data, photoUrl: event.target.value })} - /> - - -
- ); -} diff --git a/datahub-web-react/src/app/entity/group/GroupInfoSideBar.tsx b/datahub-web-react/src/app/entity/group/GroupInfoSideBar.tsx deleted file mode 100644 index b174ef4da216d6..00000000000000 --- a/datahub-web-react/src/app/entity/group/GroupInfoSideBar.tsx +++ /dev/null @@ -1,381 +0,0 @@ -import { EditOutlined, LockOutlined, MailOutlined, SlackOutlined } from '@ant-design/icons'; -import { Button, Col, Divider, Row, Space, Tooltip, Typography, message } from 'antd'; -import React, { useEffect, useState } from 'react'; -import { useHistory, useRouteMatch } from 'react-router-dom'; -import styled from 'styled-components'; - -import { useUserContext } from '@app/context/useUserContext'; -import EditGroupDescriptionModal from '@app/entity/group/EditGroupDescriptionModal'; -import GroupEditModal from '@app/entity/group/GroupEditModal'; -import GroupMembersSideBarSection from '@app/entity/group/GroupMembersSideBarSection'; -import GroupOwnerSideBarSection from '@app/entity/group/GroupOwnerSideBarSection'; -import { - AboutSection, - EditButton, - EmptyValue, - GroupsSection, - SideBar, - SideBarSubSection, - SocialDetails, -} from '@app/entity/shared/SidebarStyledComponents'; -import StripMarkdownText, { removeMarkdown } from '@app/entity/shared/components/styled/StripMarkdownText'; -import { REDESIGN_COLORS } from '@app/entity/shared/constants'; -import { Editor } from '@app/entity/shared/tabs/Documentation/components/editor/Editor'; -import { useBrowserTitle } from '@app/shared/BrowserTabTitleContext'; -import CustomAvatar from '@app/shared/avatar/CustomAvatar'; - -import { useUpdateCorpGroupPropertiesMutation } from '@graphql/group.generated'; -import { useUpdateNameMutation } from '@graphql/mutations.generated'; -import { EntityRelationshipsResult, Ownership } from '@types'; - -type SideBarData = { - photoUrl: string | undefined; - avatarName: string | undefined; - name: string | undefined; - email: string | undefined; - slack: string | undefined; - aboutText: string | undefined; - groupMemberRelationships: EntityRelationshipsResult; - groupOwnerShip: Ownership; - isExternalGroup: boolean; - externalGroupType: string | undefined; - urn: string; -}; - -type Props = { - sideBarData: SideBarData; - refetch: () => Promise; -}; - -const AVATAR_STYLE = { margin: '3px 5px 3px 0px' }; - -const TITLES = { - about: 'About', - members: 'Members ', - editGroup: 'Edit Group', -}; - -const GroupNameHeader = styled(Row)` - font-size: 20px; - line-height: 28px; - color: #262626; - margin: 16px 16px 8px 8px; - display: flex; - align-items: center; - justify-content: center; - min-height: 100px; -`; - -const GroupTitle = styled(Typography.Title)` - max-width: 260px; - word-wrap: break-word; - width: 140px; - - &&& { - margin-bottom: 0; - word-break: break-all; - margin-left: 10px; - } - - .ant-typography-edit { - font-size: 16px; - margin-left: 10px; - } -`; - -const EditIcon = styled(EditOutlined)` - cursor: pointer; - color: ${REDESIGN_COLORS.BLUE}; -`; -const AddNewDescription = styled(Button)` - display: none; - margin: -4px; - width: 140px; -`; - -const StyledViewer = styled(Editor)` - padding-right: 8px; - display: block; - - .remirror-editor.ProseMirror { - padding: 0; - } -`; - -const DescriptionContainer = styled.div` - position: relative; - display: flex; - flex-direction: column; - width: 100%; - text-align:left; - font-weight: normal; - font - min-height: 22px; - - &:hover ${AddNewDescription} { - display: block; - } - & ins.diff { - background-color: #b7eb8f99; - text-decoration: none; - &:hover { - background-color: #b7eb8faa; - } - } - & del.diff { - background-color: #ffa39e99; - text-decoration: line-through; - &: hover { - background-color: #ffa39eaa; - } - } -`; - -const ExpandedActions = styled.div` - height: 10px; -`; -const ReadLessText = styled(Typography.Link)` - margin-right: 4px; -`; - -/** - * Responsible for reading & writing users. - */ -export default function GroupInfoSidebar({ sideBarData, refetch }: Props) { - const { - avatarName, - name, - aboutText, - groupMemberRelationships, - email, - photoUrl, - slack, - urn, - isExternalGroup, - externalGroupType, - groupOwnerShip: ownership, - } = sideBarData; - const [updateCorpGroupPropertiesMutation] = useUpdateCorpGroupPropertiesMutation(); - const { url } = useRouteMatch(); - const history = useHistory(); - - const { updateTitle } = useBrowserTitle(); - - useEffect(() => { - // You can use the title and updateTitle function here - // For example, updating the title when the component mounts - if (name) { - updateTitle(`Group | ${name}`); - } - // // Don't forget to clean up the title when the component unmounts - return () => { - if (name) { - // added to condition for rerendering issue - updateTitle(''); - } - }; - }, [name, updateTitle]); - - /* eslint-disable @typescript-eslint/no-unused-vars */ - const [editGroupModal, showEditGroupModal] = useState(false); - const me = useUserContext(); - const canEditGroup = me?.platformPrivileges?.manageIdentities; - const [groupTitle, setGroupTitle] = useState(name); - const [expanded, setExpanded] = useState(false); - const [isUpdatingDescription, SetIsUpdatingDescription] = useState(false); - const [stagedDescription, setStagedDescription] = useState(aboutText); - - const [updateName] = useUpdateNameMutation(); - const overLimit = removeMarkdown(aboutText || '').length > 80; - const ABBREVIATED_LIMIT = 80; - - useEffect(() => { - setStagedDescription(aboutText); - }, [aboutText]); - - useEffect(() => { - setGroupTitle(groupTitle); - }, [groupTitle]); - - // Update Group Title - // eslint-disable-next-line @typescript-eslint/no-shadow - const handleTitleUpdate = async (name: string) => { - setGroupTitle(name); - await updateName({ variables: { input: { name, urn } } }) - .then(() => { - message.success({ content: 'Name Updated', duration: 2 }); - refetch(); - }) - .catch((e: unknown) => { - message.destroy(); - if (e instanceof Error) { - message.error({ content: `Failed to update name: \n ${e.message || ''}`, duration: 3 }); - } - }); - }; - - const getEditModalData = { - urn, - email, - slack, - photoUrl, - }; - - // About Text save - const onSaveAboutMe = () => { - updateCorpGroupPropertiesMutation({ - variables: { - urn: urn || '', - input: { - description: stagedDescription, - }, - }, - }) - .then(() => { - message.success({ - content: `Changes saved.`, - duration: 3, - }); - refetch(); - }) - .catch((e) => { - message.destroy(); - message.error({ content: `Failed to Save changes!: \n ${e.message || ''}`, duration: 3 }); - }); - }; - return ( - <> - - - -
- - - - - {groupTitle} - - - - {isExternalGroup && ( - - - - )} - - - - - - - {email || } - - - - - - {slack || } - - - - - - {TITLES.about} - - SetIsUpdatingDescription(true)} data-testid="edit-icon" /> - - - - - {(aboutText && expanded) || !overLimit ? ( - <> - {/* Read only viewer for displaying group description */} - - - {overLimit && ( - { - setExpanded(false); - }} - > - Read Less - - )} - - - ) : ( - <> - {/* Display abbreviated description with option to read more */} - - { - setExpanded(true); - }} - > - Read More - - - } - shouldWrap - > - {aboutText} - - - )} - - {/* Modal for updating group description */} - {isUpdatingDescription && ( - { - SetIsUpdatingDescription(false); - setStagedDescription(aboutText); - }} - onSaveAboutMe={onSaveAboutMe} - setStagedDescription={setStagedDescription} - stagedDescription={stagedDescription} - /> - )} - - - - - - - history.replace(`${url}/members`)} - /> - - - {canEditGroup && ( - - - - )} - - {/* Modal */} - showEditGroupModal(false)} - onSave={() => { - refetch(); - }} - editModalData={getEditModalData} - /> - - ); -} diff --git a/datahub-web-react/src/app/entity/group/GroupMembers.tsx b/datahub-web-react/src/app/entity/group/GroupMembers.tsx deleted file mode 100644 index 956c57b154386b..00000000000000 --- a/datahub-web-react/src/app/entity/group/GroupMembers.tsx +++ /dev/null @@ -1,245 +0,0 @@ -import { MoreOutlined, UserAddOutlined, UserDeleteOutlined } from '@ant-design/icons'; -import { Button, Col, Dropdown, Empty, MenuProps, Modal, Pagination, Row, Typography, message } from 'antd'; -import React, { useState } from 'react'; -import { Link } from 'react-router-dom'; -import styled from 'styled-components'; - -import { AddGroupMembersModal } from '@app/entity/group/AddGroupMembersModal'; -import { CustomAvatar } from '@app/shared/avatar'; -import { scrollToTop } from '@app/shared/searchUtils'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { useGetAllGroupMembersQuery, useRemoveGroupMembersMutation } from '@graphql/group.generated'; -import { CorpUser, EntityType } from '@types'; - -const ADD_MEMBER_STYLE = { - backGround: '#ffffff', - boxShadow: '0px 2px 6px rgba(0, 0, 0, 0.05)', -}; -const AVATAR_STYLE = { margin: '5px 5px 5px 0' }; - -/** - * Styled Components - */ -const AddMember = styled(Button)` - padding: 13px 13px 30px 30px; - cursor: pointer; - - &&& .anticon.anticon-user-add { - margin-right: 6px; - } -`; - -const AddMemberText = styled(Typography.Text)` - font-family: Manrope; - font-style: normal; - font-weight: 500; - font-size: 12px; - line-height: 20px; -`; - -const MemberNameSection = styled.div` - font-size: 20px; - line-height: 28px; - color: #262626; - display: flex; - align-items: center; - justify-content: start; - padding-left: 12px; -`; - -const GroupMemberWrapper = styled.div` - height: calc(100vh - 217px); - overflow-y: auto; - - & .groupMemberRow { - margin: 0 19px; - } -`; - -const MemberColumn = styled(Col)` - padding: 19px 0 19px 0; - border-bottom: 1px solid #f0f0f0; -`; - -const MemberEditIcon = styled.div` - font-size: 22px; - float: right; -`; - -const Name = styled.span` - font-weight: bold; - font-size: 14px; - line-height: 22px; - color: #262626; - margin-left: 8px; -`; - -const NoGroupMembers = styled(Empty)` - padding: 40px; -`; - -type Props = { - urn: string; - pageSize: number; - isExternalGroup: boolean; - onChangeMembers?: () => void; -}; - -export default function GroupMembers({ urn, pageSize, isExternalGroup, onChangeMembers }: Props) { - const entityRegistry = useEntityRegistry(); - - const [page, setPage] = useState(1); - /* eslint-disable @typescript-eslint/no-unused-vars */ - const [isEditingMembers, setIsEditingMembers] = useState(false); - const start = (page - 1) * pageSize; - const { data: membersData, refetch } = useGetAllGroupMembersQuery({ - variables: { urn, start, count: pageSize }, - }); - const [removeGroupMembersMutation] = useRemoveGroupMembersMutation(); - - const onChangeMembersPage = (newPage: number) => { - scrollToTop(); - setPage(newPage); - }; - - const removeGroupMember = (userUrn: string) => { - removeGroupMembersMutation({ - variables: { - groupUrn: urn, - userUrns: [userUrn], - }, - }) - .then(({ errors }) => { - if (!errors) { - message.success({ content: 'Removed Group Member!', duration: 2 }); - onChangeMembers?.(); - // Hack to deal with eventual consistency - setTimeout(() => { - // Reload the page. - refetch(); - }, 2000); - } - }) - .catch((e) => { - message.destroy(); - message.error({ content: `Failed to remove group member: \n ${e.message || ''}`, duration: 3 }); - }); - }; - - const onClickEditMembers = () => { - setIsEditingMembers(true); - }; - - const onAddMembers = () => { - onChangeMembers?.(); - setTimeout(() => { - // Reload the page. - refetch(); - }, 3000); - }; - - const onRemoveMember = (memberEntity: CorpUser) => { - const memberName = entityRegistry.getDisplayName(EntityType.CorpUser, memberEntity); - Modal.confirm({ - title: `Confirm Group Member Removal`, - content: `Are you sure you want to remove ${memberName} user from the group?`, - onOk() { - removeGroupMember(memberEntity?.urn); - }, - onCancel() {}, - okText: 'Yes', - maskClosable: true, - closable: true, - }); - }; - - const relationships = membersData && membersData.corpGroup?.relationships; - const total = relationships?.total || 0; - const groupMembers = relationships?.relationships?.map((rel) => rel.entity as CorpUser) || []; - - const getItems = (userEntity: CorpUser): MenuProps['items'] => { - return [ - { - key: 'make', - disabled: true, - label: ( - - Make owner - - ), - }, - { - key: 'remove', - disabled: isExternalGroup, - onClick: () => onRemoveMember(userEntity), - label: ( - - Remove from Group - - ), - }, - ]; - }; - - return ( - <> - - - - Add Member - - - - {groupMembers.length === 0 && } - {groupMembers && - groupMembers.map((item) => { - const entityUrn = entityRegistry.getEntityUrl(EntityType.CorpUser, item.urn); - return ( - - - - - - {entityRegistry.getDisplayName(EntityType.CorpUser, item)} - - - - - - - - - - - - ); - })} - - - - - {isEditingMembers && ( - setIsEditingMembers(false)} - /> - )} - - ); -} diff --git a/datahub-web-react/src/app/entity/group/GroupMembersSideBarSection.tsx b/datahub-web-react/src/app/entity/group/GroupMembersSideBarSection.tsx deleted file mode 100644 index 1a7d4465da8a70..00000000000000 --- a/datahub-web-react/src/app/entity/group/GroupMembersSideBarSection.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { Tag, Tooltip } from 'antd'; -import React from 'react'; -import { Link } from 'react-router-dom'; -import styled from 'styled-components'; - -import { - DisplayCount, - EmptyValue, - GroupSectionHeader, - GroupSectionTitle, - GroupsSeeMoreText, - TagsSection, -} from '@app/entity/shared/SidebarStyledComponents'; -import { CustomAvatar } from '@app/shared/avatar'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { CorpUser, EntityRelationship, EntityType } from '@types'; - -const TITLE = 'Members'; - -const MemberTag = styled(Tag)` - padding: 2px; - padding-right: 6px; - margin-bottom: 8px; - display: inline-flex; - align-items: center; -`; - -type Props = { - total: number; - relationships: Array; - onSeeMore: () => void; -}; - -export default function GroupMembersSideBarSection({ total, relationships, onSeeMore }: Props) { - const entityRegistry = useEntityRegistry(); - - return ( - <> - - {TITLE} - {total} - - - {relationships.length === 0 && } - {relationships.length > 0 && - relationships.map((item) => { - const user = item.entity as CorpUser; - const name = entityRegistry.getDisplayName(EntityType.CorpUser, user); - return ( - - - - {name.length > 15 ? ( - {`${name.substring(0, 15)}..`} - ) : ( - {name} - )} - - - ); - })} - {relationships.length > 15 && ( -
- {`+${ - relationships.length - 15 - } more`} -
- )} -
- - ); -} diff --git a/datahub-web-react/src/app/entity/group/GroupOwnerSideBarSection.tsx b/datahub-web-react/src/app/entity/group/GroupOwnerSideBarSection.tsx deleted file mode 100644 index 2ad254adb08274..00000000000000 --- a/datahub-web-react/src/app/entity/group/GroupOwnerSideBarSection.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { PlusOutlined } from '@ant-design/icons'; -import { Button, Typography } from 'antd'; -import React, { useState } from 'react'; -import styled from 'styled-components'; - -import { DisplayCount, GroupSectionHeader, GroupSectionTitle } from '@app/entity/shared/SidebarStyledComponents'; -import { ExpandedOwner } from '@app/entity/shared/components/styled/ExpandedOwner/ExpandedOwner'; -import { EditOwnersModal } from '@app/entity/shared/containers/profile/sidebar/Ownership/EditOwnersModal'; - -import { EntityType, Ownership } from '@types'; - -const TITLE = 'Owners'; - -const SectionWrapper = styled.div``; - -const OwnersWrapper = styled.div` - display: flex; - gap: 6px; - flex-wrap: wrap; - margin-bottom: 8px; -`; - -const AddOwnerButton = styled(Button)``; - -type Props = { - ownership: Ownership; - refetch: () => Promise; - urn: string; -}; - -export default function GroupOwnerSideBarSection({ urn, ownership, refetch }: Props) { - const [showAddModal, setShowAddModal] = useState(false); - const ownersEmpty = !ownership?.owners?.length; - - return ( - <> - - {TITLE} - {ownership?.owners?.length || ''} - - - - {ownership && - ownership?.owners?.map((owner) => ( - - ))} - - {ownersEmpty && ( - No group owners added yet. - )} - {ownersEmpty && ( - setShowAddModal(true)}> - - Add Owners - - )} - {!ownersEmpty && ( - setShowAddModal(true)}> - - Add Owners - - )} - - {showAddModal && ( - { - setShowAddModal(false); - }} - /> - )} - - ); -} diff --git a/datahub-web-react/src/app/entity/group/GroupProfile.tsx b/datahub-web-react/src/app/entity/group/GroupProfile.tsx deleted file mode 100644 index ec41a78f2a5f93..00000000000000 --- a/datahub-web-react/src/app/entity/group/GroupProfile.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import { Col, Row } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -import { GroupAssets } from '@app/entity/group/GroupAssets'; -import GroupInfoSidebar from '@app/entity/group/GroupInfoSideBar'; -import GroupMembers from '@app/entity/group/GroupMembers'; -import NonExistentEntityPage from '@app/entity/shared/entity/NonExistentEntityPage'; -import { decodeUrn } from '@app/entity/shared/utils'; -import { Message } from '@app/shared/Message'; -import { RoutedTabs } from '@app/shared/RoutedTabs'; -import useUserParams from '@app/shared/entitySearch/routingUtils/useUserParams'; -import { ErrorSection } from '@app/shared/error/ErrorSection'; - -import { useGetGroupQuery } from '@graphql/group.generated'; -import { EntityRelationshipsResult, OriginType, Ownership } from '@types'; - -const messageStyle = { marginTop: '10%' }; - -export enum TabType { - Assets = 'Owner Of', - Members = 'Members', -} - -const ENABLED_TAB_TYPES = [TabType.Assets, TabType.Members]; - -const MEMBER_PAGE_SIZE = 15; - -/** - * Styled Components - */ -const GroupProfileWrapper = styled.div` - &&& .ant-tabs-nav { - margin: 0; - } -`; - -const Content = styled.div` - color: #262626; - height: calc(100vh - 60px); - - &&& .ant-tabs > .ant-tabs-nav .ant-tabs-nav-wrap { - padding-left: 15px; - } -`; - -/** - * Responsible for reading & writing groups. - */ -export default function GroupProfile() { - const { urn: encodedUrn } = useUserParams(); - const urn = encodedUrn && decodeUrn(encodedUrn); - const { loading, error, data, refetch } = useGetGroupQuery({ variables: { urn, membersCount: MEMBER_PAGE_SIZE } }); - - const groupMemberRelationships = data?.corpGroup?.relationships as EntityRelationshipsResult; - const isExternalGroup: boolean = data?.corpGroup?.origin?.type === OriginType.External; - const externalGroupType: string = data?.corpGroup?.origin?.externalType || 'outside DataHub'; - - const getTabs = () => { - return [ - { - name: TabType.Assets, - path: TabType.Assets.toLocaleLowerCase(), - content: , - display: { - enabled: () => true, - }, - }, - { - name: TabType.Members, - path: TabType.Members.toLocaleLowerCase(), - content: ( - { - setTimeout(() => refetch(), 2000); - }} - /> - ), - display: { - enabled: () => true, - }, - }, - ].filter((tab) => ENABLED_TAB_TYPES.includes(tab.name)); - }; - - const defaultTabPath = getTabs() && getTabs()?.length > 0 ? getTabs()[0].path : ''; - const onTabChange = () => null; - - // Side bar data - const sideBarData = { - photoUrl: data?.corpGroup?.editableProperties?.pictureLink || undefined, - avatarName: - data?.corpGroup?.properties?.displayName || - data?.corpGroup?.name || - data?.corpGroup?.info?.displayName || - undefined, - name: - data?.corpGroup?.properties?.displayName || - data?.corpGroup?.name || - data?.corpGroup?.info?.displayName || - undefined, - email: data?.corpGroup?.editableProperties?.email || data?.corpGroup?.properties?.email || undefined, - slack: data?.corpGroup?.editableProperties?.slack || data?.corpGroup?.properties?.slack || undefined, - aboutText: - data?.corpGroup?.editableProperties?.description || data?.corpGroup?.properties?.description || undefined, - groupMemberRelationships: groupMemberRelationships as EntityRelationshipsResult, - groupOwnerShip: data?.corpGroup?.ownership as Ownership, - isExternalGroup, - externalGroupType, - urn, - }; - - if (data?.corpGroup?.exists === false) { - return ; - } - return ( - <> - {error && } - {loading && } - {data && data?.corpGroup && ( - - -
- - - - - - - - - - )} - - ); -} diff --git a/datahub-web-react/src/app/entity/group/preview/Preview.tsx b/datahub-web-react/src/app/entity/group/preview/Preview.tsx deleted file mode 100644 index 3899ceba911bf9..00000000000000 --- a/datahub-web-react/src/app/entity/group/preview/Preview.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import { Tag, Typography } from 'antd'; -import React from 'react'; -import { Link } from 'react-router-dom'; -import styled from 'styled-components'; - -import { IconStyleType } from '@app/entity/Entity'; -import NoMarkdownViewer from '@app/entity/shared/components/styled/StripMarkdownText'; -import { ANTD_GRAY } from '@app/entity/shared/constants'; -import SearchTextHighlighter from '@app/search/matches/SearchTextHighlighter'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { EntityType } from '@types'; - -const PreviewContainer = styled.div` - margin-bottom: 4px; - display: flex; - width: 100%; - justify-content: space-between; - align-items: center; -`; - -const PlatformInfo = styled.div` - margin-bottom: 8px; - display: flex; - align-items: center; - height: 24px; -`; - -const TitleContainer = styled.div` - margin-bottom: 8px; -`; - -const PreviewImage = styled.div` - max-height: 18px; - width: auto; - object-fit: contain; - margin-right: 10px; - background-color: transparent; -`; - -const EntityTitle = styled(Typography.Text)` - &&& { - margin-bottom: 0; - font-size: 16px; - font-weight: 600; - vertical-align: middle; - } -`; - -const PlatformText = styled(Typography.Text)` - font-size: 12px; - line-height: 20px; - font-weight: 700; - color: ${ANTD_GRAY[7]}; -`; - -const DescriptionContainer = styled.div` - margin-top: 5px; -`; - -const MemberCountContainer = styled.span` - margin-left: 12px; - margin-right: 12px; -`; - -export const Preview = ({ - urn, - name, - description, - membersCount, -}: { - urn: string; - name: string; - description?: string | null; - membersCount?: number; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - const url = entityRegistry.getEntityUrl(EntityType.CorpGroup, urn); - - return ( - -
- - - - - {entityRegistry.getIcon(EntityType.CorpGroup, 20, IconStyleType.HIGHLIGHT)} - - {entityRegistry.getEntityName(EntityType.CorpGroup)} - - - {name ? : urn} - - {membersCount} members - - - - - {description && description.length > 0 && ( - - } - > - {description} - - - )} -
-
- ); -}; diff --git a/datahub-web-react/src/app/entity/mlFeature/MLFeatureEntity.tsx b/datahub-web-react/src/app/entity/mlFeature/MLFeatureEntity.tsx deleted file mode 100644 index b6aa4edfe6da40..00000000000000 --- a/datahub-web-react/src/app/entity/mlFeature/MLFeatureEntity.tsx +++ /dev/null @@ -1,217 +0,0 @@ -import { DotChartOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { Preview } from '@app/entity/mlFeature/preview/Preview'; -import { EntityMenuItems } from '@app/entity/shared/EntityDropdown/EntityDropdown'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import DataProductSection from '@app/entity/shared/containers/profile/sidebar/DataProduct/DataProductSection'; -import { SidebarDomainSection } from '@app/entity/shared/containers/profile/sidebar/Domain/SidebarDomainSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import { SidebarTagsSection } from '@app/entity/shared/containers/profile/sidebar/SidebarTagsSection'; -import SidebarStructuredPropsSection from '@app/entity/shared/containers/profile/sidebar/StructuredProperties/SidebarStructuredPropsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { DocumentationTab } from '@app/entity/shared/tabs/Documentation/DocumentationTab'; -import { IncidentTab } from '@app/entity/shared/tabs/Incident/IncidentTab'; -import { LineageTab } from '@app/entity/shared/tabs/Lineage/LineageTab'; -import { FeatureTableTab } from '@app/entity/shared/tabs/ML/MlFeatureFeatureTableTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; -import { GenericEntityProperties } from '@app/entity/shared/types'; -import { getDataProduct } from '@app/entity/shared/utils'; - -import { useGetMlFeatureQuery } from '@graphql/mlFeature.generated'; -import { EntityType, MlFeature, OwnershipType, SearchResult } from '@types'; - -/** - * Definition of the DataHub MLFeature entity. - */ -export class MLFeatureEntity implements Entity { - type: EntityType = EntityType.Mlfeature; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => false; - - isLineageEnabled = () => true; - - getAutoCompleteFieldName = () => 'name'; - - getGraphName = () => 'mlFeature'; - - getPathName = () => 'features'; - - getEntityName = () => 'Feature'; - - getCollectionName = () => 'Features'; - - useEntityQuery = useGetMlFeatureQuery; - - getOverridePropertiesFromEntity = (feature?: MlFeature | null): GenericEntityProperties => { - return { - // eslint-disable-next-line - platform: feature?.['featureTables']?.relationships?.[0]?.entity?.platform, - }; - }; - - renderProfile = (urn: string) => ( - { - const activeIncidentCount = mlFeature?.mlFeature?.activeIncidents?.total; - return `Incidents${(activeIncidentCount && ` (${activeIncidentCount})`) || ''}`; - }, - }, - ]} - sidebarSections={this.getSidebarSections()} - /> - ); - - getSidebarSections = () => [ - { - component: SidebarAboutSection, - }, - { - component: SidebarOwnerSection, - properties: { - defaultOwnerType: OwnershipType.TechnicalOwner, - }, - }, - { - component: SidebarTagsSection, - properties: { - hasTags: true, - hasTerms: true, - }, - }, - { - component: SidebarDomainSection, - }, - { - component: DataProductSection, - }, - { - component: SidebarStructuredPropsSection, - }, - ]; - - renderPreview = (previewType: PreviewType, data: MlFeature) => { - const genericProperties = this.getGenericEntityProperties(data); - // eslint-disable-next-line - const platform = data?.['featureTables']?.relationships?.[0]?.entity?.platform; - return ( - - ); - }; - - renderSearch = (result: SearchResult) => { - const data = result.entity as MlFeature; - const genericProperties = this.getGenericEntityProperties(data); - // eslint-disable-next-line - const platform = data?.['featureTables']?.relationships?.[0]?.entity?.platform; - return ( - - ); - }; - - displayName = (data: MlFeature) => { - return data.name || data.urn; - }; - - getGenericEntityProperties = (mlFeature: MlFeature) => { - return getDataForEntityType({ - data: mlFeature, - entityType: this.type, - getOverrideProperties: this.getOverridePropertiesFromEntity, - }); - }; - - getLineageVizConfig = (entity: MlFeature) => { - return { - urn: entity.urn, - name: entity.name, - type: EntityType.Mlfeature, - // eslint-disable-next-line - icon: entity?.['featureTables']?.relationships?.[0]?.entity?.platform?.properties?.logoUrl || undefined, - // eslint-disable-next-line - platform: entity?.['featureTables']?.relationships?.[0]?.entity?.platform, - }; - }; - - supportedCapabilities = () => { - return new Set([ - EntityCapabilityType.OWNERS, - EntityCapabilityType.GLOSSARY_TERMS, - EntityCapabilityType.TAGS, - EntityCapabilityType.DOMAINS, - EntityCapabilityType.DEPRECATION, - EntityCapabilityType.SOFT_DELETE, - EntityCapabilityType.DATA_PRODUCTS, - ]); - }; -} diff --git a/datahub-web-react/src/app/entity/mlFeature/preview/Preview.tsx b/datahub-web-react/src/app/entity/mlFeature/preview/Preview.tsx deleted file mode 100644 index 0f5a92deb9168c..00000000000000 --- a/datahub-web-react/src/app/entity/mlFeature/preview/Preview.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react'; - -import { IconStyleType, PreviewType } from '@app/entity/Entity'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { capitalizeFirstLetterOnly } from '@app/shared/textUtil'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { DataPlatform, DataProduct, EntityPath, EntityType, Owner } from '@types'; - -export const Preview = ({ - urn, - name, - platformInstanceId, - featureNamespace, - description, - dataProduct, - owners, - platform, - degree, - paths, - previewType, -}: { - urn: string; - name: string; - featureNamespace: string; - platformInstanceId?: string; - description?: string | null; - dataProduct?: DataProduct | null; - owners?: Array | null; - platform?: DataPlatform | null | undefined; - degree?: number; - paths?: EntityPath[]; - previewType: PreviewType; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - return ( - - ); -}; diff --git a/datahub-web-react/src/app/entity/mlFeatureTable/MLFeatureTableEntity.tsx b/datahub-web-react/src/app/entity/mlFeatureTable/MLFeatureTableEntity.tsx deleted file mode 100644 index f3639e985935f7..00000000000000 --- a/datahub-web-react/src/app/entity/mlFeatureTable/MLFeatureTableEntity.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import { DotChartOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { Preview } from '@app/entity/mlFeatureTable/preview/Preview'; -import Sources from '@app/entity/mlFeatureTable/profile/Sources'; -import MlFeatureTableFeatures from '@app/entity/mlFeatureTable/profile/features/MlFeatureTableFeatures'; -import { EntityMenuItems } from '@app/entity/shared/EntityDropdown/EntityDropdown'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import DataProductSection from '@app/entity/shared/containers/profile/sidebar/DataProduct/DataProductSection'; -import { SidebarDomainSection } from '@app/entity/shared/containers/profile/sidebar/Domain/SidebarDomainSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import { SidebarTagsSection } from '@app/entity/shared/containers/profile/sidebar/SidebarTagsSection'; -import SidebarStructuredPropsSection from '@app/entity/shared/containers/profile/sidebar/StructuredProperties/SidebarStructuredPropsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { DocumentationTab } from '@app/entity/shared/tabs/Documentation/DocumentationTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; -import { GenericEntityProperties } from '@app/entity/shared/types'; -import { getDataProduct } from '@app/entity/shared/utils'; -import { capitalizeFirstLetterOnly } from '@app/shared/textUtil'; - -import { useGetMlFeatureTableQuery } from '@graphql/mlFeatureTable.generated'; -import { EntityType, MlFeatureTable, OwnershipType, SearchResult } from '@types'; - -/** - * Definition of the DataHub MLFeatureTable entity. - */ -export class MLFeatureTableEntity implements Entity { - type: EntityType = EntityType.MlfeatureTable; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => true; - - isLineageEnabled = () => true; - - getAutoCompleteFieldName = () => 'name'; - - getGraphName = () => 'mlFeatureTable'; - - getPathName = () => 'featureTables'; - - getEntityName = () => 'Feature Table'; - - getCollectionName = () => 'Feature Tables'; - - getOverridePropertiesFromEntity = (_?: MlFeatureTable | null): GenericEntityProperties => { - return {}; - }; - - useEntityQuery = useGetMlFeatureTableQuery; - - getSidebarSections = () => [ - { - component: SidebarAboutSection, - }, - { - component: SidebarOwnerSection, - properties: { - defaultOwnerType: OwnershipType.TechnicalOwner, - }, - }, - { - component: SidebarTagsSection, - properties: { - hasTags: true, - hasTerms: true, - }, - }, - { - component: SidebarDomainSection, - }, - { - component: DataProductSection, - }, - { - component: SidebarStructuredPropsSection, - }, - ]; - - renderProfile = (urn: string) => ( - - ); - - renderPreview = (_: PreviewType, data: MlFeatureTable) => { - const genericProperties = this.getGenericEntityProperties(data); - return ( - - ); - }; - - renderSearch = (result: SearchResult) => { - const data = result.entity as MlFeatureTable; - const genericProperties = this.getGenericEntityProperties(data); - return ( - - ); - }; - - getLineageVizConfig = (entity: MlFeatureTable) => { - return { - urn: entity.urn, - name: entity.name, - type: EntityType.MlfeatureTable, - icon: entity.platform.properties?.logoUrl || undefined, - platform: entity.platform, - }; - }; - - displayName = (data: MlFeatureTable) => { - return data.name || data.urn; - }; - - getGenericEntityProperties = (mlFeatureTable: MlFeatureTable) => { - return getDataForEntityType({ - data: mlFeatureTable, - entityType: this.type, - getOverrideProperties: (data) => data, - }); - }; - - supportedCapabilities = () => { - return new Set([ - EntityCapabilityType.OWNERS, - EntityCapabilityType.GLOSSARY_TERMS, - EntityCapabilityType.TAGS, - EntityCapabilityType.DOMAINS, - EntityCapabilityType.DEPRECATION, - EntityCapabilityType.SOFT_DELETE, - EntityCapabilityType.DATA_PRODUCTS, - ]); - }; -} diff --git a/datahub-web-react/src/app/entity/mlFeatureTable/preview/Preview.tsx b/datahub-web-react/src/app/entity/mlFeatureTable/preview/Preview.tsx deleted file mode 100644 index dcdad78f5a04dd..00000000000000 --- a/datahub-web-react/src/app/entity/mlFeatureTable/preview/Preview.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React from 'react'; - -import { IconStyleType } from '@app/entity/Entity'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { DataProduct, EntityPath, EntityType, Owner } from '@types'; - -export const Preview = ({ - urn, - name, - description, - owners, - logoUrl, - platformName, - dataProduct, - platformInstanceId, - degree, - paths, -}: { - urn: string; - name: string; - description?: string | null; - owners?: Array | null; - logoUrl?: string | null; - platformName?: string | null; - dataProduct?: DataProduct | null; - platformInstanceId?: string; - degree?: number; - paths?: EntityPath[]; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - return ( - - ); -}; diff --git a/datahub-web-react/src/app/entity/mlFeatureTable/profile/MLFeatureTableHeader.tsx b/datahub-web-react/src/app/entity/mlFeatureTable/profile/MLFeatureTableHeader.tsx deleted file mode 100644 index 4bfa562c39062f..00000000000000 --- a/datahub-web-react/src/app/entity/mlFeatureTable/profile/MLFeatureTableHeader.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { Image, Row, Space, Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -import MarkdownViewer from '@app/entity/shared/components/legacy/MarkdownViewer'; -import CompactContext from '@app/shared/CompactContext'; -import { AvatarsGroup } from '@app/shared/avatar'; -import { capitalizeFirstLetterOnly } from '@app/shared/textUtil'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { MlFeatureTable } from '@types'; - -const HeaderInfoItem = styled.div` - display: inline-block; - text-align: left; - width: 125px; - vertical-align: top; -`; - -const PlatformName = styled(Typography.Text)` - font-size: 16px; -`; -const PreviewImage = styled(Image)` - max-height: 20px; - padding-top: 3px; - width: auto; - object-fit: contain; -`; - -export type Props = { - mlFeatureTable: MlFeatureTable; -}; - -export default function MLFeatureTableHeader({ mlFeatureTable: { platform, description, ownership } }: Props) { - const entityRegistry = useEntityRegistry(); - const isCompact = React.useContext(CompactContext); - - const platformName = platform.properties?.displayName || capitalizeFirstLetterOnly(platform.name); - - return ( - <> - - - {platform ? ( - -
- - Platform - -
- - {platform.properties?.logoUrl ? ( - - ) : null} - {platformName} - -
- ) : null} -
- - -
- - ); -} diff --git a/datahub-web-react/src/app/entity/mlFeatureTable/profile/Sources.tsx b/datahub-web-react/src/app/entity/mlFeatureTable/profile/Sources.tsx deleted file mode 100644 index 3c21eced46f0dc..00000000000000 --- a/datahub-web-react/src/app/entity/mlFeatureTable/profile/Sources.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { List, Typography } from 'antd'; -import React, { useMemo } from 'react'; -import styled from 'styled-components'; - -import { PreviewType } from '@app/entity/Entity'; -import { useBaseEntity } from '@app/entity/shared/EntityContext'; -import { notEmpty } from '@app/entity/shared/utils'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { GetMlFeatureTableQuery } from '@graphql/mlFeatureTable.generated'; -import { Dataset, EntityType } from '@types'; - -const ViewRawButtonContainer = styled.div` - display: flex; - justify-content: flex-end; -`; - -export default function SourcesView() { - const entityRegistry = useEntityRegistry(); - const baseEntity = useBaseEntity(); - const featureTable = baseEntity?.mlFeatureTable; - - const features = useMemo( - () => - featureTable?.properties && - (featureTable?.properties?.mlFeatures || featureTable?.properties?.mlPrimaryKeys) - ? [ - ...(featureTable?.properties?.mlPrimaryKeys || []), - ...(featureTable?.properties?.mlFeatures || []), - ].filter(notEmpty) - : [], - [featureTable?.properties], - ); - - const sources = useMemo( - () => - features?.reduce((accumulator: Array, feature) => { - if (feature.__typename === 'MLFeature' && feature.properties?.sources) { - // eslint-disable-next-line array-callback-return - feature.properties?.sources?.map((source: Dataset | null) => { - if (source && accumulator.findIndex((dataset) => dataset.urn === source?.urn) === -1) { - accumulator.push(source); - } - }); - } else if (feature.__typename === 'MLPrimaryKey' && feature.properties?.sources) { - // eslint-disable-next-line array-callback-return - feature.properties?.sources?.map((source: Dataset | null) => { - if (source && accumulator.findIndex((dataset) => dataset.urn === source?.urn) === -1) { - accumulator.push(source); - } - }); - } - return accumulator; - }, []), - [features], - ); - - return ( - <> -
- - { - // ToDo: uncomment below these after refactored Lineage to support dynamic entities - /* */ - } - -
- Sources} - renderItem={(item) => ( - - {entityRegistry.renderPreview(item?.type || EntityType.Dataset, PreviewType.PREVIEW, item)} - - )} - /> - - ); -} diff --git a/datahub-web-react/src/app/entity/mlFeatureTable/profile/features/MlFeatureDataTypeIcon.tsx b/datahub-web-react/src/app/entity/mlFeatureTable/profile/features/MlFeatureDataTypeIcon.tsx deleted file mode 100644 index 7f72f206129a4c..00000000000000 --- a/datahub-web-react/src/app/entity/mlFeatureTable/profile/features/MlFeatureDataTypeIcon.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { - AudioOutlined, - ClockCircleOutlined, - FieldBinaryOutlined, - FieldStringOutlined, - FileImageOutlined, - LineChartOutlined, - NumberOutlined, - OrderedListOutlined, - QuestionOutlined, - StopOutlined, - UnorderedListOutlined, - VideoCameraOutlined, -} from '@ant-design/icons'; -import { Tooltip, Typography } from 'antd'; -import React, { FC } from 'react'; -import { VscFileBinary } from 'react-icons/vsc'; -import styled from 'styled-components'; - -import { capitalizeFirstLetter } from '@app/shared/textUtil'; - -import { MlFeatureDataType } from '@types'; - -const TypeIconContainer = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - text-align: center; - margin-top: 2.5px; - width: 70px; -`; - -const TypeSubtitle = styled(Typography.Text)<{ hasicon?: string }>` - font-size: 8px; - text-align: center; - text-transform: uppercase; - ${(props) => (props.hasicon ? '' : 'margin-top: 4px;')} -`; - -const IconSpan = styled.span` - font-size: 18px; -`; - -const DATA_TYPE_ICON_MAP: Record | null; size: number; text: string }> = { - [MlFeatureDataType.Byte]: { - icon: () => ( - - - - ), - size: 18, - text: 'Byte', - }, - [MlFeatureDataType.Time]: { icon: ClockCircleOutlined, size: 18, text: 'Time' }, - [MlFeatureDataType.Set]: { icon: UnorderedListOutlined, size: 18, text: 'Set' }, - [MlFeatureDataType.Unknown]: { icon: QuestionOutlined, size: 16, text: 'Unknown' }, - [MlFeatureDataType.Map]: { icon: UnorderedListOutlined, size: 14, text: 'Map' }, - [MlFeatureDataType.Useless]: { icon: StopOutlined, size: 18, text: 'Useless' }, - [MlFeatureDataType.Nominal]: { icon: NumberOutlined, size: 14, text: 'Nominal' }, - [MlFeatureDataType.Ordinal]: { icon: OrderedListOutlined, size: 18, text: 'Ordinal' }, - [MlFeatureDataType.Binary]: { icon: FieldBinaryOutlined, size: 16, text: 'Binary' }, - [MlFeatureDataType.Count]: { icon: NumberOutlined, size: 14, text: 'Count' }, - [MlFeatureDataType.Interval]: { icon: ClockCircleOutlined, size: 16, text: 'Interval' }, - [MlFeatureDataType.Image]: { icon: FileImageOutlined, size: 16, text: 'Image' }, - [MlFeatureDataType.Video]: { icon: VideoCameraOutlined, size: 16, text: 'Video' }, - [MlFeatureDataType.Audio]: { icon: AudioOutlined, size: 16, text: 'Audio' }, - [MlFeatureDataType.Text]: { icon: FieldStringOutlined, size: 18, text: 'Text' }, - [MlFeatureDataType.Sequence]: { icon: OrderedListOutlined, size: 16, text: 'Sequence' }, - [MlFeatureDataType.Continuous]: { icon: LineChartOutlined, size: 16, text: 'Continuous' }, -}; - -type Props = { - dataType?: MlFeatureDataType; -}; - -export default function MlFeatureDataTypeIcon({ dataType }: Props) { - const { icon: Icon, size, text } = DATA_TYPE_ICON_MAP[dataType || MlFeatureDataType.Unknown]; - - // eslint-disable-next-line react/prop-types - const NativeDataTypeTooltip = ({ children }) => ( - - {children} - - ); - - return ( - - - {Icon && } - - {text} - - - - ); -} diff --git a/datahub-web-react/src/app/entity/mlFeatureTable/profile/features/MlFeatureTableFeatures.tsx b/datahub-web-react/src/app/entity/mlFeatureTable/profile/features/MlFeatureTableFeatures.tsx deleted file mode 100644 index cb7a92a204daf0..00000000000000 --- a/datahub-web-react/src/app/entity/mlFeatureTable/profile/features/MlFeatureTableFeatures.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; - -import TableOfMlFeatures from '@app/entity/mlFeatureTable/profile/features/TableOfMlFeatures'; -import { useBaseEntity } from '@app/entity/shared/EntityContext'; -import { notEmpty } from '@app/entity/shared/utils'; - -import { GetMlFeatureTableQuery } from '@graphql/mlFeatureTable.generated'; -import { MlFeature, MlPrimaryKey } from '@types'; - -export default function MlFeatureTableFeatures() { - const baseEntity = useBaseEntity(); - const featureTable = baseEntity?.mlFeatureTable; - - const features = ( - featureTable?.properties && (featureTable?.properties?.mlFeatures || featureTable?.properties?.mlPrimaryKeys) - ? [ - ...(featureTable?.properties?.mlPrimaryKeys || []), - ...(featureTable?.properties?.mlFeatures || []), - ].filter(notEmpty) - : [] - ) as Array; - - return ; -} diff --git a/datahub-web-react/src/app/entity/mlFeatureTable/profile/features/TableOfMlFeatures.tsx b/datahub-web-react/src/app/entity/mlFeatureTable/profile/features/TableOfMlFeatures.tsx deleted file mode 100644 index 5c8d6a2df2e35f..00000000000000 --- a/datahub-web-react/src/app/entity/mlFeatureTable/profile/features/TableOfMlFeatures.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import { CheckSquareOutlined } from '@ant-design/icons'; -import { Table, Typography } from 'antd'; -import { AlignType } from 'rc-table/lib/interface'; -import React, { useState } from 'react'; -import { Link } from 'react-router-dom'; -import styled from 'styled-components'; - -import SchemaDescriptionField from '@app/entity/dataset/profile/schema/components/SchemaDescriptionField'; -import MlFeatureDataTypeIcon from '@app/entity/mlFeatureTable/profile/features/MlFeatureDataTypeIcon'; -import { useRefetch } from '@app/entity/shared/EntityContext'; -import TagTermGroup from '@app/shared/tags/TagTermGroup'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { useUpdateDescriptionMutation } from '@graphql/mutations.generated'; -import { MlFeature, MlFeatureDataType, MlPrimaryKey } from '@types'; - -const FeaturesContainer = styled.div` - margin-bottom: 100px; -`; - -const defaultColumns = [ - { - title: 'Type', - dataIndex: 'dataType', - key: 'dataType', - width: 100, - align: 'left' as AlignType, - render: (dataType: MlFeatureDataType) => { - return ; - }, - }, -]; - -type Props = { - features: Array; -}; - -export default function TableOfMlFeatures({ features }: Props) { - const refetch = useRefetch(); - const [updateDescription] = useUpdateDescriptionMutation(); - const entityRegistry = useEntityRegistry(); - - const [tagHoveredIndex, setTagHoveredIndex] = useState(undefined); - const [expandedRows, setExpandedRows] = useState({}); - - const onTagTermCell = (record: any, rowIndex: number | undefined) => ({ - onMouseEnter: () => { - setTagHoveredIndex(`${record.urn}-${rowIndex}`); - }, - onMouseLeave: () => { - setTagHoveredIndex(undefined); - }, - }); - - const nameColumn = { - title: 'Name', - dataIndex: 'name', - key: 'name', - width: 100, - render: (name: string, feature: MlFeature | MlPrimaryKey) => ( - - {name} - - ), - }; - - const descriptionColumn = { - title: 'Description', - dataIndex: 'description', - key: 'description', - render: (_, feature: MlFeature | MlPrimaryKey, index: number) => ( - { - setExpandedRows((prev) => ({ ...prev, [index]: expanded })); - }} - expanded={!!expandedRows[index]} - description={feature?.editableProperties?.description || feature?.properties?.description || ''} - original={feature?.properties?.description} - isEdited={!!feature?.editableProperties?.description} - onUpdate={(updatedDescription) => - updateDescription({ - variables: { - input: { - description: updatedDescription, - resourceUrn: feature.urn, - }, - }, - }).then(refetch) - } - /> - ), - width: 300, - }; - - const tagColumn = { - width: 125, - title: 'Tags', - dataIndex: 'tags', - key: 'tags', - render: (_, feature: MlFeature | MlPrimaryKey, rowIndex: number) => ( - setTagHoveredIndex(undefined)} - entityUrn={feature.urn} - entityType={feature.type} - refetch={refetch} - /> - ), - onCell: onTagTermCell, - }; - - const termColumn = { - width: 125, - title: 'Terms', - dataIndex: 'glossaryTerms', - key: 'glossaryTerms', - render: (_, feature: MlFeature | MlPrimaryKey, rowIndex: number) => ( - setTagHoveredIndex(undefined)} - entityUrn={feature.urn} - entityType={feature.type} - refetch={refetch} - /> - ), - onCell: onTagTermCell, - }; - - const primaryKeyColumn = { - title: 'Primary Key', - dataIndex: 'primaryKey', - key: 'primaryKey', - render: (_: any, record: MlFeature | MlPrimaryKey) => - record.__typename === 'MLPrimaryKey' ? : null, - width: 50, - }; - - const allColumns = [...defaultColumns, nameColumn, descriptionColumn, tagColumn, termColumn, primaryKeyColumn]; - - return ( - - {features && features.length > 0 && ( -
`${record.dataType}-${record.name}`} - expandable={{ defaultExpandAllRows: true, expandRowByClick: true }} - pagination={false} - /> - )} - - ); -} diff --git a/datahub-web-react/src/app/entity/mlModel/MLModelEntity.tsx b/datahub-web-react/src/app/entity/mlModel/MLModelEntity.tsx deleted file mode 100644 index e9e9d137be9ebb..00000000000000 --- a/datahub-web-react/src/app/entity/mlModel/MLModelEntity.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import { CodeSandboxOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { Preview } from '@app/entity/mlModel/preview/Preview'; -import MLModelGroupsTab from '@app/entity/mlModel/profile/MLModelGroupsTab'; -import MLModelSummary from '@app/entity/mlModel/profile/MLModelSummary'; -import MlModelFeaturesTab from '@app/entity/mlModel/profile/MlModelFeaturesTab'; -import { EntityMenuItems } from '@app/entity/shared/EntityDropdown/EntityDropdown'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import DataProductSection from '@app/entity/shared/containers/profile/sidebar/DataProduct/DataProductSection'; -import { SidebarDomainSection } from '@app/entity/shared/containers/profile/sidebar/Domain/SidebarDomainSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import { SidebarTagsSection } from '@app/entity/shared/containers/profile/sidebar/SidebarTagsSection'; -import SidebarStructuredPropsSection from '@app/entity/shared/containers/profile/sidebar/StructuredProperties/SidebarStructuredPropsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { DocumentationTab } from '@app/entity/shared/tabs/Documentation/DocumentationTab'; -import { IncidentTab } from '@app/entity/shared/tabs/Incident/IncidentTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; -import { GenericEntityProperties } from '@app/entity/shared/types'; - -import { useGetMlModelQuery } from '@graphql/mlModel.generated'; -import { EntityType, MlModel, OwnershipType, SearchResult } from '@types'; - -/** - * Definition of the DataHub MlModel entity. - */ -export class MLModelEntity implements Entity { - type: EntityType = EntityType.Mlmodel; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => true; - - isLineageEnabled = () => true; - - getAutoCompleteFieldName = () => 'name'; - - getGraphName = () => 'mlModel'; - - getPathName = () => 'mlModels'; - - getEntityName = () => 'ML Model'; - - getCollectionName = () => 'ML Models'; - - getOverridePropertiesFromEntity = (mlModel?: MlModel | null): GenericEntityProperties => { - return { - externalUrl: mlModel?.properties?.externalUrl, - }; - }; - - useEntityQuery = useGetMlModelQuery; - - getSidebarSections = () => [ - { - component: SidebarAboutSection, - }, - { - component: SidebarOwnerSection, - properties: { - defaultOwnerType: OwnershipType.TechnicalOwner, - }, - }, - { - component: SidebarTagsSection, - properties: { - hasTags: true, - hasTerms: true, - }, - }, - { - component: SidebarDomainSection, - }, - { - component: DataProductSection, - }, - { - component: SidebarStructuredPropsSection, - }, - ]; - - renderProfile = (urn: string) => ( - { - const activeIncidentCount = mlModel?.mlModel?.activeIncidents?.total; - return `Incidents${(activeIncidentCount && ` (${activeIncidentCount})`) || ''}`; - }, - }, - ]} - sidebarSections={this.getSidebarSections()} - /> - ); - - renderPreview = (_: PreviewType, data: MlModel) => { - return ; - }; - - renderSearch = (result: SearchResult) => { - const data = result.entity as MlModel; - return ; - }; - - getLineageVizConfig = (entity: MlModel) => { - return { - urn: entity.urn, - // eslint-disable-next-line @typescript-eslint/dot-notation - name: entity.properties?.['propertiesName'] || entity.name, - type: EntityType.Mlmodel, - icon: entity.platform?.properties?.logoUrl || undefined, - platform: entity.platform, - }; - }; - - displayName = (data: MlModel) => { - return data.properties?.name || data.name || data.urn; - }; - - getGenericEntityProperties = (mlModel: MlModel) => { - return getDataForEntityType({ data: mlModel, entityType: this.type, getOverrideProperties: (data) => data }); - }; - - supportedCapabilities = () => { - return new Set([ - EntityCapabilityType.OWNERS, - EntityCapabilityType.GLOSSARY_TERMS, - EntityCapabilityType.TAGS, - EntityCapabilityType.DOMAINS, - EntityCapabilityType.DEPRECATION, - EntityCapabilityType.SOFT_DELETE, - EntityCapabilityType.DATA_PRODUCTS, - ]); - }; -} diff --git a/datahub-web-react/src/app/entity/mlModel/preview/Preview.tsx b/datahub-web-react/src/app/entity/mlModel/preview/Preview.tsx deleted file mode 100644 index 0ee5698658846c..00000000000000 --- a/datahub-web-react/src/app/entity/mlModel/preview/Preview.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; - -import { IconStyleType } from '@app/entity/Entity'; -import { getDataProduct } from '@app/entity/shared/utils'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { capitalizeFirstLetterOnly } from '@app/shared/textUtil'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { EntityPath, EntityType, MlModel } from '@types'; - -export const Preview = ({ - model, - degree, - paths, -}: { - model: MlModel; - degree?: number; - paths?: EntityPath[]; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - const genericProperties = entityRegistry.getGenericEntityProperties(EntityType.Mlmodel, model); - - return ( - - ); -}; diff --git a/datahub-web-react/src/app/entity/mlModel/profile/MLModelGroupsTab.tsx b/datahub-web-react/src/app/entity/mlModel/profile/MLModelGroupsTab.tsx deleted file mode 100644 index fda5a50ea42f20..00000000000000 --- a/datahub-web-react/src/app/entity/mlModel/profile/MLModelGroupsTab.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { Space, Table, Typography } from 'antd'; -import { ColumnsType } from 'antd/es/table'; -import Link from 'antd/lib/typography/Link'; -import React from 'react'; -import styled from 'styled-components'; - -import { useBaseEntity } from '@app/entity/shared/EntityContext'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { GetMlModelQuery } from '@graphql/mlModel.generated'; -import { EntityType, MlModelGroup } from '@types'; - -const TabContent = styled.div` - padding: 16px; -`; - -export default function MLModelGroupsTab() { - const baseEntity = useBaseEntity(); - const model = baseEntity?.mlModel; - - const entityRegistry = useEntityRegistry(); - - const propertyTableColumns: ColumnsType = [ - { - title: 'Group', - dataIndex: 'name', - render: (name, record) => { - return ( - - {record.properties?.name || name} - - ); - }, - }, - { - title: 'Description', - dataIndex: 'description', - }, - ]; - - return ( - - - Groups -
- - - ); -} diff --git a/datahub-web-react/src/app/entity/mlModel/profile/MLModelSummary.tsx b/datahub-web-react/src/app/entity/mlModel/profile/MLModelSummary.tsx deleted file mode 100644 index 7e7652b30b650b..00000000000000 --- a/datahub-web-react/src/app/entity/mlModel/profile/MLModelSummary.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import { Space, Table, Typography } from 'antd'; -import React from 'react'; -import { Link } from 'react-router-dom'; -import styled from 'styled-components'; - -import { Pill } from '@components/components/Pills'; - -import { useBaseEntity } from '@app/entity/shared/EntityContext'; -import { InfoItem } from '@app/entity/shared/components/styled/InfoItem'; -import { notEmpty } from '@app/entity/shared/utils'; -import { TimestampPopover } from '@app/sharedV2/TimestampPopover'; -import { useEntityRegistry } from '@app/useEntityRegistry'; -import { colors } from '@src/alchemy-components/theme'; - -import { GetMlModelQuery } from '@graphql/mlModel.generated'; -import { EntityType, MlHyperParam, MlMetric } from '@types'; - -const TabContent = styled.div` - padding: 16px; -`; - -const InfoItemContainer = styled.div<{ justifyContent }>` - display: flex; - position: relative; - justify-content: ${(props) => props.justifyContent}; - padding: 0px 2px; -`; - -const InfoItemContent = styled.div` - padding-top: 8px; - width: 100px; - overflow-wrap: break-word; -`; - -const JobLink = styled(Link)` - color: ${colors.blue[700]}; - &:hover { - text-decoration: underline; - } -`; - -export default function MLModelSummary() { - const baseEntity = useBaseEntity(); - const model = baseEntity?.mlModel; - const entityRegistry = useEntityRegistry(); - - const propertyTableColumns = [ - { - title: 'Name', - dataIndex: 'name', - width: 450, - }, - { - title: 'Value', - dataIndex: 'value', - }, - ]; - - const renderTrainingJobs = () => { - const trainingJobs = - model?.trainedBy?.relationships?.map((relationship) => relationship.entity).filter(notEmpty) || []; - - if (trainingJobs.length === 0) return '-'; - - return ( -
- {trainingJobs.map((job, index) => { - const { urn, name } = job as { urn: string; name?: string }; - return ( - - - {name || urn} - - {index < trainingJobs.length - 1 && ', '} - - ); - })} -
- ); - }; - - return ( - - - Model Details - - - {model?.versionProperties?.version?.versionTag} - - - - - - - - - {model?.properties?.created?.actor || '-'} - - - - - - {model?.versionProperties?.aliases?.map((alias) => ( - - ))} - - - - {renderTrainingJobs()} - - - Training Metrics -
- Hyper Parameters -
- - - ); -} diff --git a/datahub-web-react/src/app/entity/mlModel/profile/MlModelFeaturesTab.tsx b/datahub-web-react/src/app/entity/mlModel/profile/MlModelFeaturesTab.tsx deleted file mode 100644 index cac4135307f722..00000000000000 --- a/datahub-web-react/src/app/entity/mlModel/profile/MlModelFeaturesTab.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; - -import TableOfMlFeatures from '@app/entity/mlFeatureTable/profile/features/TableOfMlFeatures'; -import { useBaseEntity } from '@app/entity/shared/EntityContext'; - -import { GetMlModelQuery } from '@graphql/mlModel.generated'; -import { MlFeature, MlPrimaryKey } from '@types'; - -export default function MlModelFeaturesTab() { - const entity = useBaseEntity() as GetMlModelQuery; - - const model = entity && entity.mlModel; - const features = model?.features?.relationships?.map((relationship) => relationship.entity) as Array< - MlFeature | MlPrimaryKey - >; - - return ; -} diff --git a/datahub-web-react/src/app/entity/mlModelGroup/MLModelGroupEntity.tsx b/datahub-web-react/src/app/entity/mlModelGroup/MLModelGroupEntity.tsx deleted file mode 100644 index d3448c96e3a558..00000000000000 --- a/datahub-web-react/src/app/entity/mlModelGroup/MLModelGroupEntity.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import { CodeSandboxOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { Preview } from '@app/entity/mlModelGroup/preview/Preview'; -import ModelGroupModels from '@app/entity/mlModelGroup/profile/ModelGroupModels'; -import { EntityMenuItems } from '@app/entity/shared/EntityDropdown/EntityDropdown'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import DataProductSection from '@app/entity/shared/containers/profile/sidebar/DataProduct/DataProductSection'; -import { SidebarDomainSection } from '@app/entity/shared/containers/profile/sidebar/Domain/SidebarDomainSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import { SidebarTagsSection } from '@app/entity/shared/containers/profile/sidebar/SidebarTagsSection'; -import SidebarStructuredPropsSection from '@app/entity/shared/containers/profile/sidebar/StructuredProperties/SidebarStructuredPropsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { DocumentationTab } from '@app/entity/shared/tabs/Documentation/DocumentationTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; -import { GenericEntityProperties } from '@app/entity/shared/types'; - -import { useGetMlModelGroupQuery } from '@graphql/mlModelGroup.generated'; -import { EntityType, MlModelGroup, OwnershipType, SearchResult } from '@types'; - -/** - * Definition of the DataHub MlModelGroup entity. - */ -export class MLModelGroupEntity implements Entity { - type: EntityType = EntityType.MlmodelGroup; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => true; - - isLineageEnabled = () => true; - - getAutoCompleteFieldName = () => 'name'; - - getGraphName = () => 'mlModelGroup'; - - getPathName = () => 'mlModelGroup'; - - getEntityName = () => 'ML Group'; - - getCollectionName = () => 'ML Groups'; - - getOverridePropertiesFromEntity = (_?: MlModelGroup | null): GenericEntityProperties => { - return {}; - }; - - useEntityQuery = useGetMlModelGroupQuery; - - getSidebarSections = () => [ - { - component: SidebarAboutSection, - }, - { - component: SidebarOwnerSection, - properties: { - defaultOwnerType: OwnershipType.TechnicalOwner, - }, - }, - { - component: SidebarTagsSection, - properties: { - hasTags: true, - hasTerms: true, - }, - }, - { - component: SidebarDomainSection, - }, - { - component: DataProductSection, - }, - { - component: SidebarStructuredPropsSection, - }, - ]; - - renderProfile = (urn: string) => ( - - ); - - renderPreview = (_: PreviewType, data: MlModelGroup) => { - return ; - }; - - renderSearch = (result: SearchResult) => { - const data = result.entity as MlModelGroup; - return ; - }; - - getLineageVizConfig = (entity: MlModelGroup) => { - return { - urn: entity.urn, - // eslint-disable-next-line @typescript-eslint/dot-notation - name: entity.properties?.['propertiesName'] || entity.name, - type: EntityType.MlmodelGroup, - icon: entity.platform?.properties?.logoUrl || undefined, - platform: entity.platform, - }; - }; - - displayName = (data: MlModelGroup) => { - return data.properties?.name || data.name || data.urn; - }; - - getGenericEntityProperties = (mlModelGroup: MlModelGroup) => { - return getDataForEntityType({ - data: mlModelGroup, - entityType: this.type, - getOverrideProperties: (data) => data, - }); - }; - - supportedCapabilities = () => { - return new Set([ - EntityCapabilityType.OWNERS, - EntityCapabilityType.GLOSSARY_TERMS, - EntityCapabilityType.TAGS, - EntityCapabilityType.DOMAINS, - EntityCapabilityType.DEPRECATION, - EntityCapabilityType.SOFT_DELETE, - EntityCapabilityType.DATA_PRODUCTS, - ]); - }; -} diff --git a/datahub-web-react/src/app/entity/mlModelGroup/preview/Preview.tsx b/datahub-web-react/src/app/entity/mlModelGroup/preview/Preview.tsx deleted file mode 100644 index fb7cb6587fa344..00000000000000 --- a/datahub-web-react/src/app/entity/mlModelGroup/preview/Preview.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; - -import { getDataProduct } from '@app/entity/shared/utils'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { capitalizeFirstLetterOnly } from '@app/shared/textUtil'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { EntityPath, EntityType, MlModelGroup } from '@types'; - -export const Preview = ({ - group, - degree, - paths, -}: { - group: MlModelGroup; - degree?: number; - paths?: EntityPath[]; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - const genericProperties = entityRegistry.getGenericEntityProperties(EntityType.MlmodelGroup, group); - return ( - - ); -}; diff --git a/datahub-web-react/src/app/entity/mlModelGroup/profile/ModelGroupModels.tsx b/datahub-web-react/src/app/entity/mlModelGroup/profile/ModelGroupModels.tsx deleted file mode 100644 index 22a2f93ed2ff6c..00000000000000 --- a/datahub-web-react/src/app/entity/mlModelGroup/profile/ModelGroupModels.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import { Table, Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -import { useBaseEntity } from '@app/entity/shared/EntityContext'; -import { EmptyTab } from '@app/entity/shared/components/styled/EmptyTab'; -import { InfoItem } from '@app/entity/shared/components/styled/InfoItem'; -import { notEmpty } from '@app/entity/shared/utils'; -import { TimestampPopover } from '@app/sharedV2/TimestampPopover'; -import { useEntityRegistry } from '@app/useEntityRegistry'; -import { Pill } from '@src/alchemy-components/components/Pills'; -import { colors } from '@src/alchemy-components/theme'; - -import { GetMlModelGroupQuery } from '@graphql/mlModelGroup.generated'; -import { EntityType } from '@types'; - -const InfoItemContainer = styled.div<{ justifyContent }>` - display: flex; - position: relative; - justify-content: ${(props) => props.justifyContent}; - padding: 12px 2px 20px 2px; -`; - -const InfoItemContent = styled.div` - padding-top: 8px; - width: 100px; -`; - -const NameContainer = styled.div` - display: flex; - align-items: center; -`; - -const NameLink = styled.a` - font-weight: 700; - color: inherit; - font-size: 0.9rem; - - &:hover { - color: ${colors.blue[400]} !important; - } -`; - -const TagContainer = styled.div` - display: inline-flex; - margin-left: 0px; - margin-top: 3px; - flex-wrap: wrap; - margin-right: 8px; - backgroundcolor: white; - gap: 5px; -`; - -const StyledTable = styled(Table)` - &&& .ant-table-cell { - padding: 16px; - } -` as typeof Table; - -const ModelsContainer = styled.div` - width: 100%; - padding: 20px; -`; - -const VersionContainer = styled.div` - display: flex; - align-items: center; -`; - -export default function MLGroupModels() { - const baseEntity = useBaseEntity(); - const entityRegistry = useEntityRegistry(); - const modelGroup = baseEntity?.mlModelGroup; - - const models = - baseEntity?.mlModelGroup?.incoming?.relationships - ?.map((relationship) => relationship.entity) - .filter(notEmpty) || []; - - const columns = [ - { - title: 'Name', - dataIndex: 'name', - key: 'name', - width: 300, - render: (_: any, record) => ( - - - {record?.properties?.propertiesName || record?.name} - - - ), - }, - { - title: 'Version', - key: 'version', - width: 70, - render: (_: any, record: any) => ( - {record.versionProperties?.version?.versionTag || '-'} - ), - }, - { - title: 'Created At', - key: 'createdAt', - width: 150, - render: (_: any, record: any) => ( - - ), - }, - { - title: 'Aliases', - key: 'aliases', - width: 200, - render: (_: any, record: any) => { - const aliases = record.versionProperties?.aliases || []; - - return ( - - {aliases.map((alias) => ( - - ))} - - ); - }, - }, - { - title: 'Properties', - key: 'properties', - width: 200, - render: (_: any, record: any) => { - const tags = record.properties?.tags || []; - - return ( - - {tags.map((tag) => ( - - ))} - - ); - }, - }, - { - title: 'Description', - dataIndex: 'description', - key: 'description', - width: 300, - render: (_: any, record: any) => { - const editableDesc = record.editableProperties?.description; - const originalDesc = record.description; - - return {editableDesc || originalDesc || '-'}; - }, - }, - ]; - - return ( - - Model Group Details - - - - - - - - {modelGroup?.properties?.created?.actor && ( - - {modelGroup.properties.created?.actor} - - )} - - Models - , - }} - /> - - ); -} diff --git a/datahub-web-react/src/app/entity/mlPrimaryKey/MLPrimaryKeyEntity.tsx b/datahub-web-react/src/app/entity/mlPrimaryKey/MLPrimaryKeyEntity.tsx deleted file mode 100644 index 2ca67e208e0fbb..00000000000000 --- a/datahub-web-react/src/app/entity/mlPrimaryKey/MLPrimaryKeyEntity.tsx +++ /dev/null @@ -1,204 +0,0 @@ -import { DotChartOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { Preview } from '@app/entity/mlPrimaryKey/preview/Preview'; -import { EntityProfile } from '@app/entity/shared/containers/profile/EntityProfile'; -import { SidebarAboutSection } from '@app/entity/shared/containers/profile/sidebar/AboutSection/SidebarAboutSection'; -import DataProductSection from '@app/entity/shared/containers/profile/sidebar/DataProduct/DataProductSection'; -import { SidebarDomainSection } from '@app/entity/shared/containers/profile/sidebar/Domain/SidebarDomainSection'; -import { SidebarOwnerSection } from '@app/entity/shared/containers/profile/sidebar/Ownership/sidebar/SidebarOwnerSection'; -import { SidebarTagsSection } from '@app/entity/shared/containers/profile/sidebar/SidebarTagsSection'; -import SidebarStructuredPropsSection from '@app/entity/shared/containers/profile/sidebar/StructuredProperties/SidebarStructuredPropsSection'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { DocumentationTab } from '@app/entity/shared/tabs/Documentation/DocumentationTab'; -import { LineageTab } from '@app/entity/shared/tabs/Lineage/LineageTab'; -import { FeatureTableTab } from '@app/entity/shared/tabs/ML/MlPrimaryKeyFeatureTableTab'; -import { PropertiesTab } from '@app/entity/shared/tabs/Properties/PropertiesTab'; -import { GenericEntityProperties } from '@app/entity/shared/types'; -import { getDataProduct } from '@app/entity/shared/utils'; - -import { useGetMlPrimaryKeyQuery } from '@graphql/mlPrimaryKey.generated'; -import { EntityType, MlPrimaryKey, OwnershipType, SearchResult } from '@types'; - -/** - * Definition of the DataHub MLPrimaryKey entity. - */ -export class MLPrimaryKeyEntity implements Entity { - type: EntityType = EntityType.MlprimaryKey; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - return ( - - ); - }; - - isSearchEnabled = () => true; - - isBrowseEnabled = () => false; - - isLineageEnabled = () => true; - - getAutoCompleteFieldName = () => 'name'; - - getGraphName = () => 'mlPrimaryKey'; - - getPathName = () => 'mlPrimaryKeys'; - - getEntityName = () => 'ML Primary Key'; - - getCollectionName = () => 'ML Primary Keys'; - - getOverridePropertiesFromEntity = (key?: MlPrimaryKey | null): GenericEntityProperties => { - return { - // eslint-disable-next-line - platform: key?.['featureTables']?.relationships?.[0]?.entity?.platform, - }; - }; - - useEntityQuery = useGetMlPrimaryKeyQuery; - - renderProfile = (urn: string) => ( - - ); - - getSidebarSections = () => [ - { - component: SidebarAboutSection, - }, - { - component: SidebarOwnerSection, - properties: { - defaultOwnerType: OwnershipType.TechnicalOwner, - }, - }, - { - component: SidebarTagsSection, - properties: { - hasTags: true, - hasTerms: true, - }, - }, - { - component: SidebarDomainSection, - }, - { - component: DataProductSection, - }, - { - component: SidebarStructuredPropsSection, - }, - ]; - - renderPreview = (_: PreviewType, data: MlPrimaryKey) => { - const genericProperties = this.getGenericEntityProperties(data); - // eslint-disable-next-line - const platform = data?.['featureTables']?.relationships?.[0]?.entity?.platform; - return ( - - ); - }; - - renderSearch = (result: SearchResult) => { - const data = result.entity as MlPrimaryKey; - const genericProperties = this.getGenericEntityProperties(data); - // eslint-disable-next-line - const platform = data?.['featureTables']?.relationships?.[0]?.entity?.platform; - return ( - - ); - }; - - displayName = (data: MlPrimaryKey) => { - return data.name || data.urn; - }; - - getGenericEntityProperties = (mlPrimaryKey: MlPrimaryKey) => { - return getDataForEntityType({ - data: mlPrimaryKey, - entityType: this.type, - getOverrideProperties: this.getOverridePropertiesFromEntity, - }); - }; - - getLineageVizConfig = (entity: MlPrimaryKey) => { - return { - urn: entity.urn, - name: entity.name, - type: EntityType.MlprimaryKey, - // eslint-disable-next-line - icon: entity?.['featureTables']?.relationships?.[0]?.entity?.platform?.properties?.logoUrl || undefined, - // eslint-disable-next-line - platform: entity?.['featureTables']?.relationships?.[0]?.entity?.platform?.name, - }; - }; - - supportedCapabilities = () => { - return new Set([ - EntityCapabilityType.OWNERS, - EntityCapabilityType.GLOSSARY_TERMS, - EntityCapabilityType.TAGS, - EntityCapabilityType.DOMAINS, - EntityCapabilityType.DEPRECATION, - EntityCapabilityType.SOFT_DELETE, - EntityCapabilityType.DATA_PRODUCTS, - ]); - }; -} diff --git a/datahub-web-react/src/app/entity/mlPrimaryKey/preview/Preview.tsx b/datahub-web-react/src/app/entity/mlPrimaryKey/preview/Preview.tsx deleted file mode 100644 index 70fb631b2f5c8e..00000000000000 --- a/datahub-web-react/src/app/entity/mlPrimaryKey/preview/Preview.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; - -import { IconStyleType } from '@app/entity/Entity'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { capitalizeFirstLetterOnly } from '@app/shared/textUtil'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { DataPlatform, DataProduct, EntityPath, EntityType, Owner } from '@types'; - -export const Preview = ({ - urn, - name, - featureNamespace, - description, - owners, - platform, - dataProduct, - platformInstanceId, - degree, - paths, -}: { - urn: string; - name: string; - featureNamespace: string; - description?: string | null; - owners?: Array | null; - platform?: DataPlatform | null | undefined; - dataProduct?: DataProduct | null; - platformInstanceId?: string; - degree?: number; - paths?: EntityPath[]; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - return ( - - ); -}; diff --git a/datahub-web-react/src/app/entity/ownership/ManageOwnership.tsx b/datahub-web-react/src/app/entity/ownership/ManageOwnership.tsx deleted file mode 100644 index ccc27e948c8e09..00000000000000 --- a/datahub-web-react/src/app/entity/ownership/ManageOwnership.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components/macro'; - -import { OwnershipList } from '@app/entity/ownership/OwnershipList'; - -const PageContainer = styled.div` - padding-top: 20px; - width: 100%; - display: flex; - flex-direction: column; - overflow: auto; -`; - -const PageHeaderContainer = styled.div` - && { - padding-left: 24px; - } -`; - -const PageTitle = styled(Typography.Title)` - && { - margin-bottom: 12px; - } -`; - -const ListContainer = styled.div` - display: flex; - flex-direction: column; - overflow: auto; -`; - -/** - * Component used for displaying the 'Manage Ownership' experience. - */ -export const ManageOwnership = () => { - return ( - - - Manage Ownership - - Create, edit, and remove custom Ownership Types. - - - - - - - ); -}; diff --git a/datahub-web-react/src/app/entity/ownership/OwnershipBuilderModal.tsx b/datahub-web-react/src/app/entity/ownership/OwnershipBuilderModal.tsx deleted file mode 100644 index 42bf0da7b57d14..00000000000000 --- a/datahub-web-react/src/app/entity/ownership/OwnershipBuilderModal.tsx +++ /dev/null @@ -1,227 +0,0 @@ -import { Button, Form, Input, Modal, Typography, message, notification } from 'antd'; -import React, { useEffect, useState } from 'react'; -import styled from 'styled-components/macro'; - -import { OwnershipTypeBuilderState } from '@app/entity/ownership/table/types'; - -import { useCreateOwnershipTypeMutation, useUpdateOwnershipTypeMutation } from '@graphql/ownership.generated'; -import { OwnershipTypeEntity } from '@types'; - -const NAME_INPUT_TEST_ID = 'ownership-type-name-input'; -const DESCRIPTION_INPUT_TEST_ID = 'ownership-type-description-input'; - -const TitleContainer = styled.div` - display: flex; - justify-content: space-between; -`; - -const TitleText = styled(Typography.Text)` - font-size: 16px; - font-weight: 700; -`; - -const FormItemContainer = styled.div` - display: flex; - flex-direction: column; -`; - -const FormItemTitle = styled(Typography.Text)` - margin-bottom: 8px; - font-weight: 700; -`; - -const StyledFormItem = styled(Form.Item)` - margin-bottom: 8px; -`; - -const SaveButtonContainer = styled.div` - width: 100%; - display: flex; - justify-content: right; -`; - -const CancelButton = styled(Button)` - margin-right: 12px; -`; - -type Props = { - isOpen: boolean; - onClose: () => void; - refetch: () => void; - ownershipType?: OwnershipTypeEntity; -}; - -export const OwnershipBuilderModal = ({ isOpen, onClose, refetch, ownershipType }: Props) => { - // State - const [ownershipTypeBuilderState, setOwnershipTypeBuilderState] = useState({ - name: ownershipType?.info?.name || ownershipType?.urn || '', - description: ownershipType?.info?.description || '', - }); - const setName = (name: string) => { - setOwnershipTypeBuilderState({ ...ownershipTypeBuilderState, name }); - }; - const setDescription = (description: string) => { - setOwnershipTypeBuilderState({ ...ownershipTypeBuilderState, description }); - }; - const [form] = Form.useForm(); - form.setFieldsValue(ownershipTypeBuilderState); - - // Side effects - useEffect(() => { - if (ownershipType) { - const ownershipTypeName = ownershipType?.info?.name || ownershipType?.urn; - const ownershipTypeDescription = ownershipType?.info?.description || ''; - setOwnershipTypeBuilderState({ - name: ownershipTypeName, - description: ownershipTypeDescription, - }); - } else { - setOwnershipTypeBuilderState({ - name: '', - description: '', - }); - } - }, [ownershipType]); - - // Queries - const [createOwnershipTypeMutation] = useCreateOwnershipTypeMutation(); - const [updateOwnershipTypeMutation] = useUpdateOwnershipTypeMutation(); - - const onCreateOwnershipType = () => { - if (ownershipTypeBuilderState.name) { - createOwnershipTypeMutation({ - variables: { - input: { - name: ownershipTypeBuilderState.name, - description: ownershipTypeBuilderState.description, - }, - }, - }) - .then(() => { - setName(''); - setDescription(''); - onClose(); - notification.success({ - message: `Success`, - description: 'Successfully created ownership type.', - placement: 'bottomLeft', - duration: 3, - }); - setTimeout(() => { - refetch(); - }, 3000); - }) - .catch((e: unknown) => { - message.destroy(); - if (e instanceof Error) { - message.error({ - content: `Failed to create ownership type`, - duration: 3, - }); - } - }); - } - }; - - const onUpdateOwnershipType = () => { - if (ownershipType) { - updateOwnershipTypeMutation({ - variables: { - urn: ownershipType?.urn || '', - input: { - name: ownershipTypeBuilderState.name, - description: ownershipTypeBuilderState.description, - }, - }, - }) - .then(() => { - setName(''); - setDescription(''); - onClose(); - notification.success({ - message: `Success`, - description: 'Successfully updated ownership type.', - placement: 'bottomLeft', - duration: 3, - }); - setTimeout(() => { - refetch(); - }, 3000); - }) - .catch((e: unknown) => { - message.destroy(); - if (e instanceof Error) { - message.error({ - content: `Failed to update ownership type`, - duration: 3, - }); - } - }); - } - }; - - const onUpsert = ownershipType ? onUpdateOwnershipType : onCreateOwnershipType; - const titleText = ownershipType ? 'Edit ownership type' : 'Add a new ownership type'; - return ( - - {titleText} - - } - footer={null} - > -
- - Name - - { - setName(e.target.value); - }} - /> - - - - Description - - { - setDescription(e.target.value); - }} - /> - - - - - - Cancel - - - -
- ); -}; diff --git a/datahub-web-react/src/app/entity/ownership/OwnershipList.tsx b/datahub-web-react/src/app/entity/ownership/OwnershipList.tsx deleted file mode 100644 index 617ed303d6b248..00000000000000 --- a/datahub-web-react/src/app/entity/ownership/OwnershipList.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import { PlusOutlined } from '@ant-design/icons'; -import { Button, Pagination, message } from 'antd'; -import React, { useState } from 'react'; -import styled from 'styled-components/macro'; - -import { OwnershipBuilderModal } from '@app/entity/ownership/OwnershipBuilderModal'; -import { OwnershipTable } from '@app/entity/ownership/table/OwnershipTable'; -import TabToolbar from '@app/entity/shared/components/styled/TabToolbar'; -import { SearchBar } from '@app/search/SearchBar'; -import { Message } from '@app/shared/Message'; -import { scrollToTop } from '@app/shared/searchUtils'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { useListOwnershipTypesQuery } from '@graphql/ownership.generated'; -import { OwnershipTypeEntity } from '@types'; - -const PaginationContainer = styled.div` - display: flex; - justify-content: center; -`; - -const StyledPagination = styled(Pagination)` - margin: 40px; -`; - -const searchBarStyle = { - maxWidth: 220, - padding: 0, -}; - -const searchBarInputStyle = { - height: 32, - fontSize: 12, -}; - -/** - * This component renders a paginated, searchable list of Ownership Types. - */ -export const OwnershipList = () => { - /** - * Context - */ - const entityRegistry = useEntityRegistry(); - - /** - * State - */ - const [page, setPage] = useState(1); - const [showOwnershipBuilder, setShowOwnershipBuilder] = useState(false); - const [ownershipType, setOwnershipType] = useState(undefined); - const [query, setQuery] = useState(undefined); - - /** - * Queries - */ - const pageSize = 10; - const start: number = (page - 1) * pageSize; - const { data, loading, error, refetch } = useListOwnershipTypesQuery({ - variables: { - input: { - start, - count: pageSize, - query, - }, - }, - }); - const totalOwnershipTypes = data?.listOwnershipTypes?.total || 0; - const ownershipTypes = - data?.listOwnershipTypes?.ownershipTypes?.filter((type) => type.urn !== 'urn:li:ownershipType:none') || []; - - const onClickCreateOwnershipType = () => { - setShowOwnershipBuilder(true); - }; - - const onCloseModal = () => { - setShowOwnershipBuilder(false); - setOwnershipType(undefined); - }; - - const onChangePage = (newPage: number) => { - scrollToTop(); - setPage(newPage); - }; - - return ( - <> - {!data && loading && } - {error && - message.error({ - content: `Failed to load Ownership Types! An unexpected error occurred.`, - duration: 3, - })} - - - null} - onQueryChange={(q) => setQuery(q.length > 0 ? q : undefined)} - entityRegistry={entityRegistry} - /> - - - {totalOwnershipTypes >= pageSize && ( - - - - )} - - - ); -}; diff --git a/datahub-web-react/src/app/entity/ownership/table/ActionsColumn.tsx b/datahub-web-react/src/app/entity/ownership/table/ActionsColumn.tsx deleted file mode 100644 index 31613811691d96..00000000000000 --- a/datahub-web-react/src/app/entity/ownership/table/ActionsColumn.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import { CopyOutlined, DeleteOutlined, EditOutlined, MoreOutlined } from '@ant-design/icons'; -import { Dropdown, MenuProps, Popconfirm, Typography, message, notification } from 'antd'; -import React from 'react'; -import styled from 'styled-components/macro'; - -import { useDeleteOwnershipTypeMutation } from '@graphql/ownership.generated'; -import { OwnershipTypeEntity } from '@types'; - -const DROPDOWN_TEST_ID = 'ownership-table-dropdown'; -const EDIT_OWNERSHIP_TYPE_TEST_ID = 'edit-ownership-type'; -const DELETE_OWNERSHIP_TYPE_TEST_ID = 'delete-ownership-type'; - -const StyledDropdown = styled(Dropdown)``; - -const MenuButtonContainer = styled.div` - display: flex; - justify-content: center; - align-items: center; -`; - -const MenuButtonText = styled(Typography.Text)` - font-size: 14px; - font-weight: 400; - margin-left: 8px; -`; - -const StyledMoreOutlined = styled(MoreOutlined)` - width: 20px; - &&& { - padding-left: 0px; - padding-right: 0px; - font-size: 18px; - } - :hover { - cursor: pointer; - } -`; - -type Props = { - ownershipType: OwnershipTypeEntity; - setIsOpen: (isOpen: boolean) => void; - setOwnershipType: (ownershipType: OwnershipTypeEntity) => void; - refetch: () => void; -}; - -export const ActionsColumn = ({ ownershipType, setIsOpen, setOwnershipType, refetch }: Props) => { - const editOnClick = () => { - setIsOpen(true); - setOwnershipType(ownershipType); - }; - - const onCopy = () => { - navigator.clipboard.writeText(ownershipType.urn); - }; - - const [deleteOwnershipTypeMutation] = useDeleteOwnershipTypeMutation(); - - const onDelete = () => { - deleteOwnershipTypeMutation({ - variables: { - urn: ownershipType.urn, - }, - }) - .then(() => { - notification.success({ - message: `Success`, - description: 'You have deleted an ownership type.', - placement: 'bottomLeft', - duration: 3, - }); - setTimeout(() => { - refetch(); - }, 3000); - }) - .catch((e: unknown) => { - message.destroy(); - if (e instanceof Error) { - message.error({ - content: `Failed to delete an ownership type`, - duration: 3, - }); - } - }); - }; - - const items: MenuProps['items'] = [ - { - key: 'edit', - icon: ( - - - Edit - - ), - }, - { - key: 'delete', - icon: ( - Are you sure you want to delete this ownership type?} - placement="left" - onCancel={() => {}} - onConfirm={onDelete} - okText="Yes" - cancelText="No" - > - - - Delete - - - ), - }, - { - key: 'copy', - icon: ( - - - Copy Urn - - ), - }, - ]; - - const onClick: MenuProps['onClick'] = (e) => { - const key = e.key as string; - if (key === 'edit') { - editOnClick(); - } else if (key === 'copy') { - onCopy(); - } - }; - - const menuProps: MenuProps = { - items, - onClick, - }; - - return ( - - - - ); -}; diff --git a/datahub-web-react/src/app/entity/ownership/table/DescriptionColumn.tsx b/datahub-web-react/src/app/entity/ownership/table/DescriptionColumn.tsx deleted file mode 100644 index e53c37b778c132..00000000000000 --- a/datahub-web-react/src/app/entity/ownership/table/DescriptionColumn.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components/macro'; - -import { OwnershipTypeEntity } from '@types'; - -const DescriptionText = styled(Typography.Text)` - font-size: 14px; - font-weight: 400; -`; - -type Props = { - ownershipType: OwnershipTypeEntity; -}; - -export const DescriptionColumn = ({ ownershipType }: Props) => { - const description = ownershipType?.info?.description || ''; - - return {description}; -}; diff --git a/datahub-web-react/src/app/entity/ownership/table/NameColumn.tsx b/datahub-web-react/src/app/entity/ownership/table/NameColumn.tsx deleted file mode 100644 index 4358dac1557d53..00000000000000 --- a/datahub-web-react/src/app/entity/ownership/table/NameColumn.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { Typography } from 'antd'; -import React from 'react'; -import styled from 'styled-components/macro'; - -import { OwnershipTypeEntity } from '@types'; - -const NameText = styled(Typography.Text)` - font-size: 14px; - font-weight: 700; -`; - -type Props = { - ownershipType: OwnershipTypeEntity; -}; - -export const NameColumn = ({ ownershipType }: Props) => { - const name = ownershipType?.info?.name || ownershipType?.urn; - - return {name}; -}; diff --git a/datahub-web-react/src/app/entity/ownership/table/OwnershipTable.tsx b/datahub-web-react/src/app/entity/ownership/table/OwnershipTable.tsx deleted file mode 100644 index 9430899f9e8d71..00000000000000 --- a/datahub-web-react/src/app/entity/ownership/table/OwnershipTable.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { Empty } from 'antd'; -import React from 'react'; - -import { ActionsColumn } from '@app/entity/ownership/table/ActionsColumn'; -import { DescriptionColumn } from '@app/entity/ownership/table/DescriptionColumn'; -import { NameColumn } from '@app/entity/ownership/table/NameColumn'; -import { StyledTable } from '@app/entity/shared/components/styled/StyledTable'; - -import { OwnershipTypeEntity } from '@types'; - -type Props = { - ownershipTypes: OwnershipTypeEntity[]; - setIsOpen: (isOpen: boolean) => void; - setOwnershipType: (ownershipType: OwnershipTypeEntity) => void; - refetch: () => void; -}; - -export const OwnershipTable = ({ ownershipTypes, setIsOpen, setOwnershipType, refetch }: Props) => { - const tableColumns = [ - { - title: 'Name', - dataIndex: 'name', - sorter: (a: any, b: any) => a?.info?.name?.localeCompare(b?.info?.name), - key: 'name', - render: (_, record: any) => , - }, - { - title: 'Description', - dataIndex: 'description', - key: 'description', - render: (_, record: any) => , - }, - { - dataIndex: 'actions', - key: 'actions', - render: (_, record: any) => ( - - ), - }, - ]; - - const getRowKey = (ownershipType: OwnershipTypeEntity) => { - return ownershipType?.info?.name || ownershipType.urn; - }; - - return ( - , - }} - pagination={false} - /> - ); -}; diff --git a/datahub-web-react/src/app/entity/ownership/table/types.ts b/datahub-web-react/src/app/entity/ownership/table/types.ts deleted file mode 100644 index 6d7f9bdcf760b7..00000000000000 --- a/datahub-web-react/src/app/entity/ownership/table/types.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * The object represents the state of the Ownership Type Builder form. - */ -export interface OwnershipTypeBuilderState { - /** - * The name of the Ownership Type. - */ - name: string; - - /** - * The description of the Ownership Type. - */ - description: string; -} diff --git a/datahub-web-react/src/app/entity/query/QueryEntity.tsx b/datahub-web-react/src/app/entity/query/QueryEntity.tsx deleted file mode 100644 index 33b1069d40dec6..00000000000000 --- a/datahub-web-react/src/app/entity/query/QueryEntity.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { ConsoleSqlOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, IconStyleType } from '@app/entity/Entity'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { GenericEntityProperties } from '@app/entity/shared/types'; -import { TYPE_ICON_CLASS_NAME } from '@app/entityV2/shared/components/subtypes'; - -import { useGetQueryQuery } from '@graphql/query.generated'; -import { DataPlatform, EntityType, QueryEntity as Query } from '@types'; - -/** - * Definition of the DataHub DataPlatformInstance entity. - * Most of this still needs to be filled out. - */ -export class QueryEntity implements Entity { - type: EntityType = EntityType.Query; - - icon = (fontSize?: number, _styleType?: IconStyleType, color?: string) => { - return ( - - ); - }; - - isSearchEnabled = () => false; - - isBrowseEnabled = () => false; - - isLineageEnabled = () => false; - - getAutoCompleteFieldName = () => 'name'; - - getPathName = () => 'query'; - - getEntityName = () => 'Query'; - - getCollectionName = () => 'Queries'; - - useEntityQuery = useGetQueryQuery; - - renderProfile = (_urn: string) => { - return <>; - }; - - getOverridePropertiesFromEntity = (query?: Query | null): GenericEntityProperties => { - return { - name: query && this.displayName(query), - platform: query?.platform, - }; - }; - - renderEmbeddedProfile = (_: string) => <>; - - renderPreview = () => { - return <>; - }; - - renderSearch = () => { - return <>; - }; - - getLineageVizConfig = (query: Query) => { - // TODO: Set up types better here - const platform: DataPlatform | undefined = (query as any)?.queryPlatform; - return { - urn: query.urn, - name: query.properties?.name || query.urn, - type: EntityType.Query, - icon: platform?.properties?.logoUrl || undefined, - platform: platform || undefined, - }; - }; - - displayName = (data: Query) => { - return data?.properties?.name || (data?.properties?.source === 'SYSTEM' && 'System Query') || data?.urn; - }; - - getGenericEntityProperties = (data: Query) => { - return getDataForEntityType({ - data, - entityType: this.type, - getOverrideProperties: this.getOverridePropertiesFromEntity, - }); - }; - - supportedCapabilities = () => { - return new Set([]); - }; - - getGraphName = () => { - return 'query'; - }; -} diff --git a/datahub-web-react/src/app/entity/restricted/RestrictedEntity.tsx b/datahub-web-react/src/app/entity/restricted/RestrictedEntity.tsx deleted file mode 100644 index 348fe0fddd4dbf..00000000000000 --- a/datahub-web-react/src/app/entity/restricted/RestrictedEntity.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { QuestionOutlined } from '@ant-design/icons'; -import React from 'react'; - -import { Entity, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { RestrictedEntityProfile } from '@app/entity/restricted/RestrictedEntityProfile'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; - -import { EntityType, Restricted, SearchResult } from '@types'; - -import RestrictedIcon from '@images/restricted.svg'; - -/** - * Definition of the DataHub Data Product entity. - */ -export class RestrictedEntity implements Entity { - type: EntityType = EntityType.Restricted; - - icon = (fontSize: number, styleType: IconStyleType, color?: string) => { - if (styleType === IconStyleType.TAB_VIEW) { - return ; - } - - if (styleType === IconStyleType.HIGHLIGHT) { - return ; - } - - return ( - - ); - }; - - isSearchEnabled = () => false; - - isBrowseEnabled = () => false; - - isLineageEnabled = () => true; - - getAutoCompleteFieldName = () => 'name'; - - getPathName = () => 'restricted'; - - getEntityName = () => 'Restricted'; - - getCollectionName = () => 'Restricted Assets'; - - renderProfile = (_: string) => ; - - renderPreview = (_: PreviewType, _data: Restricted) => { - return ; - }; - - renderSearch = (_result: SearchResult) => { - return ; - }; - - getLineageVizConfig = (entity: Restricted) => { - return { - urn: entity?.urn, - name: 'Restricted Asset', - type: EntityType.Restricted, - icon: RestrictedIcon, - }; - }; - - displayName = (_data: Restricted) => { - return 'Restricted Asset'; - }; - - getOverridePropertiesFromEntity = (_data: Restricted) => { - return {}; - }; - - getGenericEntityProperties = (data: Restricted) => { - return getDataForEntityType({ - data, - entityType: this.type, - getOverrideProperties: this.getOverridePropertiesFromEntity, - }); - }; - - supportedCapabilities = () => { - return new Set([]); - }; - - getGraphName = () => { - return 'restricted'; - }; -} diff --git a/datahub-web-react/src/app/entity/restricted/RestrictedEntityProfile.tsx b/datahub-web-react/src/app/entity/restricted/RestrictedEntityProfile.tsx deleted file mode 100644 index 1c9d50afcab4a6..00000000000000 --- a/datahub-web-react/src/app/entity/restricted/RestrictedEntityProfile.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; -import styled from 'styled-components'; - -import { EntityTitle } from '@app/entity/shared/containers/profile/header/EntityName'; -import { - LogoIcon, - PlatformContentWrapper, - PlatformText, - PreviewImage, -} from '@app/entity/shared/containers/profile/header/PlatformContent/PlatformContentView'; - -import RestrictedIcon from '@images/restricted.svg'; - -const SubHeader = styled.div` - margin-top: 8px; - font-size: 14px; -`; - -export function RestrictedEntityProfile() { - return ( - <> - - - - - Restricted - - Restricted Asset - This asset is Restricted. Please request access to see more. - - ); -} diff --git a/datahub-web-react/src/app/entity/schemaField/SchemaFieldPropertiesEntity.tsx b/datahub-web-react/src/app/entity/schemaField/SchemaFieldPropertiesEntity.tsx deleted file mode 100644 index 0cc5c4969de4c2..00000000000000 --- a/datahub-web-react/src/app/entity/schemaField/SchemaFieldPropertiesEntity.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { PicCenterOutlined } from '@ant-design/icons'; -import * as React from 'react'; - -import { Entity, IconStyleType, PreviewType } from '@app/entity/Entity'; -import { Preview } from '@app/entity/schemaField/preview/Preview'; -import { getDataForEntityType } from '@app/entity/shared/containers/profile/utils'; -import { capitalizeFirstLetterOnly } from '@app/shared/textUtil'; - -import { Dataset, EntityType, SchemaFieldEntity, SearchResult } from '@types'; - -export class SchemaFieldPropertiesEntity implements Entity { - type: EntityType = EntityType.SchemaField; - - icon = (fontSize: number, styleType: IconStyleType, color = '#BFBFBF') => ( - - ); - - isSearchEnabled = () => true; - - isBrowseEnabled = () => false; - - isLineageEnabled = () => false; - - getParentDataset = (parent) => { - return { - urn: parent?.urn, - name: parent?.name, - type: parent?.type, - platform: parent?.platfrom, - properties: parent?.properties, - } as Dataset; - }; - - // Currently unused. - getAutoCompleteFieldName = () => 'schemaField'; - - // Currently unused. - getPathName = () => 'schemaField'; - - getEntityName = () => 'Column'; - - getCollectionName = () => 'Columns'; - - // Currently unused. - renderProfile = (_: string) => <>; - - getGraphName = () => 'schemaField'; - - renderPreview = (previewType: PreviewType, data: SchemaFieldEntity) => { - const parent = data.parent as Dataset; - return ( - - ); - }; - - renderSearch = (result: SearchResult) => this.renderPreview(PreviewType.SEARCH, result.entity as SchemaFieldEntity); - - displayName = (data: SchemaFieldEntity) => data?.fieldPath || data.urn; - - getGenericEntityProperties = (data: SchemaFieldEntity) => - getDataForEntityType({ data, entityType: this.type, getOverrideProperties: (newData) => newData }); - - supportedCapabilities = () => new Set([]); -} diff --git a/datahub-web-react/src/app/entity/schemaField/preview/Preview.tsx b/datahub-web-react/src/app/entity/schemaField/preview/Preview.tsx deleted file mode 100644 index 75e6ef31081ff2..00000000000000 --- a/datahub-web-react/src/app/entity/schemaField/preview/Preview.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { PicCenterOutlined } from '@ant-design/icons'; -import React from 'react'; - -import { IconStyleType, PreviewType } from '@app/entity/Entity'; -import DefaultPreviewCard from '@app/preview/DefaultPreviewCard'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { Dataset, EntityType, Owner, ParentContainersResult } from '@types'; - -export const Preview = ({ - datasetUrn, - name, - description, - owners, - previewType, - parentContainers, - platformName, - platformLogo, - platformInstanceId, - parentDataset, -}: { - datasetUrn: string; - name: string; - description?: string | null; - owners?: Array | null; - previewType: PreviewType; - parentContainers?: ParentContainersResult | null; - platformName?: string; - platformLogo?: string | null; - platformInstanceId?: string; - parentDataset?: Dataset; -}): JSX.Element => { - const entityRegistry = useEntityRegistry(); - - const url = `${entityRegistry.getEntityUrl(EntityType.Dataset, datasetUrn)}/${encodeURIComponent( - 'Schema', - )}?schemaFilter=${encodeURIComponent(name)}`; - - return ( - } - type="Column" - typeIcon={entityRegistry.getIcon(EntityType.SchemaField, 14, IconStyleType.ACCENT)} - logoUrl={platformLogo || ''} - platform={platformName} - platformInstanceId={platformInstanceId} - parentContainers={parentContainers} - parentDataset={parentDataset} - /> - ); -}; diff --git a/datahub-web-react/src/app/entity/shared/EntityContext.ts b/datahub-web-react/src/app/entity/shared/EntityContext.ts index b2bfe2aef3494a..d18f20699ed33d 100644 --- a/datahub-web-react/src/app/entity/shared/EntityContext.ts +++ b/datahub-web-react/src/app/entity/shared/EntityContext.ts @@ -19,7 +19,6 @@ export const EntityContext = React.createContext({ entityState: { shouldRefetchContents: false, setShouldRefetchContents: () => {} }, }); -export default EntityContext; export function useEntityContext() { return useContext(EntityContext); @@ -55,11 +54,6 @@ export const useRefetch = () => { return refetch; }; -export const useLineageData = () => { - const { lineage } = useContext(EntityContext); - return lineage; -}; - export const useMutationUrn = () => { const { urn, entityData } = useContext(EntityContext); const isHideSiblingMode = useIsSeparateSiblingsMode(); diff --git a/datahub-web-react/src/app/entity/shared/EntityDropdown/DomainParentSelect.tsx b/datahub-web-react/src/app/entity/shared/EntityDropdown/DomainParentSelect.tsx index 35b65666000049..02f9333ca3655a 100644 --- a/datahub-web-react/src/app/entity/shared/EntityDropdown/DomainParentSelect.tsx +++ b/datahub-web-react/src/app/entity/shared/EntityDropdown/DomainParentSelect.tsx @@ -21,7 +21,7 @@ const SearchResultContainer = styled.div` `; // filter out entity itself and its children -export function filterResultsForMove(entity: Domain, entityUrn: string) { +function filterResultsForMove(entity: Domain, entityUrn: string) { return ( entity.urn !== entityUrn && entity.__typename === 'Domain' && diff --git a/datahub-web-react/src/app/entity/shared/EntityDropdown/NodeParentSelect.tsx b/datahub-web-react/src/app/entity/shared/EntityDropdown/NodeParentSelect.tsx index d94e405468bc38..c5a7d112f29441 100644 --- a/datahub-web-react/src/app/entity/shared/EntityDropdown/NodeParentSelect.tsx +++ b/datahub-web-react/src/app/entity/shared/EntityDropdown/NodeParentSelect.tsx @@ -20,7 +20,7 @@ const SearchResultContainer = styled.div` `; // filter out entity itself and its children -export function filterResultsForMove(entity: GlossaryNode, entityUrn: string) { +function filterResultsForMove(entity: GlossaryNode, entityUrn: string) { return ( entity.urn !== entityUrn && entity.__typename === 'GlossaryNode' && diff --git a/datahub-web-react/src/app/entity/shared/EntityDropdown/__tests__/NodeParentSelect.test.tsx b/datahub-web-react/src/app/entity/shared/EntityDropdown/__tests__/NodeParentSelect.test.tsx deleted file mode 100644 index 07f32fa6f570a8..00000000000000 --- a/datahub-web-react/src/app/entity/shared/EntityDropdown/__tests__/NodeParentSelect.test.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { filterResultsForMove } from '@app/entity/shared/EntityDropdown/NodeParentSelect'; -import { glossaryNode1, glossaryNode2, glossaryNode3, glossaryNode4 } from '@src/Mocks'; - -describe('filterResultsForMove', () => { - it('should return true if the given node is different than given urn and the node is not a child of the given urn', () => { - const shouldKeep = filterResultsForMove(glossaryNode4, glossaryNode1.urn); - expect(shouldKeep).toBe(true); - }); - - it('should return false if the given node has the same urn as the given urn', () => { - const shouldKeep = filterResultsForMove(glossaryNode1, glossaryNode1.urn); - expect(shouldKeep).toBe(false); - }); - - it('should return false if the given node is a direct child of the given urn', () => { - const shouldKeep = filterResultsForMove(glossaryNode2, glossaryNode1.urn); - expect(shouldKeep).toBe(false); - }); - - it('should return false if the given node is a child of a child of the given urn', () => { - const shouldKeep = filterResultsForMove(glossaryNode3, glossaryNode1.urn); - expect(shouldKeep).toBe(false); - }); -}); diff --git a/datahub-web-react/src/app/entity/shared/EntityDropdown/useDeleteGlossaryEntity.tsx b/datahub-web-react/src/app/entity/shared/EntityDropdown/useDeleteGlossaryEntity.tsx deleted file mode 100644 index e08fccc3c830c4..00000000000000 --- a/datahub-web-react/src/app/entity/shared/EntityDropdown/useDeleteGlossaryEntity.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { Modal, message } from 'antd'; -import { useState } from 'react'; - -import { useEntityData } from '@app/entity/shared/EntityContext'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { useDeleteGlossaryEntityMutation } from '@graphql/glossary.generated'; - -function useDeleteGlossaryEntity() { - const [hasBeenDeleted, setHasBeenDeleted] = useState(false); - const { entityData, urn: entityDataUrn, entityType } = useEntityData(); - const entityRegistry = useEntityRegistry(); - - const [deleteGlossaryEntity] = useDeleteGlossaryEntityMutation(); - - function handleDeleteGlossaryEntity() { - deleteGlossaryEntity({ - variables: { - urn: entityDataUrn, - }, - }) - .catch((e) => { - message.destroy(); - message.error({ content: `Failed to delete: \n ${e.message || ''}`, duration: 3 }); - }) - .finally(() => { - message.loading({ - content: 'Deleting...', - duration: 2, - }); - setTimeout(() => { - setHasBeenDeleted(true); - message.success({ - content: `Deleted ${entityRegistry.getEntityName(entityType)}!`, - duration: 2, - }); - }, 2000); - }); - } - - function onDeleteEntity() { - Modal.confirm({ - title: `Delete ${entityRegistry.getDisplayName(entityType, entityData)}`, - content: `Are you sure you want to remove this ${entityRegistry.getEntityName(entityType)}?`, - onOk() { - handleDeleteGlossaryEntity(); - }, - onCancel() {}, - okText: 'Yes', - maskClosable: true, - closable: true, - }); - } - - return { onDeleteEntity, hasBeenDeleted }; -} - -export default useDeleteGlossaryEntity; diff --git a/datahub-web-react/src/app/entity/shared/EntityGroups.tsx b/datahub-web-react/src/app/entity/shared/EntityGroups.tsx deleted file mode 100644 index bc857048736dca..00000000000000 --- a/datahub-web-react/src/app/entity/shared/EntityGroups.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { Tag } from 'antd'; -import React from 'react'; -import { Link } from 'react-router-dom'; - -import { EmptyValue, GroupsSeeMoreText, Tags, TagsSection } from '@app/entity/shared/SidebarStyledComponents'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { EntityRelationship, EntityType } from '@types'; - -type Props = { - readMore: boolean; - setReadMore: (readMore: boolean) => void; - groupMemberRelationships: Array; -}; - -/** - * EntityGroups- to display the groups category in sidebar section - */ -export default function EntityGroups({ readMore, setReadMore, groupMemberRelationships }: Props) { - const entityRegistry = useEntityRegistry(); - - return ( - - {groupMemberRelationships?.length === 0 && } - {!readMore && - groupMemberRelationships?.slice(0, 2).map((item) => { - if (!item?.entity?.urn) return null; - const entityUrn = entityRegistry.getEntityUrl(EntityType.CorpGroup, item?.entity?.urn); - return ( - - - {entityRegistry.getDisplayName(EntityType.CorpGroup, item.entity)} - - - ); - })} - {readMore && - groupMemberRelationships?.length > 2 && - groupMemberRelationships?.map((item) => { - if (!item?.entity?.urn) return null; - const entityUrn = entityRegistry.getEntityUrl(EntityType.CorpGroup, item.entity.urn); - return ( - - - {entityRegistry.getDisplayName(EntityType.CorpGroup, item.entity)} - - - ); - })} - {!readMore && groupMemberRelationships?.length > 2 && ( - setReadMore(!readMore)}> - {`+${groupMemberRelationships?.length - 2} more`} - - )} - - ); -} diff --git a/datahub-web-react/src/app/entity/shared/EntitySearchInput/EntitySearchInput.tsx b/datahub-web-react/src/app/entity/shared/EntitySearchInput/EntitySearchInput.tsx deleted file mode 100644 index a1a06daec87128..00000000000000 --- a/datahub-web-react/src/app/entity/shared/EntitySearchInput/EntitySearchInput.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { Select, Tag, Tooltip } from 'antd'; -import React from 'react'; - -import { EntitySearchInputResult } from '@app/entity/shared/EntitySearchInput/EntitySearchInputResult'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -import { useGetSearchResultsForMultipleLazyQuery } from '@graphql/search.generated'; -import { EntityType } from '@types'; - -type Props = { - selectedUrns: string[]; - entityTypes: EntityType[]; - placeholder?: string; - mode?: 'multiple' | 'single'; - style?: any; - onChangeSelectedUrns: (newUrns: string[]) => void; -}; - -export const EntitySearchInput = ({ - selectedUrns, - entityTypes, - placeholder, - style, - mode, - onChangeSelectedUrns, -}: Props) => { - const entityRegistry = useEntityRegistry(); - const [searchResources, { data: resourcesSearchData }] = useGetSearchResultsForMultipleLazyQuery(); - const searchResults = resourcesSearchData?.searchAcrossEntities?.searchResults || []; - - const urnToSearchResultEntity = new Map(); - searchResults.forEach((result) => { - urnToSearchResultEntity[result.entity.urn] = { - urn: result.entity.urn, - type: result.entity.type, - displayName: entityRegistry.getDisplayName(result.entity.type, result.entity), - }; - }); - - const onSelect = (newUrn) => { - if (mode === 'single') { - onChangeSelectedUrns([newUrn]); - } else { - const newUrns = [...selectedUrns, newUrn]; - onChangeSelectedUrns(newUrns); - } - }; - - const onDeselect = (urn) => { - if (mode === 'single') { - onChangeSelectedUrns([]); - } else { - onChangeSelectedUrns(selectedUrns.filter((u) => u !== urn)); - } - }; - - const onSearch = (text: string) => { - searchResources({ - variables: { - input: { - types: entityTypes, - query: text, - start: 0, - count: 10, - }, - }, - }); - }; - - return ( - - ); -}; diff --git a/datahub-web-react/src/app/entity/shared/EntitySearchInput/EntitySearchInputResult.tsx b/datahub-web-react/src/app/entity/shared/EntitySearchInput/EntitySearchInputResult.tsx deleted file mode 100644 index 29bccfa4437556..00000000000000 --- a/datahub-web-react/src/app/entity/shared/EntitySearchInput/EntitySearchInputResult.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; -import styled from 'styled-components'; - -import { IconStyleType } from '@app/entity/Entity'; -import { useEntityRegistry } from '@app/useEntityRegistry'; - -type Props = { - entity: any; -}; - -const Container = styled.div` - display: flex; - justify-content: left; - align-items: center; - padding: 12px; -`; - -const IconContainer = styled.div` - margin-right: 8px; -`; - -export const EntitySearchInputResult = ({ entity }: Props) => { - const entityRegistry = useEntityRegistry(); - return ( - - {entityRegistry.getIcon(entity.type, 12, IconStyleType.ACCENT)} - {entityRegistry.getDisplayName(entity.type, entity)} - - ); -}; - -export default EntitySearchInputResult; diff --git a/datahub-web-react/src/app/entity/shared/ExternalUrlButton.tsx b/datahub-web-react/src/app/entity/shared/ExternalUrlButton.tsx deleted file mode 100644 index f806ae9f8e368f..00000000000000 --- a/datahub-web-react/src/app/entity/shared/ExternalUrlButton.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; - -import analytics, { EntityActionType, EventType } from '@app/analytics'; -import UrlButton from '@app/entity/shared/UrlButton'; - -import { EntityType } from '@types'; - -const GITHUB_LINK = 'github.com'; -const GITHUB = 'GitHub'; -const GITLAB_LINK = 'gitlab.'; -const GITLAB = 'GitLab'; - -interface Props { - externalUrl: string; - platformName?: string; - entityUrn: string; - entityType?: string; -} - -export default function ExternalUrlButton({ externalUrl, platformName, entityType, entityUrn }: Props) { - function sendAnalytics() { - analytics.event({ - type: EventType.EntityActionEvent, - actionType: EntityActionType.ClickExternalUrl, - entityType: entityType as EntityType, - entityUrn, - }); - } - - let displayedName = platformName; - if (externalUrl.toLocaleLowerCase().includes(GITHUB_LINK)) { - displayedName = GITHUB; - } else if (externalUrl.toLocaleLowerCase().includes(GITLAB_LINK)) { - displayedName = GITLAB; - } - - return ( - - {displayedName ? `View in ${displayedName}` : 'View link'} - - ); -} diff --git a/datahub-web-react/src/app/entity/shared/PreviewContext.tsx b/datahub-web-react/src/app/entity/shared/PreviewContext.tsx index f4b2bc34b1ea0c..e1024b8deec366 100644 --- a/datahub-web-react/src/app/entity/shared/PreviewContext.tsx +++ b/datahub-web-react/src/app/entity/shared/PreviewContext.tsx @@ -4,7 +4,3 @@ import { GenericEntityProperties } from '@app/entity/shared/types'; const PreviewContext = React.createContext(null); export default PreviewContext; - -export function usePreviewData() { - return React.useContext(PreviewContext); -} diff --git a/datahub-web-react/src/app/entity/shared/SidebarStyledComponents.tsx b/datahub-web-react/src/app/entity/shared/SidebarStyledComponents.tsx deleted file mode 100644 index 8d66b17b0b8d71..00000000000000 --- a/datahub-web-react/src/app/entity/shared/SidebarStyledComponents.tsx +++ /dev/null @@ -1,202 +0,0 @@ -import styled from 'styled-components'; - -/** - * Styled Components- Users and Groups Side bar component - * Description: The following styles are used for User and Groups UI for sidebar. - */ - -export const SideBar = styled.div` - padding: 0 0 0 17px; - text-align: center; - - font-style: normal; - font-weight: bold; - height: calc(100vh - 60px); - position: relative; - - &&& .ant-avatar.ant-avatar-icon { - font-size: 46px !important; - } - - .divider-infoSection { - margin: 18px 0px 18px 0; - } - .divider-aboutSection { - margin: 23px 0px 11px 0; - } - .divider-groupsSection { - margin: 23px 0px 11px 0; - } -`; - -export const SideBarSubSection = styled.div` - height: calc(100vh - 135px); - overflow: auto; - padding-right: 18px; - &.fullView { - height: calc(100vh - 70px); - } - &::-webkit-scrollbar { - height: 12px; - width: 1px; - background: #d6d6d6; - } - &::-webkit-scrollbar-thumb { - background: #d6d6d6; - -webkit-border-radius: 1ex; - -webkit-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.75); - } -`; - -export const EmptyValue = styled.div` - &:after { - content: 'None'; - color: #b7b7b7; - font-style: italic; - font-weight: 100; - } -`; - -export const Name = styled.div` - font-size: 20px; - line-height: 28px; - color: #262626; - margin: 13px 0 7px 0; -`; - -export const UserDetails = styled.div` - font-size: 14px; - line-height: 35px; - font-weight: 400; - line-height: 28px; - text-align: center; -`; - -export const TitleRole = styled.div` - font-size: 14px; - line-height: 22px; - color: #595959; - margin-bottom: 7px; -`; - -export const Team = styled.div` - font-size: 12px; - line-height: 20px; - font-weight: 400; - padding-bottom: 10px; - color: #8c8c8c; -`; - -export const SocialDetails = styled.div` - font-size: 12px; - line-height: 20px; - color: #262626; - text-align: left; - margin: 6px 0; -`; - -export const EditButton = styled.div` - bottom: 24px; - position: absolute; - right: 27px; - width: 80%; - left: 50%; - -webkit-transform: translateX(-50%); - -moz-transform: translateX(-50%); - transform: translateX(-50%); - - button { - width: 100%; - font-size: 12px; - line-height: 20px; - color: #262626; - } -`; - -export const AboutSection = styled.div` - text-align: left; - font-weight: bold; - font-size: 14px; - line-height: 22px; - color: #262626; -`; - -export const LocationSection = styled.div` - text-align: left; - font-weight: bold; - font-size: 14px; - line-height: 26px; - color: #262626; -`; - -export const LocationSectionText = styled.div` - text-align: left; - font-weight: normal; - font-size: 14px; - line-height: 26px; - margin-bottom: -10px; - color: #262626; -`; - -export const AboutSectionText = styled.div` - font-size: 12px; - font-weight: 100; - line-height: 15px; - padding: 5px 0; - - &&& .ant-typography { - margin-bottom: 0; - } - &&& .ant-typography-edit-content { - padding-left: 15px; - padding-top: 5px; - } -`; - -export const GroupsSection = styled.div` - text-align: left; - font-weight: bold; - font-size: 14px; - line-height: 22px; - color: #262626; -`; - -export const TagsSection = styled.div` - height: calc(75vh - 460px); - padding: 0px 5px 5px 0; -`; - -export const NoDataFound = styled.span` - font-size: 12px; - color: #262626; - font-weight: 100; -`; - -export const Tags = styled.div` - margin-top: 5px; -`; - -export const GroupsSeeMoreText = styled.span` - font-weight: 500; - font-size: 12px; - line-height: 20px; - color: #1890ff; - cursor: pointer; -`; - -export const DisplayCount = styled.span` - font-family: Manrope; - font-style: normal; - font-weight: 500; - font-size: 12px; - line-height: 20px; - color: #8c8c8c; -`; - -export const GroupSectionTitle = styled.span` - margin-right: 8px; -`; - -export const GroupSectionHeader = styled.div` - padding-bottom: 12px; -`; diff --git a/datahub-web-react/src/app/entity/shared/UrlButton.tsx b/datahub-web-react/src/app/entity/shared/UrlButton.tsx deleted file mode 100644 index 17099c93e6ed1d..00000000000000 --- a/datahub-web-react/src/app/entity/shared/UrlButton.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { ArrowRightOutlined } from '@ant-design/icons'; -import { Button } from 'antd'; -import React, { ReactNode } from 'react'; -import styled from 'styled-components/macro'; - -const UrlButtonContainer = styled.span` - font-size: 12px; -`; - -const StyledButton = styled(Button)` - > :hover { - text-decoration: underline; - } - &&& { - padding-bottom: 0px; - } - padding-left: 12px; - padding-right: 12px; -`; - -interface Props { - href: string; - children: ReactNode; - onClick?: () => void; -} - -const NOOP = () => {}; - -export default function UrlButton({ href, children, onClick = NOOP }: Props) { - return ( - - - {children} - - - ); -} diff --git a/datahub-web-react/src/app/entity/shared/__tests__/siblingsUtils.test.ts b/datahub-web-react/src/app/entity/shared/__tests__/siblingsUtils.test.ts deleted file mode 100644 index 8e13386d9ddd91..00000000000000 --- a/datahub-web-react/src/app/entity/shared/__tests__/siblingsUtils.test.ts +++ /dev/null @@ -1,731 +0,0 @@ -import { - cleanHelper, - combineEntityDataWithSiblings, - mergeOwners, - shouldEntityBeTreatedAsPrimary, -} from '@app/entity/shared/siblingUtils'; -import { dataset3WithLineage, dataset3WithSchema, dataset4WithLineage } from '@src/Mocks'; - -import { EntityType, OwnershipType, SchemaFieldDataType } from '@types'; - -const usageStats = { - buckets: [ - { - bucket: 1650412800000, - duration: 'DAY', - resource: 'urn:li:dataset:4', - metrics: { - uniqueUserCount: 1, - totalSqlQueries: 37, - topSqlQueries: ['some sql query'], - __typename: 'UsageAggregationMetrics', - }, - __typename: 'UsageAggregation', - }, - ], - aggregations: { - uniqueUserCount: 2, - totalSqlQueries: 39, - users: [ - { - user: { - urn: 'urn:li:corpuser:user', - username: 'user', - __typename: 'CorpUser', - }, - count: 2, - userEmail: 'user@datahubproject.io', - __typename: 'UserUsageCounts', - }, - ], - fields: [ - { - fieldName: 'field', - count: 7, - __typename: 'FieldUsageCounts', - }, - ], - __typename: 'UsageQueryResultAggregations', - }, - __typename: 'UsageQueryResult', -}; - -const datasetPrimary = { - ...dataset3WithLineage, - ...dataset3WithSchema.dataset, - properties: { - ...dataset3WithLineage.properties, - description: 'primary description', - }, - editableProperties: { - description: null, - }, - globalTags: { - tags: [ - { - tag: { - type: EntityType.Tag, - urn: 'urn:li:tag:primary-tag', - name: 'primary-tag', - description: 'primary tag', - properties: { - name: 'primary-tag', - description: 'primary tag', - colorHex: 'primary tag color', - }, - }, - }, - ], - }, - siblings: { - isPrimary: true, - }, -}; - -const datasetUnprimary = { - ...dataset4WithLineage, - usageStats, - properties: { - ...dataset4WithLineage.properties, - description: 'unprimary description', - }, - editableProperties: { - description: 'secondary description', - }, - globalTags: { - tags: [ - { - tag: { - type: EntityType.Tag, - urn: 'urn:li:tag:unprimary-tag', - name: 'unprimary-tag', - description: 'unprimary tag', - properties: { - name: 'unprimary-tag', - description: 'unprimary tag', - colorHex: 'unprimary tag color', - }, - }, - }, - ], - }, - schemaMetadata: { - ...dataset4WithLineage.schemaMetadata, - fields: [ - { - __typename: 'SchemaField', - nullable: false, - recursive: false, - fieldPath: 'new_one', - description: 'Test to make sure fields merge works', - type: SchemaFieldDataType.String, - nativeDataType: 'varchar(100)', - isPartOfKey: false, - jsonPath: null, - globalTags: null, - glossaryTerms: null, - label: 'hi', - }, - ...(dataset4WithLineage.schemaMetadata?.fields || []), - { - __typename: 'SchemaField', - nullable: false, - recursive: false, - fieldPath: 'duplicate_field', - description: 'Test to make sure fields merge works case insensitive', - type: SchemaFieldDataType.String, - nativeDataType: 'varchar(100)', - isPartOfKey: false, - jsonPath: null, - globalTags: null, - glossaryTerms: null, - label: 'hi', - }, - ], - }, - siblings: { - isPrimary: false, - }, -}; - -const datasetPrimaryWithSiblings = { - ...datasetPrimary, - schemaMetadata: { - ...datasetPrimary.schemaMetadata, - fields: [ - ...(datasetPrimary.schemaMetadata?.fields || []), - { - __typename: 'SchemaField', - nullable: false, - recursive: false, - fieldPath: 'DUPLICATE_FIELD', - description: 'Test to make sure fields merge works case insensitive', - type: SchemaFieldDataType.String, - nativeDataType: 'varchar(100)', - isPartOfKey: false, - jsonPath: null, - globalTags: null, - glossaryTerms: null, - label: 'hi', - }, - ], - }, - - siblings: { - isPrimary: true, - siblings: [datasetUnprimary], - }, - siblingsSearch: { - count: 1, - total: 1, - searchResults: [{ entity: datasetUnprimary, matchedFields: [] }], - }, -}; - -const datasetUnprimaryWithPrimarySiblings = { - ...datasetUnprimary, - siblings: { - isPrimary: false, - siblings: [datasetPrimary], - }, - siblingsSearch: { - count: 1, - total: 1, - searchResults: [{ entity: datasetPrimary, matchedFields: [] }], - }, -}; - -const datasetUnprimaryWithNoPrimarySiblings = { - ...datasetUnprimary, - siblings: { - isPrimary: false, - siblings: [datasetUnprimary], - }, - siblingsSearch: { - count: 1, - total: 1, - searchResults: [{ entity: datasetUnprimary, matchedFields: [] }], - }, -}; - -describe('siblingUtils', () => { - describe('combineEntityDataWithSiblings', () => { - it('combines my metadata with my siblings as primary', () => { - const baseEntity = { dataset: datasetPrimaryWithSiblings }; - expect(baseEntity.dataset.usageStats).toBeNull(); - const combinedData = combineEntityDataWithSiblings(baseEntity); - // will merge properties only one entity has - expect(combinedData.dataset.usageStats).toEqual(usageStats); - - // will merge arrays - expect(combinedData.dataset.globalTags.tags).toHaveLength(2); - expect(combinedData.dataset.globalTags.tags[0].tag.urn).toEqual('urn:li:tag:unprimary-tag'); - expect(combinedData.dataset.globalTags.tags[1].tag.urn).toEqual('urn:li:tag:primary-tag'); - - // merges schema metadata properly by fieldPath - expect(combinedData.dataset.schemaMetadata?.fields).toHaveLength(4); - expect(combinedData.dataset.schemaMetadata?.fields[0]?.fieldPath).toEqual('new_one'); - expect(combinedData.dataset.schemaMetadata?.fields[1]?.fieldPath).toEqual('DUPLICATE_FIELD'); - expect(combinedData.dataset.schemaMetadata?.fields[2]?.fieldPath).toEqual('user_id'); - expect(combinedData.dataset.schemaMetadata?.fields[3]?.fieldPath).toEqual('user_name'); - - // will overwrite string properties w/ primary - expect(combinedData.dataset.editableProperties.description).toEqual('secondary description'); - - // will take secondary string properties in the case of empty string - expect(combinedData.dataset.properties.description).toEqual('primary description'); - - // will stay primary - expect(combinedData.dataset.siblings.isPrimary).toBeTruthy(); - }); - - it('combines my metadata with my siblings as secondary', () => { - const baseEntity = { dataset: datasetUnprimaryWithPrimarySiblings }; - const combinedData = combineEntityDataWithSiblings(baseEntity); - - // will stay secondary - expect(combinedData.dataset.siblings.isPrimary).toBeFalsy(); - }); - }); - - describe('shouldEntityBeTreatedAsPrimary', () => { - it('will say a primary entity is primary', () => { - expect(shouldEntityBeTreatedAsPrimary(datasetPrimaryWithSiblings)).toBeTruthy(); - }); - - it('will say a un-primary entity is not primary', () => { - expect(shouldEntityBeTreatedAsPrimary(datasetUnprimaryWithPrimarySiblings)).toBeFalsy(); - }); - - it('will say a un-primary entity is primary if it has no primary sibling', () => { - expect(shouldEntityBeTreatedAsPrimary(datasetUnprimaryWithNoPrimarySiblings)).toBeTruthy(); - }); - }); - - describe('cleanHelper', () => { - let visited: Set; - - beforeEach(() => { - visited = new Set(); - }); - - it('should return the object immediately if it has already been visited', () => { - const obj = { test: 'value' }; - visited.add(obj); - const result = cleanHelper(obj, visited); - expect(result).toBe(obj); - }); - - it('should remove null properties from objects', () => { - const obj = { - validProperty: 'test', - nullProperty: null, - anotherValid: 42, - }; - const result = cleanHelper(obj, visited); - expect(result).toEqual({ - validProperty: 'test', - anotherValid: 42, - }); - expect(result).not.toHaveProperty('nullProperty'); - }); - - it('should remove undefined properties from objects', () => { - const obj = { - validProperty: 'test', - undefinedProperty: undefined, - anotherValid: 42, - }; - const result = cleanHelper(obj, visited); - expect(result).toEqual({ - validProperty: 'test', - anotherValid: 42, - }); - expect(result).not.toHaveProperty('undefinedProperty'); - }); - - it('should remove empty object properties', () => { - const obj = { - validProperty: 'test', - emptyObject: {}, - anotherValid: 42, - }; - const result = cleanHelper(obj, visited); - expect(result).toEqual({ - validProperty: 'test', - anotherValid: 42, - }); - expect(result).not.toHaveProperty('emptyObject'); - }); - - it('should recursively clean nested objects', () => { - const obj = { - level1: { - validProp: 'test', - nullProp: null, - level2: { - nestedValid: 'nested', - nestedNull: null, - emptyNested: {}, - }, - }, - topLevel: 'value', - }; - const result = cleanHelper(obj, visited); - expect(result).toEqual({ - level1: { - validProp: 'test', - level2: { - nestedValid: 'nested', - }, - }, - topLevel: 'value', - }); - }); - - it('should not modify arrays when they contain null, undefined, or empty objects', () => { - const obj = { - arrayProp: ['valid', null, undefined, {}, { validNested: 'test' }], - regularProp: 'test', - }; - const result = cleanHelper(obj, visited); - expect(result.arrayProp).toHaveLength(5); - expect(result.arrayProp[0]).toBe('valid'); - expect(result.arrayProp[1]).toBe(null); - expect(result.arrayProp[2]).toBe(undefined); - expect(result.arrayProp[3]).toEqual({}); - expect(result.arrayProp[4]).toEqual({ validNested: 'test' }); - }); - - it('should still clean objects within arrays recursively', () => { - const obj = { - arrayProp: [ - { - validProp: 'test', - nullProp: null, - nestedObj: { - valid: 'nested', - invalid: null, - }, - }, - ], - }; - const result = cleanHelper(obj, visited); - expect(result.arrayProp[0]).toEqual({ - validProp: 'test', - nestedObj: { - valid: 'nested', - }, - }); - expect(result.arrayProp[0]).not.toHaveProperty('nullProp'); - }); - - it('should handle circular references without infinite recursion', () => { - const obj: any = { - validProp: 'test', - nullProp: null, - }; - obj.circular = obj; - - const result = cleanHelper(obj, visited); - expect(result.validProp).toBe('test'); - expect(result).not.toHaveProperty('nullProp'); - expect(result.circular).toBe(result); - }); - - it('should handle objects with non-configurable properties gracefully', () => { - const obj = { - validProp: 'test', - nullProp: null, - }; - - // Make a property non-configurable - Object.defineProperty(obj, 'nonConfigurable', { - value: null, - configurable: false, - enumerable: true, - writable: true, - }); - - const result = cleanHelper(obj, visited); - expect(result.validProp).toBe('test'); - expect(result).not.toHaveProperty('nullProp'); - // Non-configurable null property should remain - expect(result).toHaveProperty('nonConfigurable'); - expect(result.nonConfigurable).toBe(null); - }); - - it('should preserve valid nested structures', () => { - const obj = { - user: { - name: 'John', - age: 30, - preferences: { - theme: 'dark', - notifications: true, - }, - }, - metadata: { - created: '2023-01-01', - tags: ['important', 'user'], - }, - }; - - const result = cleanHelper(obj, visited); - expect(result).toEqual(obj); - }); - - it('should handle complex mixed structures', () => { - const obj = { - validString: 'test', - validNumber: 42, - validBoolean: true, - validArray: [1, 2, 3], - nullValue: null, - undefinedValue: undefined, - emptyObject: {}, - nestedValid: { - innerValid: 'nested', - innerNull: null, - innerArray: ['a', null, 'b'], - innerEmpty: {}, - }, - mixedArray: [{ valid: 'item', invalid: null }, null, 'string', 42], - }; - - const result = cleanHelper(obj, visited); - expect(result).toEqual({ - validString: 'test', - validNumber: 42, - validBoolean: true, - validArray: [1, 2, 3], - nestedValid: { - innerValid: 'nested', - innerArray: ['a', null, 'b'], - }, - mixedArray: [{ valid: 'item' }, null, 'string', 42], - }); - }); - - it('should handle edge case of empty input object', () => { - const obj = {}; - const result = cleanHelper(obj, visited); - expect(result).toEqual({}); - }); - - it('should handle edge case where object becomes empty after cleaning', () => { - const obj = { - nullProp: null, - undefinedProp: undefined, - emptyProp: {}, - }; - const result = cleanHelper(obj, visited); - expect(result).toEqual({}); - }); - }); - - describe('mergeOwners', () => { - const createOwner = (ownerUrn: string, ownershipTypeUrn?: string, associatedUrn?: string) => ({ - associatedUrn: - associatedUrn || - 'urn:li:dataset(urn:li:dataPlatform:bigquery,cypress_project.jaffle_shop.customers,PROD)', - owner: { urn: ownerUrn }, - ownershipType: ownershipTypeUrn ? { urn: ownershipTypeUrn } : undefined, - }); - - it('should merge two arrays with no duplicates', () => { - const destinationArray = [ - createOwner('urn:li:corpGroup:bfoo', 'urn:li:ownershipType:__system__technical_owner'), - ]; - const sourceArray = [createOwner('urn:li:corpGroup:bar', 'urn:li:ownershipType:__system__business_owner')]; - - const result = mergeOwners(destinationArray, sourceArray, {}); - - expect(result).toHaveLength(2); - expect(result).toEqual([ - createOwner('urn:li:corpGroup:bfoo', 'urn:li:ownershipType:__system__technical_owner'), - createOwner('urn:li:corpGroup:bar', 'urn:li:ownershipType:__system__business_owner'), - ]); - }); - - it('should deduplicate owners with same owner.urn and ownershipType.urn', () => { - const duplicateOwner = createOwner( - 'urn:li:corpGroup:bfoo', - 'urn:li:ownershipType:__system__technical_owner', - ); - const destinationArray = [duplicateOwner]; - const sourceArray = [duplicateOwner]; - - const result = mergeOwners(destinationArray, sourceArray, {}); - - expect(result).toHaveLength(1); - expect(result[0]).toEqual(duplicateOwner); - }); - - it('should keep owners with same owner.urn but different ownershipType.urn as separate entries', () => { - const destinationArray = [ - createOwner('urn:li:corpGroup:bfoo', 'urn:li:ownershipType:__system__technical_owner'), - ]; - const sourceArray = [createOwner('urn:li:corpGroup:bfoo', 'urn:li:ownershipType:__system__business_owner')]; - - const result = mergeOwners(destinationArray, sourceArray, {}); - - expect(result).toHaveLength(2); - expect(result[0].owner.urn).toBe('urn:li:corpGroup:bfoo'); - expect(result[0].ownershipType?.urn).toBe('urn:li:ownershipType:__system__technical_owner'); - expect(result[1].owner.urn).toBe('urn:li:corpGroup:bfoo'); - expect(result[1].ownershipType?.urn).toBe('urn:li:ownershipType:__system__business_owner'); - }); - - it('should keep owners with different owner.urn but same ownershipType.urn as separate entries', () => { - const destinationArray = [ - createOwner('urn:li:corpGroup:bfoo', 'urn:li:ownershipType:__system__technical_owner'), - ]; - const sourceArray = [createOwner('urn:li:corpGroup:bar', 'urn:li:ownershipType:__system__technical_owner')]; - - const result = mergeOwners(destinationArray, sourceArray, {}); - - expect(result).toHaveLength(2); - expect(result[0].owner.urn).toBe('urn:li:corpGroup:bfoo'); - expect(result[1].owner.urn).toBe('urn:li:corpGroup:bar'); - expect(result[0].ownershipType?.urn).toBe('urn:li:ownershipType:__system__technical_owner'); - expect(result[1].ownershipType?.urn).toBe('urn:li:ownershipType:__system__technical_owner'); - }); - - it('should handle empty destination array', () => { - const destinationArray: any[] = []; - const sourceArray = [ - createOwner('urn:li:corpGroup:bfoo', 'urn:li:ownershipType:__system__technical_owner'), - ]; - - const result = mergeOwners(destinationArray, sourceArray, {}); - - expect(result).toHaveLength(1); - expect(result[0]).toEqual(sourceArray[0]); - }); - - it('should handle empty source array', () => { - const destinationArray = [ - createOwner('urn:li:corpGroup:bfoo', 'urn:li:ownershipType:__system__technical_owner'), - ]; - const sourceArray: any[] = []; - - const result = mergeOwners(destinationArray, sourceArray, {}); - - expect(result).toHaveLength(1); - expect(result[0]).toEqual(destinationArray[0]); - }); - - it('should handle both arrays being empty', () => { - const destinationArray: any[] = []; - const sourceArray: any[] = []; - - const result = mergeOwners(destinationArray, sourceArray, {}); - - expect(result).toHaveLength(0); - expect(result).toEqual([]); - }); - - it('should handle owners with missing ownershipType', () => { - const destinationArray = [ - createOwner('urn:li:corpGroup:bfoo'), // no ownershipType - ]; - const sourceArray = [ - createOwner('urn:li:corpGroup:bfoo', 'urn:li:ownershipType:__system__technical_owner'), - ]; - - const result = mergeOwners(destinationArray, sourceArray, {}); - - expect(result).toHaveLength(2); - expect(result[0].ownershipType).toBeUndefined(); - expect(result[1].ownershipType?.urn).toBe('urn:li:ownershipType:__system__technical_owner'); - }); - - it('should handle owners with missing ownershipTypes and the same deprecated types', () => { - const destinationArray = [{ ...createOwner('urn:li:corpGroup:bfoo'), type: OwnershipType.BusinessOwner }]; - const sourceArray = [{ ...createOwner('urn:li:corpGroup:bfoo'), type: OwnershipType.BusinessOwner }]; - - const result = mergeOwners(destinationArray, sourceArray, {}); - - expect(result).toHaveLength(1); - expect(result[0].owner.urn).toBe('urn:li:corpGroup:bfoo'); - expect(result[0].type).toBe(OwnershipType.BusinessOwner); - }); - - it('should handle owners with missing ownershipTypes and different deprecated types', () => { - const destinationArray = [{ ...createOwner('urn:li:corpGroup:bfoo'), type: OwnershipType.TechnicalOwner }]; - const sourceArray = [{ ...createOwner('urn:li:corpGroup:bfoo'), type: OwnershipType.BusinessOwner }]; - - const result = mergeOwners(destinationArray, sourceArray, {}); - - expect(result).toHaveLength(2); - expect(result[0].owner.urn).toBe('urn:li:corpGroup:bfoo'); - expect(result[0].type).toBe(OwnershipType.TechnicalOwner); - expect(result[1].owner.urn).toBe('urn:li:corpGroup:bfoo'); - expect(result[1].type).toBe(OwnershipType.BusinessOwner); - }); - - it('should deduplicate owners that both have undefined ownershipType', () => { - const ownerWithoutType = createOwner('urn:li:corpGroup:bfoo'); - const destinationArray = [ownerWithoutType]; - const sourceArray = [createOwner('urn:li:corpGroup:bfoo')]; // same owner, no ownershipType - - const result = mergeOwners(destinationArray, sourceArray, {}); - - expect(result).toHaveLength(1); - expect(result[0].owner.urn).toBe('urn:li:corpGroup:bfoo'); - expect(result[0].ownershipType).toBeUndefined(); - }); - - it('should preserve order with destination array items first', () => { - const destinationArray = [ - createOwner('urn:li:corpGroup:dest1', 'urn:li:ownershipType:__system__technical_owner'), - createOwner('urn:li:corpGroup:dest2', 'urn:li:ownershipType:__system__business_owner'), - ]; - const sourceArray = [ - createOwner('urn:li:corpGroup:src1', 'urn:li:ownershipType:__system__data_owner'), - createOwner('urn:li:corpGroup:src2', 'urn:li:ownershipType:__system__steward'), - ]; - - const result = mergeOwners(destinationArray, sourceArray, {}); - - expect(result).toHaveLength(4); - expect(result[0].owner.urn).toBe('urn:li:corpGroup:dest1'); - expect(result[1].owner.urn).toBe('urn:li:corpGroup:dest2'); - expect(result[2].owner.urn).toBe('urn:li:corpGroup:src1'); - expect(result[3].owner.urn).toBe('urn:li:corpGroup:src2'); - }); - - it('should handle complex scenario with multiple duplicates and unique entries', () => { - const destinationArray = [ - createOwner('urn:li:corpGroup:common', 'urn:li:ownershipType:__system__technical_owner'), - createOwner('urn:li:corpGroup:dest_only', 'urn:li:ownershipType:__system__business_owner'), - createOwner('urn:li:corpGroup:common', 'urn:li:ownershipType:__system__data_owner'), - ]; - const sourceArray = [ - createOwner('urn:li:corpGroup:common', 'urn:li:ownershipType:__system__technical_owner'), // duplicate - createOwner('urn:li:corpGroup:src_only', 'urn:li:ownershipType:__system__steward'), - createOwner('urn:li:corpGroup:common', 'urn:li:ownershipType:__system__business_owner'), // different type - ]; - - const result = mergeOwners(destinationArray, sourceArray, {}); - - expect(result).toHaveLength(5); - - // Should contain all unique combinations - const ownerCombinations = result.map((owner) => ({ - ownerUrn: owner.owner.urn, - ownershipTypeUrn: owner.ownershipType?.urn, - })); - - expect(ownerCombinations).toContainEqual({ - ownerUrn: 'urn:li:corpGroup:common', - ownershipTypeUrn: 'urn:li:ownershipType:__system__technical_owner', - }); - expect(ownerCombinations).toContainEqual({ - ownerUrn: 'urn:li:corpGroup:dest_only', - ownershipTypeUrn: 'urn:li:ownershipType:__system__business_owner', - }); - expect(ownerCombinations).toContainEqual({ - ownerUrn: 'urn:li:corpGroup:common', - ownershipTypeUrn: 'urn:li:ownershipType:__system__data_owner', - }); - expect(ownerCombinations).toContainEqual({ - ownerUrn: 'urn:li:corpGroup:src_only', - ownershipTypeUrn: 'urn:li:ownershipType:__system__steward', - }); - expect(ownerCombinations).toContainEqual({ - ownerUrn: 'urn:li:corpGroup:common', - ownershipTypeUrn: 'urn:li:ownershipType:__system__business_owner', - }); - }); - - it('should maintain all properties of owner objects beyond just owner.urn and ownershipType.urn', () => { - const destinationArray = [ - { - associatedUrn: 'urn:li:dataset:123', - owner: { urn: 'urn:li:corpGroup:bfoo', name: 'Test Group' }, - ownershipType: { urn: 'urn:li:ownershipType:__system__technical_owner', name: 'Technical Owner' }, - extraProperty: 'destination_value', - }, - ]; - const sourceArray = [ - { - associatedUrn: 'urn:li:dataset:456', - owner: { urn: 'urn:li:corpGroup:bar', displayName: 'Another Group' }, - ownershipType: { - urn: 'urn:li:ownershipType:__system__business_owner', - description: 'Business Owner', - }, - extraProperty: 'source_value', - }, - ]; - - const result = mergeOwners(destinationArray, sourceArray, {}); - - expect(result).toHaveLength(2); - expect(result[0]).toEqual(destinationArray[0]); - expect(result[1]).toEqual(sourceArray[0]); - }); - }); -}); diff --git a/datahub-web-react/src/app/entity/shared/components/legacy/MarkdownViewer.tsx b/datahub-web-react/src/app/entity/shared/components/legacy/MarkdownViewer.tsx index ab52706eee32c8..733f12c6b12a0c 100644 --- a/datahub-web-react/src/app/entity/shared/components/legacy/MarkdownViewer.tsx +++ b/datahub-web-react/src/app/entity/shared/components/legacy/MarkdownViewer.tsx @@ -63,7 +63,7 @@ const MarkdownViewContainer = styled.div<{ `} `; -export const MarkdownView = styled(MDEditor.Markdown)` +const MarkdownView = styled(MDEditor.Markdown)` display: block; overflow-wrap: break-word; word-wrap: break-word; @@ -73,7 +73,7 @@ export const MarkdownView = styled(MDEditor.Markdown)` font-weight: 400; `; -export type Props = { +type Props = { source: string; limit?: number; // eslint-disable-next-line react/no-unused-prop-types diff --git a/datahub-web-react/src/app/entity/shared/components/legacy/Properties.tsx b/datahub-web-react/src/app/entity/shared/components/legacy/Properties.tsx deleted file mode 100644 index 075c458e0a49c9..00000000000000 --- a/datahub-web-react/src/app/entity/shared/components/legacy/Properties.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { Space, Table, Typography } from 'antd'; -import { ColumnsType } from 'antd/es/table'; -import React from 'react'; - -import { StringMapEntry } from '@types'; - -export type Props = { - properties: StringMapEntry[]; -}; - -export function Properties({ properties }: Props) { - const propertyTableColumns: ColumnsType = [ - { - title: 'Name', - dataIndex: 'key', - sorter: (a, b) => a.key.localeCompare(b.key), - defaultSortOrder: 'ascend', - }, - { - title: 'Value', - dataIndex: 'value', - }, - ]; - - return ( - - Properties -
- - ); -} diff --git a/datahub-web-react/src/app/entity/shared/components/legacy/UpdatableDescription.tsx b/datahub-web-react/src/app/entity/shared/components/legacy/UpdatableDescription.tsx deleted file mode 100644 index 70eb7a1ff78765..00000000000000 --- a/datahub-web-react/src/app/entity/shared/components/legacy/UpdatableDescription.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { FetchResult, MutationFunctionOptions } from '@apollo/client'; -import { Tag, message } from 'antd'; -import React, { useState } from 'react'; -import styled from 'styled-components'; - -import analytics, { EntityActionType, EventType } from '@app/analytics'; -import UpdateDescriptionModal from '@app/entity/shared/components/legacy/DescriptionModal'; -import MarkdownViewer from '@app/entity/shared/components/legacy/MarkdownViewer'; - -import { EntityType } from '@types'; - -const DescriptionText = styled(MarkdownViewer)` - ${(props) => (props.isCompact ? 'max-width: 377px;' : '')}; -`; - -const AddNewDescription = styled(Tag)` - cursor: pointer; -`; - -export type Props = { - isCompact?: boolean; - updateEntity: (options?: MutationFunctionOptions | undefined) => Promise; - updatedDescription?: string | null; - originalDescription?: string | null; - entityType: EntityType; - urn: string; -}; - -export default function UpdatableDescription({ - isCompact, - updateEntity, - updatedDescription, - originalDescription, - entityType, - urn, -}: Props) { - const [showAddDescModal, setShowAddDescModal] = useState(false); - const onSubmit = async (description: string | null) => { - message.loading({ content: 'Updating...' }); - try { - await updateEntity({ - variables: { urn, input: { editableProperties: { description: description || '' } } }, - }); - message.destroy(); - analytics.event({ - type: EventType.EntityActionEvent, - actionType: EntityActionType.UpdateDescription, - entityType, - entityUrn: urn, - }); - message.success({ content: 'Updated!', duration: 2 }); - } catch (e: unknown) { - message.destroy(); - if (e instanceof Error) message.error({ content: `Update Failed! \n ${e.message || ''}`, duration: 2 }); - } - setShowAddDescModal(false); - }; - - return ( - <> - {updatedDescription || originalDescription ? ( - <> - setShowAddDescModal(true)} - /> - {showAddDescModal && ( - setShowAddDescModal(false)} - onSubmit={onSubmit} - original={originalDescription || ''} - description={updatedDescription || ''} - /> - )} - - ) : ( - <> - setShowAddDescModal(true)}> - + Add Description - - {showAddDescModal && ( - setShowAddDescModal(false)} - onSubmit={onSubmit} - isAddDesc - /> - )} - - )} - - ); -} 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 deleted file mode 100644 index fff9d0bb21d00b..00000000000000 --- a/datahub-web-react/src/app/entity/shared/components/styled/AddLinkModal.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { PlusOutlined } from '@ant-design/icons'; -import { Button, Form, Input, Modal, message } from 'antd'; -import React, { useState } from 'react'; - -import analytics, { EntityActionType, EventType } from '@app/analytics'; -import { useUserContext } from '@app/context/useUserContext'; -import { useEntityData, useMutationUrn } from '@app/entity/shared/EntityContext'; -import { getModalDomContainer } from '@utils/focus'; - -import { useAddLinkMutation } from '@graphql/mutations.generated'; - -type AddLinkProps = { - buttonProps?: Record; - refetch?: () => Promise; -}; - -export const AddLinkModal = ({ buttonProps, refetch }: AddLinkProps) => { - const [isModalVisible, setIsModalVisible] = useState(false); - const mutationUrn = useMutationUrn(); - const user = useUserContext(); - const { entityType } = useEntityData(); - const [addLinkMutation] = useAddLinkMutation(); - - const [form] = Form.useForm(); - - const showModal = () => { - setIsModalVisible(true); - }; - - const handleClose = () => { - form.resetFields(); - setIsModalVisible(false); - }; - - const handleAdd = async (formData: any) => { - if (user?.urn) { - try { - await addLinkMutation({ - variables: { input: { linkUrl: formData.url, label: formData.label, resourceUrn: mutationUrn } }, - }); - message.success({ content: 'Link Added', duration: 2 }); - analytics.event({ - type: EventType.EntityActionEvent, - entityType, - entityUrn: mutationUrn, - actionType: EntityActionType.UpdateLinks, - }); - } catch (e: unknown) { - message.destroy(); - if (e instanceof Error) { - message.error({ content: `Failed to add link: \n ${e.message || ''}`, duration: 3 }); - } - } - refetch?.(); - handleClose(); - } else { - message.error({ content: `Error adding link: no user`, duration: 2 }); - } - }; - - return ( - <> - - - Cancel - , - , - ]} - getContainer={getModalDomContainer} - > -
- - - - - - - -
- - ); -}; diff --git a/datahub-web-react/src/app/entity/shared/components/styled/DemoButton.tsx b/datahub-web-react/src/app/entity/shared/components/styled/DemoButton.tsx deleted file mode 100644 index 63a9ff061dc782..00000000000000 --- a/datahub-web-react/src/app/entity/shared/components/styled/DemoButton.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Button } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -const StyledButton = styled(Button)` - padding: 8px; - font-size: 14px; - margin-left: 18px; -`; - -export default function DemoButton() { - return ( - - Schedule a Demo - - ); -} diff --git a/datahub-web-react/src/app/entity/shared/components/styled/DeprecationPill.tsx b/datahub-web-react/src/app/entity/shared/components/styled/DeprecationPill.tsx deleted file mode 100644 index 334ed97b8c8aff..00000000000000 --- a/datahub-web-react/src/app/entity/shared/components/styled/DeprecationPill.tsx +++ /dev/null @@ -1,247 +0,0 @@ -import { blue } from '@ant-design/colors'; -import { InfoCircleOutlined } from '@ant-design/icons'; -import { Popover, Tooltip, colors } from '@components'; -import { Divider, Modal, Typography, message } from 'antd'; -import moment from 'moment'; -import React, { useState } from 'react'; -import styled from 'styled-components'; - -import StripMarkdownText, { removeMarkdown } from '@app/entity/shared/components/styled/StripMarkdownText'; -import { ANTD_GRAY } from '@app/entity/shared/constants'; -import { Editor } from '@app/entity/shared/tabs/Documentation/components/editor/Editor'; -import { getLocaleTimezone } from '@app/shared/time/timeUtils'; - -import { useBatchUpdateDeprecationMutation } from '@graphql/mutations.generated'; -import { Deprecation } from '@types'; - -const DeprecatedContainer = styled.div` - height: 18px; - border: 1px solid ${colors.red[500]}; - border-radius: 15px; - display: flex; - justify-content: center; - align-items: center; - color: ${colors.red[500]}; - padding-top: 8px; - padding-bottom: 8px; - padding-right: 4px; - padding-left: 4px; -`; - -const DeprecatedText = styled.div` - padding-right: 2px; - padding-left: 2px; - font-size: 10px; -`; - -const DeprecatedTitle = styled(Typography.Text)` - display: block; - font-size: 14px; - margin-bottom: 5px; - font-weight: bold; -`; - -const LastEvaluatedAtLabel = styled.div` - padding: 0; - margin: 0; - display: flex; - align-items: center; - color: ${ANTD_GRAY[7]}; -`; - -const ThinDivider = styled(Divider)` - margin-top: 8px; - margin-bottom: 8px; -`; - -const UndeprecatedIcon = styled(InfoCircleOutlined)` - font-size: 14px; - padding-right: 6px; -`; - -const IconGroup = styled.div` - font-size: 12px; - color: 'black'; - &:hover { - color: ${blue[4]}; - cursor: pointer; - } -`; - -const DescriptionContainer = styled.div` - position: relative; - display: flex; - flex-direction: column; - width: 100%; - height: 100%; - min-height: 22px; - margin-bottom: 14px; -`; -const StyledViewer = styled(Editor)` - padding-right: 8px; - display: block; - - .remirror-editor.ProseMirror { - padding: 0; - } -`; - -const ExpandedActions = styled.div` - height: 10px; -`; -const ReadLessText = styled(Typography.Link)` - margin-right: 4px; -`; -type Props = { - urn: string; - deprecation: Deprecation; - refetch?: () => void; - showUndeprecate: boolean | null; -}; -const ABBREVIATED_LIMIT = 80; - -export const DeprecationPill = ({ deprecation, urn, refetch, showUndeprecate }: Props) => { - const [batchUpdateDeprecationMutation] = useBatchUpdateDeprecationMutation(); - const [expanded, setExpanded] = useState(false); - const overLimit = deprecation?.note && removeMarkdown(deprecation?.note).length > 80; - /** - * Deprecation Decommission Timestamp - */ - const localeTimezone = getLocaleTimezone(); - - let decommissionTimeSeconds; - if (deprecation.decommissionTime) { - if (deprecation.decommissionTime < 943920000000) { - // Time is set in way past if it was milli-second so considering this as set in seconds - decommissionTimeSeconds = deprecation.decommissionTime; - } else { - decommissionTimeSeconds = deprecation.decommissionTime / 1000; - } - } - const decommissionTimeLocal = - (decommissionTimeSeconds && - `Scheduled to be decommissioned on ${moment - .unix(decommissionTimeSeconds) - .format('DD/MMM/YYYY')} (${localeTimezone})`) || - undefined; - const decommissionTimeGMT = - decommissionTimeSeconds && moment.unix(decommissionTimeSeconds).utc().format('dddd, DD/MMM/YYYY HH:mm:ss z'); - - const hasDetails = deprecation.note !== '' || deprecation.decommissionTime !== null; - const isDividerNeeded = deprecation.note !== '' && deprecation.decommissionTime !== null; - - const batchUndeprecate = () => { - batchUpdateDeprecationMutation({ - variables: { - input: { - resources: [{ resourceUrn: urn }], - deprecated: false, - }, - }, - }) - .then(({ errors }) => { - if (!errors) { - message.success({ content: 'Marked assets as un-deprecated!', duration: 2 }); - refetch?.(); - } - }) - .catch((e) => { - message.destroy(); - message.error({ - content: `Failed to mark assets as un-deprecated: \n ${e.message || ''}`, - duration: 3, - }); - }); - }; - - return ( - - {deprecation?.note !== '' && Deprecation note} - {isDividerNeeded && } - - {expanded || !overLimit ? ( - <> - {deprecation?.note && deprecation?.note !== '' && ( - <> - - - {overLimit && ( - { - setExpanded(false); - }} - > - Read Less - - )} - - - )} - - ) : ( - <> - - { - setExpanded(true); - }} - > - Read More - - - } - shouldWrap - > - {deprecation.note} - - - )} - - {deprecation?.decommissionTime !== null && ( - - - {decommissionTimeLocal} - - - )} - {isDividerNeeded && } - {showUndeprecate && ( - - Modal.confirm({ - title: `Confirm Mark as un-deprecated`, - content: `Are you sure you want to mark this asset as un-deprecated?`, - onOk() { - batchUndeprecate(); - }, - onCancel() {}, - okText: 'Yes', - maskClosable: true, - closable: true, - }) - } - > - - Mark as un-deprecated - - )} - - ) : ( - 'No additional details' - ) - } - > - - DEPRECATED - - - ); -}; diff --git a/datahub-web-react/src/app/entity/shared/components/styled/ERModelRelationship/CreateERModelRelationModal.tsx b/datahub-web-react/src/app/entity/shared/components/styled/ERModelRelationship/CreateERModelRelationModal.tsx deleted file mode 100644 index 2188fd39b0f11b..00000000000000 --- a/datahub-web-react/src/app/entity/shared/components/styled/ERModelRelationship/CreateERModelRelationModal.tsx +++ /dev/null @@ -1,455 +0,0 @@ -import '@app/entity/shared/components/styled/ERModelRelationship/CreateERModelRelationModal.less'; - -import { PlusOutlined } from '@ant-design/icons'; -import { Button, Form, Input, Modal, Select, Table, message } from 'antd'; -import TextArea from 'antd/lib/input/TextArea'; -import React, { useState } from 'react'; - -import { useUserContext } from '@app/context/useUserContext'; -import { - ERModelRelationDataType, - checkDuplicateERModelRelation, - getDatasetName, - validateERModelRelation, -} from '@app/entity/shared/components/styled/ERModelRelationship/ERModelRelationUtils'; -import { EditableCell } from '@app/entity/shared/components/styled/ERModelRelationship/EditableCell'; -import { EditableRow } from '@app/entity/shared/components/styled/ERModelRelationship/EditableRow'; - -import { - useCreateErModelRelationshipMutation, - useUpdateErModelRelationshipMutation, -} from '@graphql/ermodelrelationship.generated'; -import { useAddOwnerMutation } from '@graphql/mutations.generated'; -import { useGetSearchResultsQuery } from '@graphql/search.generated'; -import { EntityType, ErModelRelationship, ErModelRelationshipCardinality, OwnerEntityType } from '@types'; - -import arrow from '@images/Arrow.svg'; - -type Props = { - table1?: any; - table1Schema?: any; - table2?: any; - table2Schema?: any; - open: boolean; - setModalVisible?: any; - onCancel: () => void; - editERModelRelation?: ErModelRelationship; - isEditing?: boolean; - refetch: () => Promise; - entityName: any; -}; - -type EditableTableProps = Parameters[0]; -type ColumnTypes = Exclude; - -export const CreateERModelRelationModal = ({ - table1, - table1Schema, - table2, - table2Schema, - open, - setModalVisible, - onCancel, - editERModelRelation, - isEditing, - refetch, - entityName, -}: Props) => { - const [form] = Form.useForm(); - const { user } = useUserContext(); - const ownerEntityType = - user && user.type === EntityType.CorpGroup ? OwnerEntityType.CorpGroup : OwnerEntityType.CorpUser; - const table1Dataset = editERModelRelation?.properties?.source || table1?.dataset; - const table1DatasetSchema = editERModelRelation?.properties?.source || table1Schema; - const table2Dataset = editERModelRelation?.properties?.destination || table2?.dataset; - const table2DatasetSchema = editERModelRelation?.properties?.destination || table2Schema?.dataset; - - const [details, setDetails] = useState(editERModelRelation?.editableProperties?.description || ''); - const [ermodelrelationName, setERModelRelationName] = useState( - editERModelRelation?.editableProperties?.name || - editERModelRelation?.properties?.name || - editERModelRelation?.id || - '', - ); - const [ermodelrelationCardinality, setERModelRelationCardinality] = useState( - editERModelRelation?.properties?.cardinality, - ); - const [tableData, setTableData] = useState( - editERModelRelation?.properties?.relationshipFieldMappings?.map((item, index) => { - return { - key: index, - field1Name: item.sourceField, - field2Name: item.destinationField, - }; - }) || [ - { key: '0', field1Name: '', field2Name: '' }, - { key: '1', field1Name: '', field2Name: '' }, - ], - ); - const [count, setCount] = useState(editERModelRelation?.properties?.relationshipFieldMappings?.length || 2); - const [createMutation] = useCreateErModelRelationshipMutation(); - const [updateMutation] = useUpdateErModelRelationshipMutation(); - const [addOwnerMutation] = useAddOwnerMutation(); - const { refetch: getSearchResultsERModelRelations } = useGetSearchResultsQuery({ - skip: true, - }); - - const handleDelete = (record) => { - const newData = tableData.filter((item) => item.key !== record.key); - setTableData(newData); - }; - const onCancelSelect = () => { - Modal.confirm({ - title: `Exit`, - className: 'cancel-modal', - content: `Are you sure you want to exit? The changes made to the ${entityName} will not be applied.`, - onOk() { - setERModelRelationName(editERModelRelation?.properties?.name || ''); - setERModelRelationCardinality(editERModelRelation?.properties?.cardinality); - setDetails(editERModelRelation?.editableProperties?.description || ''); - setTableData( - editERModelRelation?.properties?.relationshipFieldMappings?.map((item, index) => { - return { - key: index, - field1Name: item.sourceField, - field2Name: item.destinationField, - }; - }) || [ - { key: '0', field1Name: '', field2Name: '' }, - { key: '1', field1Name: '', field2Name: '' }, - ], - ); - setCount(editERModelRelation?.properties?.relationshipFieldMappings?.length || 2); - onCancel?.(); - }, - onCancel() {}, - okText: 'Yes', - maskClosable: true, - closable: true, - }); - }; - const createERModelRelationship = () => { - createMutation({ - variables: { - input: { - properties: { - source: table1Dataset?.urn || '', - destination: table2Dataset?.urn || '', - name: ermodelrelationName, - relationshipFieldmappings: tableData.map((r) => { - return { - sourceField: r.field1Name, - destinationField: r.field2Name, - }; - }), - erModelRelationshipCardinality: ermodelrelationCardinality, - created: true, - }, - editableProperties: { - name: ermodelrelationName, - description: details, - }, - }, - }, - }) - .then(({ data }) => { - message.loading({ - content: 'Create...', - duration: 2, - }); - setTimeout(() => { - refetch(); - message.success({ - content: `${entityName} created!`, - duration: 2, - }); - }, 2000); - addOwnerMutation({ - variables: { - input: { - ownerUrn: user?.urn || '', - resourceUrn: data?.createERModelRelationship?.urn || '', - ownershipTypeUrn: 'urn:li:ownershipType:__system__technical_owner', - ownerEntityType: ownerEntityType || EntityType, - }, - }, - }); - }) - .catch((e) => { - message.destroy(); - message.error({ content: `Failed to create ${entityName}: ${e.message || ''}`, duration: 3 }); - }); - }; - const originalERModelRelationName = editERModelRelation?.properties?.name; - const updateERModelRelationship = () => { - updateMutation({ - variables: { - urn: editERModelRelation?.urn || '', - input: { - properties: { - source: table1Dataset?.urn || '', - destination: table2Dataset?.urn || '', - name: originalERModelRelationName || '', - erModelRelationshipCardinality: ermodelrelationCardinality, - createdBy: editERModelRelation?.properties?.createdActor?.urn || user?.urn, - createdAt: editERModelRelation?.properties?.createdTime || 0, - relationshipFieldmappings: tableData.map((r) => { - return { - sourceField: r.field1Name, - destinationField: r.field2Name, - }; - }), - }, - editableProperties: { - name: ermodelrelationName, - description: details, - }, - }, - }, - }) - .then(() => { - message.loading({ - content: 'updating...', - duration: 2, - }); - setTimeout(() => { - refetch(); - message.success({ - content: `${entityName} updated!`, - duration: 2, - }); - }, 2000); - }) - .catch((e) => { - message.destroy(); - message.error({ content: `Failed to update ${entityName}: ${e.message || ''}`, duration: 3 }); - }); - }; - const onSubmit = async () => { - const errors = validateERModelRelation( - ermodelrelationName, - tableData, - isEditing, - getSearchResultsERModelRelations, - entityName, - ); - if ((await errors).length > 0) { - const err = (await errors).join(`, `); - message.error(err); - return; - } - if (isEditing) { - updateERModelRelationship(); - } else { - createERModelRelationship(); - setERModelRelationName(''); - setERModelRelationCardinality(undefined); - setDetails(''); - setTableData([ - { key: '0', field1Name: '', field2Name: '' }, - { key: '1', field1Name: '', field2Name: '' }, - ]); - setCount(2); - } - setModalVisible(false); - }; - - const table1NameBusiness = getDatasetName(table1Dataset); - const table1NameTech = table1Dataset?.name || table1Dataset?.urn?.split(',')?.at(1) || ''; - const table2NameBusiness = getDatasetName(table2Dataset); - const table2NameTech = table2Dataset?.name || table2Dataset?.urn?.split(',')?.at(1) || ''; - - const handleAdd = () => { - const newData: ERModelRelationDataType = { - key: count, - field1Name: '', - field2Name: '', - }; - setTableData([...tableData, newData]); - setCount(count + 1); - }; - const defaultColumns: (ColumnTypes[number] & { editable?: boolean; dataIndex: string; tableRecord?: any })[] = [ - { - title: ( -

-

- {table1NameBusiness || table1NameTech} -
-
{table1NameTech !== table1NameBusiness && table1NameTech}
-

- ), - dataIndex: 'field1Name', - tableRecord: table1DatasetSchema || {}, - editable: true, - }, - { - title: '', - dataIndex: '', - editable: false, - render: () => , - }, - { - title: ( -

-

- {table2NameBusiness || table2NameTech} -
-
{table2NameTech !== table2NameBusiness && table2NameTech}
-

- ), - dataIndex: 'field2Name', - tableRecord: table2DatasetSchema || {}, - editable: true, - }, - { - title: 'Action', - dataIndex: '', - editable: false, - render: (record) => - tableData.length > 1 ? ( - - ) : null, - }, - ]; - const handleSave = (row: ERModelRelationDataType) => { - const newData = [...tableData]; - const index = newData.findIndex((item) => row.key === item.key); - const item = newData[index]; - newData.splice(index, 1, { - ...item, - ...row, - }); - setTableData(newData); - }; - const components = { - body: { - row: EditableRow, - cell: EditableCell, - }, - }; - - const columns = defaultColumns.map((col) => { - if (!col.editable) { - return col; - } - return { - ...col, - onCell: (record: ERModelRelationDataType) => ({ - record, - editable: col.editable, - dataIndex: col.dataIndex, - tableRecord: col.tableRecord, - title: col.title, - handleSave, - }), - }; - }); - return ( - -

{entityName} Parameters

-
- -
-
- -
- - } - open={open} - closable={false} - className="CreateERModelRelationModal" - okButtonProps={{ hidden: true }} - cancelButtonProps={{ hidden: true }} - onCancel={onCancelSelect} - destroyOnClose - > -
-

Table 1

-

{table1NameBusiness}

-
{table1NameTech !== table1NameBusiness && table1NameTech}
-

Table 2

-

{table2NameBusiness}

-
{table2NameTech !== table2NameBusiness && table2NameTech}
-

{entityName} name

-
- - checkDuplicateERModelRelation(getSearchResultsERModelRelations, value?.trim()).then( - (result) => { - return result === true && !isEditing - ? Promise.reject( - new Error( - `This ${entityName} name already exists. A unique name for each ${entityName} is required.`, - ), - ) - : Promise.resolve(); - }, - ), - }, - ]} - > - setERModelRelationName(e.target.value)} - /> - -

Cardinality

- - - -

Fields

-
- -

{entityName} details

- -