mirror of
https://github.com/datahub-project/datahub.git
synced 2025-12-16 20:48:35 +00:00
feat(graphql,ui): Update ML system V2 UI (#12598)
This commit is contained in:
parent
5ab2378892
commit
67a6394a37
@ -183,9 +183,11 @@ export class DataProcessInstanceEntity implements Entity<DataProcessInstance> {
|
|||||||
parentContainers={data.parentContainers}
|
parentContainers={data.parentContainers}
|
||||||
parentEntities={parentEntities}
|
parentEntities={parentEntities}
|
||||||
container={data.container || undefined}
|
container={data.container || undefined}
|
||||||
duration={firstState?.durationMillis}
|
dataProcessInstanceProps={{
|
||||||
status={firstState?.result?.resultType}
|
startTime: firstState?.timestampMillis,
|
||||||
startTime={firstState?.timestampMillis}
|
duration: firstState?.durationMillis ?? undefined,
|
||||||
|
status: firstState?.result?.resultType ?? undefined,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -39,9 +39,7 @@ export const Preview = ({
|
|||||||
health,
|
health,
|
||||||
parentEntities,
|
parentEntities,
|
||||||
parentContainers,
|
parentContainers,
|
||||||
duration,
|
dataProcessInstanceProps,
|
||||||
status,
|
|
||||||
startTime,
|
|
||||||
}: {
|
}: {
|
||||||
urn: string;
|
urn: string;
|
||||||
name: string;
|
name: string;
|
||||||
@ -64,9 +62,11 @@ export const Preview = ({
|
|||||||
health?: Health[] | null;
|
health?: Health[] | null;
|
||||||
parentEntities?: Array<GeneratedEntity> | null;
|
parentEntities?: Array<GeneratedEntity> | null;
|
||||||
parentContainers?: ParentContainersResult | null;
|
parentContainers?: ParentContainersResult | null;
|
||||||
duration?: number | null;
|
dataProcessInstanceProps?: {
|
||||||
status?: string | null;
|
startTime?: number;
|
||||||
startTime?: number | null;
|
duration?: number;
|
||||||
|
status?: string;
|
||||||
|
};
|
||||||
}): JSX.Element => {
|
}): JSX.Element => {
|
||||||
const entityRegistry = useEntityRegistry();
|
const entityRegistry = useEntityRegistry();
|
||||||
return (
|
return (
|
||||||
@ -95,9 +95,7 @@ export const Preview = ({
|
|||||||
paths={paths}
|
paths={paths}
|
||||||
health={health || undefined}
|
health={health || undefined}
|
||||||
parentEntities={parentEntities}
|
parentEntities={parentEntities}
|
||||||
duration={duration}
|
dataProcessInstanceProps={dataProcessInstanceProps}
|
||||||
status={status}
|
|
||||||
startTime={startTime}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,13 +2,13 @@ import React from 'react';
|
|||||||
import * as QueryString from 'query-string';
|
import * as QueryString from 'query-string';
|
||||||
import { useHistory, useLocation } from 'react-router';
|
import { useHistory, useLocation } from 'react-router';
|
||||||
import { ApolloError } from '@apollo/client';
|
import { ApolloError } from '@apollo/client';
|
||||||
import { EntityType, FacetFilterInput } from '../../../../../../types.generated';
|
import { FacetFilterInput } from '../../../../../../types.generated';
|
||||||
import useFilters from '../../../../../search/utils/useFilters';
|
import useFilters from '../../../../../search/utils/useFilters';
|
||||||
import { navigateToEntitySearchUrl } from './navigateToEntitySearchUrl';
|
import { navigateToEntitySearchUrl } from './navigateToEntitySearchUrl';
|
||||||
import { FilterSet, GetSearchResultsParams, SearchResultsInterface } from './types';
|
import { FilterSet, GetSearchResultsParams, SearchResultsInterface } from './types';
|
||||||
import { useEntityQueryParams } from '../../../containers/profile/utils';
|
import { useEntityQueryParams } from '../../../containers/profile/utils';
|
||||||
import { EmbeddedListSearch } from './EmbeddedListSearch';
|
import { EmbeddedListSearch } from './EmbeddedListSearch';
|
||||||
import { UnionType } from '../../../../../search/utils/constants';
|
import { EMBEDDED_LIST_SEARCH_ENTITY_TYPES, UnionType } from '../../../../../search/utils/constants';
|
||||||
import {
|
import {
|
||||||
DownloadSearchResults,
|
DownloadSearchResults,
|
||||||
DownloadSearchResultsInput,
|
DownloadSearchResultsInput,
|
||||||
@ -16,30 +16,6 @@ import {
|
|||||||
} from '../../../../../search/utils/types';
|
} from '../../../../../search/utils/types';
|
||||||
|
|
||||||
const FILTER = 'filter';
|
const FILTER = 'filter';
|
||||||
const SEARCH_ENTITY_TYPES = [
|
|
||||||
EntityType.Dataset,
|
|
||||||
EntityType.Dashboard,
|
|
||||||
EntityType.Chart,
|
|
||||||
EntityType.Mlmodel,
|
|
||||||
EntityType.MlmodelGroup,
|
|
||||||
EntityType.MlfeatureTable,
|
|
||||||
EntityType.Mlfeature,
|
|
||||||
EntityType.MlprimaryKey,
|
|
||||||
EntityType.DataFlow,
|
|
||||||
EntityType.DataJob,
|
|
||||||
EntityType.GlossaryTerm,
|
|
||||||
EntityType.GlossaryNode,
|
|
||||||
EntityType.Tag,
|
|
||||||
EntityType.Role,
|
|
||||||
EntityType.CorpUser,
|
|
||||||
EntityType.CorpGroup,
|
|
||||||
EntityType.Container,
|
|
||||||
EntityType.Domain,
|
|
||||||
EntityType.DataProduct,
|
|
||||||
EntityType.Notebook,
|
|
||||||
EntityType.BusinessAttribute,
|
|
||||||
EntityType.DataProcessInstance,
|
|
||||||
];
|
|
||||||
|
|
||||||
function getParamsWithoutFilters(params: QueryString.ParsedQuery<string>) {
|
function getParamsWithoutFilters(params: QueryString.ParsedQuery<string>) {
|
||||||
const paramsCopy = { ...params };
|
const paramsCopy = { ...params };
|
||||||
@ -161,7 +137,7 @@ export const EmbeddedListSearchSection = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<EmbeddedListSearch
|
<EmbeddedListSearch
|
||||||
entityTypes={SEARCH_ENTITY_TYPES}
|
entityTypes={EMBEDDED_LIST_SEARCH_ENTITY_TYPES}
|
||||||
query={query || ''}
|
query={query || ''}
|
||||||
page={page}
|
page={page}
|
||||||
unionType={unionType}
|
unionType={unionType}
|
||||||
|
|||||||
@ -45,6 +45,7 @@ import {
|
|||||||
Documentation,
|
Documentation,
|
||||||
DisplayProperties,
|
DisplayProperties,
|
||||||
VersionProperties,
|
VersionProperties,
|
||||||
|
DataProcessRunEvent,
|
||||||
} from '../../../types.generated';
|
} from '../../../types.generated';
|
||||||
import { FetchedEntity } from '../../lineage/types';
|
import { FetchedEntity } from '../../lineage/types';
|
||||||
|
|
||||||
@ -135,6 +136,9 @@ export type GenericEntityProperties = {
|
|||||||
displayProperties?: Maybe<DisplayProperties>;
|
displayProperties?: Maybe<DisplayProperties>;
|
||||||
notes?: Maybe<EntityRelationshipsResult>;
|
notes?: Maybe<EntityRelationshipsResult>;
|
||||||
versionProperties?: Maybe<VersionProperties>;
|
versionProperties?: Maybe<VersionProperties>;
|
||||||
|
|
||||||
|
// Data process instance
|
||||||
|
lastRunEvent?: Maybe<DataProcessRunEvent>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GenericEntityUpdate = {
|
export type GenericEntityUpdate = {
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import DataProcessInstanceSummary from '@src/app/entity/dataProcessInstance/profile/DataProcessInstanceSummary';
|
||||||
import { GenericEntityProperties } from '@app/entity/shared/types';
|
import { GenericEntityProperties } from '@app/entity/shared/types';
|
||||||
import { Entity as GraphQLEntity } from '@types';
|
import { Entity as GraphQLEntity } from '@types';
|
||||||
import { globalEntityRegistryV2 } from '@app/EntityRegistryProvider';
|
import { globalEntityRegistryV2 } from '@app/EntityRegistryProvider';
|
||||||
@ -14,7 +15,6 @@ import { ArrowsClockwise } from 'phosphor-react';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { DataProcessInstance, EntityType, SearchResult } from '../../../types.generated';
|
import { DataProcessInstance, EntityType, SearchResult } from '../../../types.generated';
|
||||||
import Preview from './preview/Preview';
|
import Preview from './preview/Preview';
|
||||||
import DataProcessInstanceSummary from './profile/DataProcessInstanceSummary';
|
|
||||||
|
|
||||||
const getParentEntities = (data: DataProcessInstance): GraphQLEntity[] => {
|
const getParentEntities = (data: DataProcessInstance): GraphQLEntity[] => {
|
||||||
const parentEntity = data?.relationships?.relationships?.find(
|
const parentEntity = data?.relationships?.relationships?.find(
|
||||||
@ -79,9 +79,7 @@ export class DataProcessInstanceEntity implements Entity<DataProcessInstance> {
|
|||||||
useEntityQuery={this.useEntityQuery}
|
useEntityQuery={this.useEntityQuery}
|
||||||
// useUpdateQuery={useUpdateDataProcessInstanceMutation}
|
// useUpdateQuery={useUpdateDataProcessInstanceMutation}
|
||||||
getOverrideProperties={this.getOverridePropertiesFromEntity}
|
getOverrideProperties={this.getOverridePropertiesFromEntity}
|
||||||
headerDropdownItems={
|
headerDropdownItems={new Set([EntityMenuItems.SHARE])}
|
||||||
new Set([EntityMenuItems.UPDATE_DEPRECATION, EntityMenuItems.RAISE_INCIDENT, EntityMenuItems.SHARE])
|
|
||||||
}
|
|
||||||
tabs={[
|
tabs={[
|
||||||
{
|
{
|
||||||
name: 'Summary',
|
name: 'Summary',
|
||||||
@ -115,6 +113,8 @@ export class DataProcessInstanceEntity implements Entity<DataProcessInstance> {
|
|||||||
(processInstance as GetDataProcessInstanceQuery['dataProcessInstance'])?.optionalPlatform ||
|
(processInstance as GetDataProcessInstanceQuery['dataProcessInstance'])?.optionalPlatform ||
|
||||||
parent?.platform,
|
parent?.platform,
|
||||||
parent,
|
parent,
|
||||||
|
// Not currently rendered in V2
|
||||||
|
lastRunEvent: processInstance?.state?.[0],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,102 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { Space, Table, Typography } from 'antd';
|
|
||||||
import { formatDetailedDuration } from '@src/app/shared/time/timeUtils';
|
|
||||||
import { capitalize } from 'lodash';
|
|
||||||
import moment from 'moment';
|
|
||||||
import { MlHyperParam, MlMetric, DataProcessInstanceRunResultType } from '../../../../types.generated';
|
|
||||||
import { useBaseEntity } from '../../../entity/shared/EntityContext';
|
|
||||||
import { InfoItem } from '../../shared/components/styled/InfoItem';
|
|
||||||
import { GetDataProcessInstanceQuery } from '../../../../graphql/dataProcessInstance.generated';
|
|
||||||
import { Pill } from '../../../../alchemy-components/components/Pills';
|
|
||||||
|
|
||||||
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;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const propertyTableColumns = [
|
|
||||||
{
|
|
||||||
title: 'Name',
|
|
||||||
dataIndex: 'name',
|
|
||||||
width: 450,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Value',
|
|
||||||
dataIndex: 'value',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function DataProcessInstanceSummary() {
|
|
||||||
const baseEntity = useBaseEntity<GetDataProcessInstanceQuery>();
|
|
||||||
const dpi = baseEntity?.dataProcessInstance;
|
|
||||||
|
|
||||||
const formatStatus = (state) => {
|
|
||||||
if (!state || state.length === 0) return '-';
|
|
||||||
const result = state[0]?.result?.resultType;
|
|
||||||
const statusColor = result === DataProcessInstanceRunResultType.Success ? 'green' : 'red';
|
|
||||||
return <Pill label={capitalize(result)} color={statusColor} clickable={false} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const formatDuration = (state) => {
|
|
||||||
if (!state || state.length === 0) return '-';
|
|
||||||
return formatDetailedDuration(state[0]?.durationMillis);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TabContent>
|
|
||||||
<Space direction="vertical" style={{ width: '100%' }} size="large">
|
|
||||||
<Typography.Title level={3}>Details</Typography.Title>
|
|
||||||
<InfoItemContainer justifyContent="left">
|
|
||||||
<InfoItem title="Created At">
|
|
||||||
<InfoItemContent>
|
|
||||||
{dpi?.properties?.created?.time
|
|
||||||
? moment(dpi.properties.created.time).format('YYYY-MM-DD HH:mm:ss')
|
|
||||||
: '-'}
|
|
||||||
</InfoItemContent>
|
|
||||||
</InfoItem>
|
|
||||||
<InfoItem title="Status">
|
|
||||||
<InfoItemContent>{formatStatus(dpi?.state)}</InfoItemContent>
|
|
||||||
</InfoItem>
|
|
||||||
<InfoItem title="Duration">
|
|
||||||
<InfoItemContent>{formatDuration(dpi?.state)}</InfoItemContent>
|
|
||||||
</InfoItem>
|
|
||||||
<InfoItem title="Run ID">
|
|
||||||
<InfoItemContent>{dpi?.mlTrainingRunProperties?.id}</InfoItemContent>
|
|
||||||
</InfoItem>
|
|
||||||
<InfoItem title="Created By">
|
|
||||||
<InfoItemContent>{dpi?.properties?.created?.actor}</InfoItemContent>
|
|
||||||
</InfoItem>
|
|
||||||
</InfoItemContainer>
|
|
||||||
<InfoItemContainer justifyContent="left">
|
|
||||||
<InfoItem title="Artifacts Location">
|
|
||||||
<InfoItemContent>{dpi?.mlTrainingRunProperties?.outputUrls}</InfoItemContent>
|
|
||||||
</InfoItem>
|
|
||||||
</InfoItemContainer>
|
|
||||||
<Typography.Title level={3}>Training Metrics</Typography.Title>
|
|
||||||
<Table
|
|
||||||
pagination={false}
|
|
||||||
columns={propertyTableColumns}
|
|
||||||
dataSource={dpi?.mlTrainingRunProperties?.trainingMetrics as MlMetric[]}
|
|
||||||
/>
|
|
||||||
<Typography.Title level={3}>Hyper Parameters</Typography.Title>
|
|
||||||
<Table
|
|
||||||
pagination={false}
|
|
||||||
columns={propertyTableColumns}
|
|
||||||
dataSource={dpi?.mlTrainingRunProperties?.hyperParams as MlHyperParam[]}
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
</TabContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import { CodeSandboxOutlined, UnorderedListOutlined } from '@ant-design/icons';
|
import { CodeSandboxOutlined, PartitionOutlined, UnorderedListOutlined } from '@ant-design/icons';
|
||||||
|
import { LineageTab } from '@app/entityV2/shared/tabs/Lineage/LineageTab';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useGetMlModelQuery } from '../../../graphql/mlModel.generated';
|
import { useGetMlModelQuery } from '../../../graphql/mlModel.generated';
|
||||||
import { EntityType, MlModel, SearchResult } from '../../../types.generated';
|
import { EntityType, MlModel, SearchResult } from '../../../types.generated';
|
||||||
@ -80,6 +81,8 @@ export class MLModelEntity implements Entity<MlModel> {
|
|||||||
|
|
||||||
getOverridePropertiesFromEntity = (mlModel?: MlModel | null): GenericEntityProperties => {
|
getOverridePropertiesFromEntity = (mlModel?: MlModel | null): GenericEntityProperties => {
|
||||||
return {
|
return {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/dot-notation
|
||||||
|
name: mlModel && this.displayName(mlModel),
|
||||||
externalUrl: mlModel?.properties?.externalUrl,
|
externalUrl: mlModel?.properties?.externalUrl,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -103,6 +106,11 @@ export class MLModelEntity implements Entity<MlModel> {
|
|||||||
name: 'Documentation',
|
name: 'Documentation',
|
||||||
component: DocumentationTab,
|
component: DocumentationTab,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Lineage',
|
||||||
|
component: LineageTab,
|
||||||
|
icon: PartitionOutlined,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Properties',
|
name: 'Properties',
|
||||||
component: PropertiesTab,
|
component: PropertiesTab,
|
||||||
@ -193,8 +201,7 @@ export class MLModelEntity implements Entity<MlModel> {
|
|||||||
getLineageVizConfig = (entity: MlModel) => {
|
getLineageVizConfig = (entity: MlModel) => {
|
||||||
return {
|
return {
|
||||||
urn: entity.urn,
|
urn: entity.urn,
|
||||||
// eslint-disable-next-line @typescript-eslint/dot-notation
|
name: entity && this.displayName(entity),
|
||||||
name: entity.properties?.['propertiesName'] || entity.name,
|
|
||||||
type: EntityType.Mlmodel,
|
type: EntityType.Mlmodel,
|
||||||
icon: entity.platform?.properties?.logoUrl || undefined,
|
icon: entity.platform?.properties?.logoUrl || undefined,
|
||||||
platform: entity.platform,
|
platform: entity.platform,
|
||||||
@ -203,11 +210,16 @@ export class MLModelEntity implements Entity<MlModel> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
displayName = (data: MlModel) => {
|
displayName = (data: MlModel) => {
|
||||||
return data.properties?.name || data.name || data.urn;
|
// eslint-disable-next-line @typescript-eslint/dot-notation
|
||||||
|
return data.properties?.['propertiesName'] || data.properties?.name || data.name || data.urn;
|
||||||
};
|
};
|
||||||
|
|
||||||
getGenericEntityProperties = (mlModel: MlModel) => {
|
getGenericEntityProperties = (mlModel: MlModel) => {
|
||||||
return getDataForEntityType({ data: mlModel, entityType: this.type, getOverrideProperties: (data) => data });
|
return getDataForEntityType({
|
||||||
|
data: mlModel,
|
||||||
|
entityType: this.type,
|
||||||
|
getOverrideProperties: this.getOverridePropertiesFromEntity,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
supportedCapabilities = () => {
|
supportedCapabilities = () => {
|
||||||
|
|||||||
@ -31,8 +31,7 @@ export const Preview = ({
|
|||||||
return (
|
return (
|
||||||
<DefaultPreviewCard
|
<DefaultPreviewCard
|
||||||
url={entityRegistry.getEntityUrl(EntityType.Mlmodel, model.urn)}
|
url={entityRegistry.getEntityUrl(EntityType.Mlmodel, model.urn)}
|
||||||
// eslint-disable-next-line @typescript-eslint/dot-notation
|
name={data?.name || ''}
|
||||||
name={model?.properties?.['propertiesName'] || model?.name || ''}
|
|
||||||
urn={model.urn}
|
urn={model.urn}
|
||||||
data={data}
|
data={data}
|
||||||
description={model.description || ''}
|
description={model.description || ''}
|
||||||
|
|||||||
@ -1,18 +1,47 @@
|
|||||||
|
import { useBaseEntity } from '@app/entity/shared/EntityContext';
|
||||||
|
import { InfoItem } from '@app/entityV2/shared/components/styled/InfoItem';
|
||||||
|
import { notEmpty } from '@app/entityV2/shared/utils';
|
||||||
|
import { useEntityRegistry } from '@app/useEntityRegistry';
|
||||||
|
import { Pill } from '@components';
|
||||||
|
import { GetMlModelQuery } from '@graphql/mlModel.generated';
|
||||||
|
import { EntityType, MlHyperParam, MlMetric } from '@types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Space, Table, Typography } from 'antd';
|
import { Space, Table, Typography } from 'antd';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
import { MlHyperParam, MlMetric } from '../../../../types.generated';
|
import { colors } from '@src/alchemy-components/theme';
|
||||||
import { useBaseEntity } from '../../../entity/shared/EntityContext';
|
import moment from 'moment';
|
||||||
import { GetMlModelQuery } from '../../../../graphql/mlModel.generated';
|
|
||||||
|
|
||||||
const TabContent = styled.div`
|
const TabContent = styled.div`
|
||||||
padding: 16px;
|
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;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 5px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const JobLink = styled(Link)`
|
||||||
|
color: ${colors.blue[700]};
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export default function MLModelSummary() {
|
export default function MLModelSummary() {
|
||||||
const baseEntity = useBaseEntity<GetMlModelQuery>();
|
const baseEntity = useBaseEntity<GetMlModelQuery>();
|
||||||
const model = baseEntity?.mlModel;
|
const model = baseEntity?.mlModel;
|
||||||
|
const entityRegistry = useEntityRegistry();
|
||||||
|
|
||||||
const propertyTableColumns = [
|
const propertyTableColumns = [
|
||||||
{
|
{
|
||||||
@ -26,9 +55,72 @@ export default function MLModelSummary() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const renderTrainingJobs = () => {
|
||||||
|
const trainingJobs =
|
||||||
|
model?.trainedBy?.relationships?.map((relationship) => relationship.entity).filter(notEmpty) || [];
|
||||||
|
|
||||||
|
if (trainingJobs.length === 0) return '-';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{trainingJobs.map((job, index) => {
|
||||||
|
const { urn, name } = job as { urn: string; name?: string };
|
||||||
|
return (
|
||||||
|
<span key={urn}>
|
||||||
|
<JobLink to={entityRegistry.getEntityUrl(EntityType.DataProcessInstance, urn)}>
|
||||||
|
{name || urn}
|
||||||
|
</JobLink>
|
||||||
|
{index < trainingJobs.length - 1 && ', '}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TabContent>
|
<TabContent>
|
||||||
<Space direction="vertical" style={{ width: '100%' }} size="large">
|
<Space direction="vertical" style={{ width: '100%' }} size="large">
|
||||||
|
<Typography.Title level={3}>Model Details</Typography.Title>
|
||||||
|
<InfoItemContainer justifyContent="left">
|
||||||
|
<InfoItem title="Version">
|
||||||
|
<InfoItemContent>{model?.versionProperties?.version?.versionTag}</InfoItemContent>
|
||||||
|
</InfoItem>
|
||||||
|
<InfoItem title="Registered At">
|
||||||
|
<InfoItemContent>
|
||||||
|
{model?.properties?.created?.time
|
||||||
|
? moment(model.properties.created.time).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
: '-'}
|
||||||
|
</InfoItemContent>
|
||||||
|
</InfoItem>
|
||||||
|
<InfoItem title="Last Modified At">
|
||||||
|
<InfoItemContent>
|
||||||
|
{model?.properties?.lastModified?.time
|
||||||
|
? moment(model.properties.lastModified.time).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
: '-'}
|
||||||
|
</InfoItemContent>
|
||||||
|
</InfoItem>
|
||||||
|
<InfoItem title="Created By">
|
||||||
|
<InfoItemContent>{model?.properties?.created?.actor}</InfoItemContent>
|
||||||
|
</InfoItem>
|
||||||
|
</InfoItemContainer>
|
||||||
|
<InfoItemContainer justifyContent="left">
|
||||||
|
<InfoItem title="Aliases">
|
||||||
|
<InfoItemContent>
|
||||||
|
{model?.versionProperties?.aliases?.map((alias) => (
|
||||||
|
<Pill
|
||||||
|
label={alias.versionTag ?? '-'}
|
||||||
|
key={alias.versionTag}
|
||||||
|
color="blue"
|
||||||
|
clickable={false}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</InfoItemContent>
|
||||||
|
</InfoItem>
|
||||||
|
<InfoItem title="Source Run">
|
||||||
|
<InfoItemContent>{renderTrainingJobs()}</InfoItemContent>
|
||||||
|
</InfoItem>
|
||||||
|
</InfoItemContainer>
|
||||||
<Typography.Title level={3}>Training Metrics</Typography.Title>
|
<Typography.Title level={3}>Training Metrics</Typography.Title>
|
||||||
<Table
|
<Table
|
||||||
pagination={false}
|
pagination={false}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { CodeSandboxOutlined, UnorderedListOutlined } from '@ant-design/icons';
|
import { CodeSandboxOutlined, PartitionOutlined, UnorderedListOutlined } from '@ant-design/icons';
|
||||||
|
import { LineageTab } from '@app/entityV2/shared/tabs/Lineage/LineageTab';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useGetMlModelGroupQuery } from '../../../graphql/mlModelGroup.generated';
|
import { useGetMlModelGroupQuery } from '../../../graphql/mlModelGroup.generated';
|
||||||
import { EntityType, MlModelGroup, SearchResult } from '../../../types.generated';
|
import { EntityType, MlModelGroup, SearchResult } from '../../../types.generated';
|
||||||
@ -24,7 +25,11 @@ import { Preview } from './preview/Preview';
|
|||||||
import ModelGroupModels from './profile/ModelGroupModels';
|
import ModelGroupModels from './profile/ModelGroupModels';
|
||||||
import SidebarNotesSection from '../shared/sidebarSection/SidebarNotesSection';
|
import SidebarNotesSection from '../shared/sidebarSection/SidebarNotesSection';
|
||||||
|
|
||||||
const headerDropdownItems = new Set([EntityMenuItems.UPDATE_DEPRECATION, EntityMenuItems.ANNOUNCE]);
|
const headerDropdownItems = new Set([
|
||||||
|
EntityMenuItems.SHARE,
|
||||||
|
EntityMenuItems.UPDATE_DEPRECATION,
|
||||||
|
EntityMenuItems.ANNOUNCE,
|
||||||
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Definition of the DataHub MlModelGroup entity.
|
* Definition of the DataHub MlModelGroup entity.
|
||||||
@ -70,8 +75,10 @@ export class MLModelGroupEntity implements Entity<MlModelGroup> {
|
|||||||
|
|
||||||
getCollectionName = () => 'ML Groups';
|
getCollectionName = () => 'ML Groups';
|
||||||
|
|
||||||
getOverridePropertiesFromEntity = (_?: MlModelGroup | null): GenericEntityProperties => {
|
getOverridePropertiesFromEntity = (mlModelGroup?: MlModelGroup | null): GenericEntityProperties => {
|
||||||
return {};
|
return {
|
||||||
|
name: mlModelGroup && this.displayName(mlModelGroup),
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
useEntityQuery = useGetMlModelGroupQuery;
|
useEntityQuery = useGetMlModelGroupQuery;
|
||||||
@ -93,6 +100,11 @@ export class MLModelGroupEntity implements Entity<MlModelGroup> {
|
|||||||
name: 'Documentation',
|
name: 'Documentation',
|
||||||
component: DocumentationTab,
|
component: DocumentationTab,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Lineage',
|
||||||
|
component: LineageTab,
|
||||||
|
icon: PartitionOutlined,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Properties',
|
name: 'Properties',
|
||||||
component: PropertiesTab,
|
component: PropertiesTab,
|
||||||
@ -175,8 +187,7 @@ export class MLModelGroupEntity implements Entity<MlModelGroup> {
|
|||||||
getLineageVizConfig = (entity: MlModelGroup) => {
|
getLineageVizConfig = (entity: MlModelGroup) => {
|
||||||
return {
|
return {
|
||||||
urn: entity.urn,
|
urn: entity.urn,
|
||||||
// eslint-disable-next-line @typescript-eslint/dot-notation
|
name: entity && this.displayName(entity),
|
||||||
name: entity.properties?.['propertiesName'] || entity.name,
|
|
||||||
type: EntityType.MlmodelGroup,
|
type: EntityType.MlmodelGroup,
|
||||||
icon: entity.platform?.properties?.logoUrl || undefined,
|
icon: entity.platform?.properties?.logoUrl || undefined,
|
||||||
platform: entity.platform,
|
platform: entity.platform,
|
||||||
@ -185,14 +196,15 @@ export class MLModelGroupEntity implements Entity<MlModelGroup> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
displayName = (data: MlModelGroup) => {
|
displayName = (data: MlModelGroup) => {
|
||||||
return data.properties?.name || data.name || data.urn;
|
// eslint-disable-next-line @typescript-eslint/dot-notation
|
||||||
|
return data.properties?.['propertiesName'] || data.properties?.name || data.name || data.urn;
|
||||||
};
|
};
|
||||||
|
|
||||||
getGenericEntityProperties = (mlModelGroup: MlModelGroup) => {
|
getGenericEntityProperties = (mlModelGroup: MlModelGroup) => {
|
||||||
return getDataForEntityType({
|
return getDataForEntityType({
|
||||||
data: mlModelGroup,
|
data: mlModelGroup,
|
||||||
entityType: this.type,
|
entityType: this.type,
|
||||||
getOverrideProperties: (data) => data,
|
getOverrideProperties: this.getOverridePropertiesFromEntity,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -30,8 +30,7 @@ export const Preview = ({
|
|||||||
return (
|
return (
|
||||||
<DefaultPreviewCard
|
<DefaultPreviewCard
|
||||||
url={entityRegistry.getEntityUrl(EntityType.MlmodelGroup, group.urn)}
|
url={entityRegistry.getEntityUrl(EntityType.MlmodelGroup, group.urn)}
|
||||||
// eslint-disable-next-line @typescript-eslint/dot-notation
|
name={data?.name || ''}
|
||||||
name={group?.properties?.['propertiesName'] || group?.name || ''}
|
|
||||||
urn={group.urn}
|
urn={group.urn}
|
||||||
data={data}
|
data={data}
|
||||||
platformInstanceId={group.dataPlatformInstance?.instanceId}
|
platformInstanceId={group.dataPlatformInstance?.instanceId}
|
||||||
|
|||||||
@ -1,32 +1,200 @@
|
|||||||
import { List, Space, Typography } from 'antd';
|
import { useBaseEntity } from '@app/entity/shared/EntityContext';
|
||||||
|
import { EmptyTab } from '@app/entityV2/shared/components/styled/EmptyTab';
|
||||||
|
import { InfoItem } from '@app/entityV2/shared/components/styled/InfoItem';
|
||||||
|
import { notEmpty } from '@app/entityV2/shared/utils';
|
||||||
|
import { useEntityRegistry } from '@app/useEntityRegistry';
|
||||||
|
import { GetMlModelGroupQuery } from '@graphql/mlModelGroup.generated';
|
||||||
|
import { EntityType } from '@types';
|
||||||
|
import { Typography, Table } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { GetMlModelGroupQuery } from '../../../../graphql/mlModelGroup.generated';
|
import styled from 'styled-components';
|
||||||
import { EntityType } from '../../../../types.generated';
|
import { colors } from '@src/alchemy-components/theme';
|
||||||
import { useEntityRegistry } from '../../../useEntityRegistry';
|
import { Pill } from '@src/alchemy-components/components/Pills';
|
||||||
import { PreviewType } from '../../Entity';
|
import moment from 'moment';
|
||||||
import { useBaseEntity } from '../../../entity/shared/EntityContext';
|
|
||||||
|
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() {
|
export default function MLGroupModels() {
|
||||||
const baseEntity = useBaseEntity<GetMlModelGroupQuery>();
|
const baseEntity = useBaseEntity<GetMlModelGroupQuery>();
|
||||||
const models = baseEntity?.mlModelGroup?.incoming?.relationships?.map((relationship) => relationship.entity) || [];
|
|
||||||
|
|
||||||
const entityRegistry = useEntityRegistry();
|
const entityRegistry = useEntityRegistry();
|
||||||
|
const modelGroup = baseEntity?.mlModelGroup;
|
||||||
|
|
||||||
|
const models =
|
||||||
|
baseEntity?.mlModelGroup?.incoming?.relationships
|
||||||
|
?.map((relationship) => relationship.entity)
|
||||||
|
.filter(notEmpty)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/dot-notation
|
||||||
|
?.sort((a, b) => b?.['properties']?.createdTS?.time - a?.['properties']?.createdTS?.time) || [];
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'Name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
width: 300,
|
||||||
|
render: (_: any, record) => (
|
||||||
|
<NameContainer>
|
||||||
|
<NameLink href={entityRegistry.getEntityUrl(EntityType.Mlmodel, record.urn)}>
|
||||||
|
{record?.properties?.propertiesName || record?.name}
|
||||||
|
</NameLink>
|
||||||
|
</NameContainer>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Version',
|
||||||
|
key: 'version',
|
||||||
|
width: 70,
|
||||||
|
render: (_: any, record: any) => (
|
||||||
|
<VersionContainer>{record.versionProperties?.version?.versionTag || '-'}</VersionContainer>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Created At',
|
||||||
|
key: 'createdAt',
|
||||||
|
width: 150,
|
||||||
|
render: (_: any, record: any) => (
|
||||||
|
<Typography.Text>
|
||||||
|
{record.properties?.createdTS?.time
|
||||||
|
? moment(record.properties.createdTS.time).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
: '-'}
|
||||||
|
</Typography.Text>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Aliases',
|
||||||
|
key: 'aliases',
|
||||||
|
width: 200,
|
||||||
|
render: (_: any, record: any) => {
|
||||||
|
const aliases = record.versionProperties?.aliases || [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TagContainer>
|
||||||
|
{aliases.map((alias) => (
|
||||||
|
<Pill key={alias.versionTag} label={alias.versionTag} color="blue" clickable={false} />
|
||||||
|
))}
|
||||||
|
</TagContainer>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Tags',
|
||||||
|
key: 'tags',
|
||||||
|
width: 200,
|
||||||
|
render: (_: any, record: any) => {
|
||||||
|
const tags = record.properties?.tags || [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TagContainer>
|
||||||
|
{tags.map((tag) => (
|
||||||
|
<Pill key={tag} label={tag} clickable={false} />
|
||||||
|
))}
|
||||||
|
</TagContainer>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Description',
|
||||||
|
dataIndex: 'description',
|
||||||
|
key: 'description',
|
||||||
|
width: 300,
|
||||||
|
render: (_: any, record: any) => {
|
||||||
|
const editableDesc = record.editableProperties?.description;
|
||||||
|
const originalDesc = record.description;
|
||||||
|
|
||||||
|
return <Typography.Text>{editableDesc || originalDesc || '-'}</Typography.Text>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<ModelsContainer>
|
||||||
<Space direction="vertical" style={{ width: '100%' }} size="large">
|
<Typography.Title level={3}>Model Group Details</Typography.Title>
|
||||||
<List
|
<InfoItemContainer justifyContent="left">
|
||||||
style={{ padding: '16px 16px' }}
|
<InfoItem title="Created At">
|
||||||
bordered
|
<InfoItemContent>
|
||||||
dataSource={models}
|
{modelGroup?.properties?.created?.time
|
||||||
header={<Typography.Title level={3}>Models</Typography.Title>}
|
? moment(modelGroup.properties.created.time).format('YYYY-MM-DD HH:mm:ss')
|
||||||
renderItem={(item) => (
|
: '-'}
|
||||||
<List.Item style={{ paddingTop: '20px' }}>
|
</InfoItemContent>
|
||||||
{entityRegistry.renderPreview(EntityType.Mlmodel, PreviewType.PREVIEW, item)}
|
</InfoItem>
|
||||||
</List.Item>
|
<InfoItem title="Last Modified At">
|
||||||
)}
|
<InfoItemContent>
|
||||||
/>
|
{modelGroup?.properties?.lastModified?.time
|
||||||
</Space>
|
? moment(modelGroup.properties.lastModified.time).format('YYYY-MM-DD HH:mm:ss')
|
||||||
</>
|
: '-'}
|
||||||
|
</InfoItemContent>
|
||||||
|
</InfoItem>
|
||||||
|
{modelGroup?.properties?.created?.actor && (
|
||||||
|
<InfoItem title="Created By">
|
||||||
|
<InfoItemContent>{modelGroup.properties.created?.actor}</InfoItemContent>
|
||||||
|
</InfoItem>
|
||||||
|
)}
|
||||||
|
</InfoItemContainer>
|
||||||
|
<Typography.Title level={3}>Models</Typography.Title>
|
||||||
|
<StyledTable
|
||||||
|
columns={columns}
|
||||||
|
dataSource={models}
|
||||||
|
pagination={false}
|
||||||
|
rowKey="urn"
|
||||||
|
expandable={{
|
||||||
|
defaultExpandAllRows: true,
|
||||||
|
expandRowByClick: true,
|
||||||
|
}}
|
||||||
|
locale={{
|
||||||
|
emptyText: <EmptyTab tab="mlModel" />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ModelsContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,13 +4,13 @@ import { useHistory, useLocation } from 'react-router';
|
|||||||
import { ApolloError } from '@apollo/client';
|
import { ApolloError } from '@apollo/client';
|
||||||
import useSortInput from '@src/app/searchV2/sorting/useSortInput';
|
import useSortInput from '@src/app/searchV2/sorting/useSortInput';
|
||||||
import { useSelectedSortOption } from '@src/app/search/context/SearchContext';
|
import { useSelectedSortOption } from '@src/app/search/context/SearchContext';
|
||||||
import { EntityType, FacetFilterInput } from '../../../../../../types.generated';
|
import { FacetFilterInput } from '../../../../../../types.generated';
|
||||||
import useFilters from '../../../../../search/utils/useFilters';
|
import useFilters from '../../../../../search/utils/useFilters';
|
||||||
import { navigateToEntitySearchUrl } from './navigateToEntitySearchUrl';
|
import { navigateToEntitySearchUrl } from './navigateToEntitySearchUrl';
|
||||||
import { FilterSet, GetSearchResultsParams, SearchResultsInterface } from './types';
|
import { FilterSet, GetSearchResultsParams, SearchResultsInterface } from './types';
|
||||||
import { useEntityQueryParams } from '../../../containers/profile/utils';
|
import { useEntityQueryParams } from '../../../containers/profile/utils';
|
||||||
import { EmbeddedListSearch } from './EmbeddedListSearch';
|
import { EmbeddedListSearch } from './EmbeddedListSearch';
|
||||||
import { UnionType } from '../../../../../search/utils/constants';
|
import { EMBEDDED_LIST_SEARCH_ENTITY_TYPES, UnionType } from '../../../../../search/utils/constants';
|
||||||
import {
|
import {
|
||||||
DownloadSearchResults,
|
DownloadSearchResults,
|
||||||
DownloadSearchResultsInput,
|
DownloadSearchResultsInput,
|
||||||
@ -19,30 +19,6 @@ import {
|
|||||||
import { decodeComma } from '../../../utils';
|
import { decodeComma } from '../../../utils';
|
||||||
|
|
||||||
const FILTER = 'filter';
|
const FILTER = 'filter';
|
||||||
const SEARCH_ENTITY_TYPES = [
|
|
||||||
EntityType.Dataset,
|
|
||||||
EntityType.Dashboard,
|
|
||||||
EntityType.Chart,
|
|
||||||
EntityType.Mlmodel,
|
|
||||||
EntityType.MlmodelGroup,
|
|
||||||
EntityType.MlfeatureTable,
|
|
||||||
EntityType.Mlfeature,
|
|
||||||
EntityType.MlprimaryKey,
|
|
||||||
EntityType.DataFlow,
|
|
||||||
EntityType.DataJob,
|
|
||||||
EntityType.GlossaryTerm,
|
|
||||||
EntityType.GlossaryNode,
|
|
||||||
EntityType.Tag,
|
|
||||||
EntityType.Role,
|
|
||||||
EntityType.CorpUser,
|
|
||||||
EntityType.CorpGroup,
|
|
||||||
EntityType.Container,
|
|
||||||
EntityType.Domain,
|
|
||||||
EntityType.DataProduct,
|
|
||||||
EntityType.Notebook,
|
|
||||||
EntityType.BusinessAttribute,
|
|
||||||
EntityType.DataProcessInstance,
|
|
||||||
];
|
|
||||||
|
|
||||||
function getParamsWithoutFilters(params: QueryString.ParsedQuery<string>) {
|
function getParamsWithoutFilters(params: QueryString.ParsedQuery<string>) {
|
||||||
const paramsCopy = { ...params };
|
const paramsCopy = { ...params };
|
||||||
@ -170,7 +146,7 @@ export const EmbeddedListSearchSection = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<EmbeddedListSearch
|
<EmbeddedListSearch
|
||||||
entityTypes={SEARCH_ENTITY_TYPES}
|
entityTypes={EMBEDDED_LIST_SEARCH_ENTITY_TYPES}
|
||||||
query={query || ''}
|
query={query || ''}
|
||||||
page={page}
|
page={page}
|
||||||
unionType={unionType}
|
unionType={unionType}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { getContextPath } from '@app/entityV2/shared/containers/profile/header/getContextPath';
|
||||||
import VersioningBadge from '@app/entityV2/shared/versioning/VersioningBadge';
|
import VersioningBadge from '@app/entityV2/shared/versioning/VersioningBadge';
|
||||||
import { Divider } from 'antd';
|
import { Divider } from 'antd';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
@ -7,7 +8,6 @@ import {
|
|||||||
DataPlatform,
|
DataPlatform,
|
||||||
DisplayProperties,
|
DisplayProperties,
|
||||||
Domain,
|
Domain,
|
||||||
Entity,
|
|
||||||
EntityType,
|
EntityType,
|
||||||
Post,
|
Post,
|
||||||
} from '../../../../../../types.generated';
|
} from '../../../../../../types.generated';
|
||||||
@ -150,14 +150,7 @@ export const DefaultEntityHeader = ({
|
|||||||
const displayedEntityType = getDisplayedEntityType(entityData, entityRegistry, entityType);
|
const displayedEntityType = getDisplayedEntityType(entityData, entityRegistry, entityType);
|
||||||
const { platform, platforms } = getEntityPlatforms(entityType, entityData);
|
const { platform, platforms } = getEntityPlatforms(entityType, entityData);
|
||||||
|
|
||||||
const containerPath =
|
const contextPath = getContextPath(entityData);
|
||||||
entityData?.parentContainers?.containers ||
|
|
||||||
entityData?.parentDomains?.domains ||
|
|
||||||
entityData?.parentNodes?.nodes ||
|
|
||||||
[];
|
|
||||||
const parentPath: Entity[] = entityData?.parent ? [entityData.parent as Entity] : [];
|
|
||||||
const parentEntities = containerPath.length ? containerPath : parentPath;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Row>
|
<Row>
|
||||||
@ -236,7 +229,7 @@ export const DefaultEntityHeader = ({
|
|||||||
type={displayedEntityType}
|
type={displayedEntityType}
|
||||||
entityType={entityType}
|
entityType={entityType}
|
||||||
browsePaths={entityData?.browsePathV2}
|
browsePaths={entityData?.browsePathV2}
|
||||||
parentEntities={parentEntities}
|
parentEntities={contextPath}
|
||||||
contentRef={contentRef}
|
contentRef={contentRef}
|
||||||
isContentTruncated={isContentTruncated}
|
isContentTruncated={isContentTruncated}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -0,0 +1,102 @@
|
|||||||
|
import { GenericEntityProperties } from '@app/entity/shared/types';
|
||||||
|
import { dataPlatform } from '@src/Mocks';
|
||||||
|
import { EntityType } from '@types';
|
||||||
|
import { getContextPath } from './getContextPath';
|
||||||
|
|
||||||
|
const PARENT_CONTAINERS: GenericEntityProperties['parentContainers'] = {
|
||||||
|
containers: [
|
||||||
|
{
|
||||||
|
urn: 'urn:li:container:1',
|
||||||
|
type: EntityType.Container,
|
||||||
|
platform: dataPlatform,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
urn: 'urn:li:container:2',
|
||||||
|
type: EntityType.Container,
|
||||||
|
platform: dataPlatform,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
count: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
const PARENT_DOMAINS: GenericEntityProperties['parentDomains'] = {
|
||||||
|
domains: [
|
||||||
|
{ urn: 'urn:li:domain:1', type: EntityType.Domain },
|
||||||
|
{ urn: 'urn:li:domain:2', type: EntityType.Domain },
|
||||||
|
],
|
||||||
|
count: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
const PARENT_NODES: GenericEntityProperties['parentNodes'] = {
|
||||||
|
nodes: [
|
||||||
|
{ urn: 'urn:li:glossaryNode:1', type: EntityType.GlossaryNode },
|
||||||
|
{
|
||||||
|
urn: 'urn:li:glossaryNode:2',
|
||||||
|
type: EntityType.GlossaryNode,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
count: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
const PARENT: GenericEntityProperties = {
|
||||||
|
urn: 'urn:li:dataset:(urn:li:dataPlatform:snowflake,name,PROD)',
|
||||||
|
type: EntityType.Dataset,
|
||||||
|
platform: dataPlatform,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('getContextPath', () => {
|
||||||
|
it('returns empty array by default', () => {
|
||||||
|
const entityData = {};
|
||||||
|
|
||||||
|
const contextPath = getContextPath(entityData);
|
||||||
|
expect(contextPath).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns correct context path for entity with parent containers', () => {
|
||||||
|
const entityData = {
|
||||||
|
parentContainers: PARENT_CONTAINERS,
|
||||||
|
parentDomains: PARENT_DOMAINS,
|
||||||
|
parentNodes: PARENT_NODES,
|
||||||
|
parent: PARENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
const contextPath = getContextPath(entityData);
|
||||||
|
expect(contextPath).toEqual(PARENT_CONTAINERS.containers);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns correct context path for entity with parent domains', () => {
|
||||||
|
const entityData = {
|
||||||
|
parentContainers: null,
|
||||||
|
parentDomains: PARENT_DOMAINS,
|
||||||
|
parentNodes: PARENT_NODES,
|
||||||
|
parent: PARENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
const contextPath = getContextPath(entityData);
|
||||||
|
expect(contextPath).toEqual(PARENT_DOMAINS.domains);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns correct context path for entity with parent nodes', () => {
|
||||||
|
const entityData = {
|
||||||
|
parentContainers: null,
|
||||||
|
parentDomains: null,
|
||||||
|
parentNodes: PARENT_NODES,
|
||||||
|
parent: PARENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
const contextPath = getContextPath(entityData);
|
||||||
|
expect(contextPath).toEqual(PARENT_NODES.nodes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns correct context path for entity with parent', () => {
|
||||||
|
const entityData = {
|
||||||
|
parentContainers: null,
|
||||||
|
parentDomains: null,
|
||||||
|
parentNodes: null,
|
||||||
|
parent: PARENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
const contextPath = getContextPath(entityData);
|
||||||
|
expect(contextPath).toEqual([PARENT]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
import { GenericEntityProperties } from '@app/entity/shared/types';
|
||||||
|
import { Entity } from '@types';
|
||||||
|
|
||||||
|
type GetContextPathInput = Pick<
|
||||||
|
GenericEntityProperties,
|
||||||
|
'parent' | 'parentContainers' | 'parentDomains' | 'parentNodes'
|
||||||
|
>;
|
||||||
|
|
||||||
|
export function getContextPath(entityData: GetContextPathInput | null): Entity[] {
|
||||||
|
const containerPath =
|
||||||
|
entityData?.parentContainers?.containers ||
|
||||||
|
entityData?.parentDomains?.domains ||
|
||||||
|
entityData?.parentNodes?.nodes ||
|
||||||
|
[];
|
||||||
|
const parentPath: Entity[] = entityData?.parent ? [entityData.parent as Entity] : [];
|
||||||
|
return containerPath.length ? containerPath : parentPath;
|
||||||
|
}
|
||||||
@ -5,7 +5,6 @@ import {
|
|||||||
toRelativeTimeString,
|
toRelativeTimeString,
|
||||||
} from '@app/shared/time/timeUtils';
|
} from '@app/shared/time/timeUtils';
|
||||||
import { Pill, Popover } from '@components';
|
import { Pill, Popover } from '@components';
|
||||||
import { Maybe } from 'graphql/jsutils/Maybe';
|
|
||||||
import { capitalize } from 'lodash';
|
import { capitalize } from 'lodash';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
@ -43,9 +42,9 @@ const popoverStyles = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
startTime: Maybe<number>;
|
startTime?: number;
|
||||||
duration: Maybe<number>;
|
duration?: number;
|
||||||
status: Maybe<string>;
|
status?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DataProcessInstanceRightColumn({ startTime, duration, status }: Props) {
|
export default function DataProcessInstanceRightColumn({ startTime, duration, status }: Props) {
|
||||||
|
|||||||
@ -200,9 +200,11 @@ interface Props {
|
|||||||
paths?: EntityPath[];
|
paths?: EntityPath[];
|
||||||
health?: Health[];
|
health?: Health[];
|
||||||
parentDataset?: Dataset;
|
parentDataset?: Dataset;
|
||||||
startTime?: number | null;
|
dataProcessInstanceProps?: {
|
||||||
duration?: number | null;
|
startTime?: number;
|
||||||
status?: string | null;
|
duration?: number;
|
||||||
|
status?: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DefaultPreviewCard({
|
export default function DefaultPreviewCard({
|
||||||
@ -246,9 +248,7 @@ export default function DefaultPreviewCard({
|
|||||||
paths,
|
paths,
|
||||||
health,
|
health,
|
||||||
parentDataset,
|
parentDataset,
|
||||||
startTime,
|
dataProcessInstanceProps,
|
||||||
duration,
|
|
||||||
status,
|
|
||||||
}: Props) {
|
}: Props) {
|
||||||
// sometimes these lists will be rendered inside an entity container (for example, in the case of impact analysis)
|
// sometimes these lists will be rendered inside an entity container (for example, in the case of impact analysis)
|
||||||
// in those cases, we may want to enrich the preview w/ context about the container entity
|
// in those cases, we may want to enrich the preview w/ context about the container entity
|
||||||
@ -277,7 +277,11 @@ export default function DefaultPreviewCard({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const shouldShowRightColumn =
|
const shouldShowRightColumn =
|
||||||
(topUsers && topUsers.length > 0) || (owners && owners.length > 0) || startTime || duration || status;
|
(topUsers && topUsers.length > 0) ||
|
||||||
|
(owners && owners.length > 0) ||
|
||||||
|
dataProcessInstanceProps?.startTime ||
|
||||||
|
dataProcessInstanceProps?.duration ||
|
||||||
|
dataProcessInstanceProps?.status;
|
||||||
const uniqueOwners = getUniqueOwners(owners);
|
const uniqueOwners = getUniqueOwners(owners);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -387,7 +391,7 @@ export default function DefaultPreviewCard({
|
|||||||
</LeftColumn>
|
</LeftColumn>
|
||||||
{shouldShowRightColumn && (
|
{shouldShowRightColumn && (
|
||||||
<RightColumn key="right-column">
|
<RightColumn key="right-column">
|
||||||
<DataProcessInstanceRightColumn startTime={startTime} duration={duration} status={status} />
|
<DataProcessInstanceRightColumn {...dataProcessInstanceProps} />
|
||||||
{topUsers && topUsers?.length > 0 && (
|
{topUsers && topUsers?.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<UserListContainer>
|
<UserListContainer>
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { EntityType } from '@types';
|
||||||
|
|
||||||
export const FILTER_URL_PREFIX = 'filter_';
|
export const FILTER_URL_PREFIX = 'filter_';
|
||||||
export const SEARCH_FOR_ENTITY_PREFIX = 'SEARCH__';
|
export const SEARCH_FOR_ENTITY_PREFIX = 'SEARCH__';
|
||||||
export const EXACT_SEARCH_PREFIX = 'EXACT__';
|
export const EXACT_SEARCH_PREFIX = 'EXACT__';
|
||||||
@ -140,3 +142,28 @@ export const FilterModes = {
|
|||||||
export type FilterMode = (typeof FilterModes)[keyof typeof FilterModes];
|
export type FilterMode = (typeof FilterModes)[keyof typeof FilterModes];
|
||||||
|
|
||||||
export const MAX_COUNT_VAL = 10000;
|
export const MAX_COUNT_VAL = 10000;
|
||||||
|
|
||||||
|
export const EMBEDDED_LIST_SEARCH_ENTITY_TYPES = [
|
||||||
|
EntityType.Dataset,
|
||||||
|
EntityType.Dashboard,
|
||||||
|
EntityType.Chart,
|
||||||
|
EntityType.Mlmodel,
|
||||||
|
EntityType.MlmodelGroup,
|
||||||
|
EntityType.MlfeatureTable,
|
||||||
|
EntityType.Mlfeature,
|
||||||
|
EntityType.MlprimaryKey,
|
||||||
|
EntityType.DataFlow,
|
||||||
|
EntityType.DataJob,
|
||||||
|
EntityType.GlossaryTerm,
|
||||||
|
EntityType.GlossaryNode,
|
||||||
|
EntityType.Tag,
|
||||||
|
EntityType.Role,
|
||||||
|
EntityType.CorpUser,
|
||||||
|
EntityType.CorpGroup,
|
||||||
|
EntityType.Container,
|
||||||
|
EntityType.Domain,
|
||||||
|
EntityType.DataProduct,
|
||||||
|
EntityType.Notebook,
|
||||||
|
EntityType.BusinessAttribute,
|
||||||
|
EntityType.DataProcessInstance,
|
||||||
|
];
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user