From 324b4f5fed3835937b3f3ecf96ffba7659d3ac43 Mon Sep 17 00:00:00 2001 From: apTalya <99441958+apTalya@users.noreply.github.com> Date: Wed, 18 May 2022 17:23:08 -0700 Subject: [PATCH] feat: add task version info (#485) made the entities component become more generic that can support Workflow, Task, and others added the inputs and outputs to task details page added the versions table in task details page added the version details page added task details link in node side panel Signed-off-by: eugenejahn Co-authored-by: Eugene Jahn --- packages/zapp/console/src/common/constants.ts | 2 +- .../components/Entities/EntityDescription.tsx | 96 ++++++++++++--- .../Entities/EntityDetailsHeader.tsx | 9 +- .../components/Entities/EntityExecutions.tsx | 6 +- .../Entities/EntityExecutionsBarChart.tsx | 5 +- .../components/Entities/EntitySchedules.tsx | 17 ++- .../components/Entities/EntityVersions.tsx | 36 +++--- .../console/src/components/Entities/Row.tsx | 32 +++++ .../VersionDetails/EntityVersionDetails.tsx | 70 +++++++++++ .../EntityVersionDetailsContainer.tsx | 115 ++++++++++++++++++ .../Entities/VersionDetails/EnvVarsTable.tsx | 71 +++++++++++ .../VersionDetails/VersionDetailsLink.tsx | 25 ++++ .../Entities/VersionDetails/constants.ts | 29 +++++ .../src/components/Entities/constants.ts | 19 ++- .../src/components/Entities/generators.ts | 62 +++++++++- .../src/components/Entities/strings.ts | 27 +++- .../Entities/test/EntityDetails.test.tsx | 72 +++++++++++ .../test/EntityVersionDetails.test.tsx | 69 +++++++++++ .../test/TaskVersionDetailsLink.test.tsx | 43 +++++++ .../NodeExecutionDetailsPanelContent.tsx | 4 +- .../NodeExecutionTabs/index.tsx | 3 + ...sionsTable.tsx => EntityVersionsTable.tsx} | 42 ++++--- .../console/src/components/Theme/muiTheme.ts | 9 +- .../src/components/common/BarChart.tsx | 2 +- .../src/components/common/DumpJSON.tsx | 4 +- .../src/components/common/NewTargetLink.tsx | 29 +++-- .../src/components/common/ReactJsonView.tsx | 3 + .../common/test/NewTargetLink.spec.tsx | 5 +- .../src/components/hooks/Entity/constants.ts | 41 +++++++ .../hooks/Entity/useEntityVersions.ts | 20 +++ .../console/src/components/hooks/useTask.ts | 2 +- .../components/hooks/useWorkflowVersions.ts | 17 --- .../zapp/console/src/models/Task/types.ts | 1 + .../console/src/models/__mocks__/taskData.ts | 8 +- .../src/models/__mocks__/workflowData.ts | 8 +- .../console/src/routes/ApplicationRouter.tsx | 4 +- .../zapp/console/src/routes/components.ts | 4 +- packages/zapp/console/src/routes/routes.ts | 20 ++- 38 files changed, 900 insertions(+), 131 deletions(-) create mode 100644 packages/zapp/console/src/components/Entities/Row.tsx create mode 100644 packages/zapp/console/src/components/Entities/VersionDetails/EntityVersionDetails.tsx create mode 100644 packages/zapp/console/src/components/Entities/VersionDetails/EntityVersionDetailsContainer.tsx create mode 100644 packages/zapp/console/src/components/Entities/VersionDetails/EnvVarsTable.tsx create mode 100644 packages/zapp/console/src/components/Entities/VersionDetails/VersionDetailsLink.tsx create mode 100644 packages/zapp/console/src/components/Entities/VersionDetails/constants.ts create mode 100644 packages/zapp/console/src/components/Entities/test/EntityDetails.test.tsx create mode 100644 packages/zapp/console/src/components/Entities/test/EntityVersionDetails.test.tsx create mode 100644 packages/zapp/console/src/components/Entities/test/TaskVersionDetailsLink.test.tsx rename packages/zapp/console/src/components/Executions/Tables/{WorkflowVersionsTable.tsx => EntityVersionsTable.tsx} (59%) create mode 100644 packages/zapp/console/src/components/hooks/Entity/constants.ts create mode 100644 packages/zapp/console/src/components/hooks/Entity/useEntityVersions.ts delete mode 100644 packages/zapp/console/src/components/hooks/useWorkflowVersions.ts diff --git a/packages/zapp/console/src/common/constants.ts b/packages/zapp/console/src/common/constants.ts index 634a751fc..46870669e 100644 --- a/packages/zapp/console/src/common/constants.ts +++ b/packages/zapp/console/src/common/constants.ts @@ -6,7 +6,7 @@ export const unknownValueString = '(unknown)'; export const dashedValueString = '----'; export const noneString = '(none)'; export const noExecutionsFoundString = 'No executions found.'; -export const noWorkflowVersionsFoundString = 'No workflow versions found.'; +export const noVersionsFoundString = 'No versions found.'; export const zeroSecondsString = '0s'; export enum KeyCodes { diff --git a/packages/zapp/console/src/components/Entities/EntityDescription.tsx b/packages/zapp/console/src/components/Entities/EntityDescription.tsx index 646af3639..ecbec2db0 100644 --- a/packages/zapp/console/src/components/Entities/EntityDescription.tsx +++ b/packages/zapp/console/src/components/Entities/EntityDescription.tsx @@ -4,20 +4,80 @@ import classnames from 'classnames'; import { useCommonStyles } from 'components/common/styles'; import { WaitForData } from 'components/common/WaitForData'; import { useNamedEntity } from 'components/hooks/useNamedEntity'; -import { NamedEntityMetadata, ResourceIdentifier } from 'models/Common/types'; +import { NamedEntityMetadata, ResourceIdentifier, Variable } from 'models/Common/types'; import * as React from 'react'; import reactLoadingSkeleton from 'react-loading-skeleton'; -import { entityStrings } from './constants'; -import t from './strings'; +import { ReactJsonViewWrapper } from 'components/common/ReactJsonView'; +import { useEntityVersions } from 'components/hooks/Entity/useEntityVersions'; +import { executionSortFields } from 'models/Execution/constants'; +import { SortDirection } from 'models/AdminEntity/types'; +import { TaskClosure } from 'models/Task/types'; +import { executionFilterGenerator } from './generators'; +import { Row } from './Row'; +import t, { patternKey } from './strings'; +import { entityStrings, entitySections } from './constants'; const Skeleton = reactLoadingSkeleton; const useStyles = makeStyles((theme: Theme) => ({ + header: { + marginBottom: theme.spacing(1), + }, description: { marginTop: theme.spacing(1), }, + divider: { + borderBottom: `1px solid ${theme.palette.divider}`, + marginBottom: theme.spacing(1), + }, })); +const InputsAndOuputs: React.FC<{ + id: ResourceIdentifier; +}> = ({ id }) => { + const sort = { + key: executionSortFields.createdAt, + direction: SortDirection.DESCENDING, + }; + + const baseFilters = executionFilterGenerator[id.resourceType](id); + + // to render the input and output, + // need to fetch the latest version and get the input and ouptut data + const versions = useEntityVersions( + { ...id, version: '' }, + { + sort, + filter: baseFilters, + limit: 1, + }, + ); + + let inputs: Record | undefined; + let outputs: Record | undefined; + + if ((versions?.value?.[0]?.closure as TaskClosure)?.compiledTask?.template) { + const template = (versions?.value?.[0]?.closure as TaskClosure)?.compiledTask?.template; + inputs = template?.interface?.inputs?.variables; + outputs = template?.interface?.outputs?.variables; + } + + return ( + + {inputs && ( + + !field?.name} /> + + )} + {outputs && ( + + !field?.name} /> + + )} + + ); +}; + /** Fetches and renders the description for a given Entity (LaunchPlan,Workflow,Task) ID */ export const EntityDescription: React.FC<{ id: ResourceIdentifier; @@ -27,21 +87,29 @@ export const EntityDescription: React.FC<{ const namedEntity = useNamedEntity(id); const { metadata = {} as NamedEntityMetadata } = namedEntity.value; const hasDescription = !!metadata.description; + const sections = entitySections[id.resourceType]; + return ( <> - Description - + + {t('basicInformation')} + +
+ - - {hasDescription - ? metadata.description - : t('noDescription', entityStrings[id.resourceType])} - + + + {hasDescription + ? metadata.description + : t(patternKey('noDescription', entityStrings[id.resourceType]))} + + + {sections?.descriptionInputsAndOutputs && } ); diff --git a/packages/zapp/console/src/components/Entities/EntityDetailsHeader.tsx b/packages/zapp/console/src/components/Entities/EntityDetailsHeader.tsx index ce41b9ce6..c22b16291 100644 --- a/packages/zapp/console/src/components/Entities/EntityDetailsHeader.tsx +++ b/packages/zapp/console/src/components/Entities/EntityDetailsHeader.tsx @@ -8,11 +8,10 @@ import { Project } from 'models/Project/types'; import { getProjectDomain } from 'models/Project/utils'; import * as React from 'react'; import { Link } from 'react-router-dom'; -import { Routes } from 'routes/routes'; import { LaunchForm } from 'components/Launch/LaunchForm/LaunchForm'; -import { backUrlGenerator } from './generators'; +import { backUrlGenerator, backToDetailUrlGenerator } from './generators'; import { entityStrings } from './constants'; -import t from './strings'; +import t, { patternKey } from './strings'; const useStyles = makeStyles((theme: Theme) => ({ headerContainer: { @@ -77,7 +76,7 @@ export const EntityDetailsHeader: React.FC = ({ className={commonStyles.linkUnstyled} to={ backToWorkflow - ? Routes.WorkflowDetails.makeUrl(id.project, id.domain, id.name) + ? backToDetailUrlGenerator[id.resourceType](id) : backUrlGenerator[id.resourceType](id) } > @@ -93,7 +92,7 @@ export const EntityDetailsHeader: React.FC = ({ onClick={() => setShowLaunchForm(true)} variant="contained" > - {t('launchStrings', entityStrings[id.resourceType])} + {t(patternKey('launchStrings', entityStrings[id.resourceType]))} ) : null}
diff --git a/packages/zapp/console/src/components/Entities/EntityExecutions.tsx b/packages/zapp/console/src/components/Entities/EntityExecutions.tsx index b2b3780e8..da2e701b8 100644 --- a/packages/zapp/console/src/components/Entities/EntityExecutions.tsx +++ b/packages/zapp/console/src/components/Entities/EntityExecutions.tsx @@ -15,6 +15,8 @@ import { executionSortFields } from 'models/Execution/constants'; import { compact } from 'lodash'; import { useOnlyMyExecutionsFilterState } from 'components/Executions/filters/useOnlyMyExecutionsFilterState'; import { executionFilterGenerator } from './generators'; +import { entityStrings } from './constants'; +import t, { patternKey } from './strings'; const useStyles = makeStyles((theme: Theme) => ({ filtersContainer: { @@ -76,8 +78,8 @@ export const EntityExecutions: React.FC = ({ return ( <> - - All Executions in the Workflow + + {t(patternKey('allExecutionsChartTitle', entityStrings[id.resourceType]))}
= return ( ({ + header: { + marginBottom: theme.spacing(1), + }, schedulesContainer: { marginTop: theme.spacing(1), }, + divider: { + borderBottom: `1px solid ${theme.palette.divider}`, + marginBottom: theme.spacing(1), + }, })); const RenderSchedules: React.FC<{ @@ -45,13 +52,17 @@ export const EntitySchedules: React.FC<{ return ( <> - {t('schedulesHeader')} + + {t('schedulesHeader')} + +
+
{scheduledLaunchPlans.value.length > 0 ? ( ) : ( - {t('noSchedules', entityStrings[id.resourceType])} + {t(patternKey('noSchedules', entityStrings[id.resourceType]))} )}
diff --git a/packages/zapp/console/src/components/Entities/EntityVersions.tsx b/packages/zapp/console/src/components/Entities/EntityVersions.tsx index 64990f86a..4509f3090 100644 --- a/packages/zapp/console/src/components/Entities/EntityVersions.tsx +++ b/packages/zapp/console/src/components/Entities/EntityVersions.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import { Routes } from 'routes/routes'; import { history } from 'routes/history'; import Typography from '@material-ui/core/Typography'; import { IconButton, makeStyles, Theme } from '@material-ui/core'; @@ -7,16 +6,16 @@ import ExpandLess from '@material-ui/icons/ExpandLess'; import ExpandMore from '@material-ui/icons/ExpandMore'; import { LocalCacheItem, useLocalCache } from 'basics/LocalCache'; import { WaitForData } from 'components/common/WaitForData'; -import { WorkflowVersionsTable } from 'components/Executions/Tables/WorkflowVersionsTable'; +import { EntityVersionsTable } from 'components/Executions/Tables/EntityVersionsTable'; import { isLoadingState } from 'components/hooks/fetchMachine'; -import { useWorkflowVersions } from 'components/hooks/useWorkflowVersions'; +import { useEntityVersions } from 'components/hooks/Entity/useEntityVersions'; import { interactiveTextColor } from 'components/Theme/constants'; import { SortDirection } from 'models/AdminEntity/types'; -import { ResourceIdentifier } from 'models/Common/types'; +import { Identifier, ResourceIdentifier } from 'models/Common/types'; import { executionSortFields } from 'models/Execution/constants'; -import { executionFilterGenerator } from './generators'; -import { WorkflowVersionsTablePageSize } from './constants'; -import t from './strings'; +import { executionFilterGenerator, versionDetailsUrlGenerator } from './generators'; +import { WorkflowVersionsTablePageSize, entityStrings } from './constants'; +import t, { patternKey } from './strings'; const useStyles = makeStyles((theme: Theme) => ({ headerContainer: { @@ -64,8 +63,10 @@ export const EntityVersions: React.FC = ({ id, showAll = fa [id, resourceType], ); - const versions = useWorkflowVersions( - { domain, project }, + // we are getting all the versions for this id + // so we don't want to specify which version + const versions = useEntityVersions( + { ...id, version: '' }, { sort, filter: baseFilters, @@ -76,12 +77,10 @@ export const EntityVersions: React.FC = ({ id, showAll = fa const preventDefault = (e) => e.preventDefault(); const handleViewAll = React.useCallback(() => { history.push( - Routes.WorkflowVersionDetails.makeUrl( - project, - domain, - name, - versions.value[0].id.version ?? '', - ), + versionDetailsUrlGenerator({ + ...id, + version: versions.value[0].id.version ?? '', + } as Identifier), ); }, [project, domain, name, versions]); @@ -102,8 +101,8 @@ export const EntityVersions: React.FC = ({ id, showAll = fa > {showTable ? : } - - {t('workflowVersionsTitle')} + + {t(patternKey('versionsTitle', entityStrings[id.resourceType]))} {t('viewAll')} @@ -112,10 +111,11 @@ export const EntityVersions: React.FC = ({ id, showAll = fa )} {showTable || showAll ? ( - ) : (
diff --git a/packages/zapp/console/src/components/Entities/Row.tsx b/packages/zapp/console/src/components/Entities/Row.tsx new file mode 100644 index 000000000..b6fd70c35 --- /dev/null +++ b/packages/zapp/console/src/components/Entities/Row.tsx @@ -0,0 +1,32 @@ +import * as React from 'react'; +import { Typography } from '@material-ui/core'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import { COLOR_SPECTRUM } from 'components/Theme/colorSpectrum'; + +const useStyles = makeStyles((theme: Theme) => ({ + row: { + display: 'flex', + marginBottom: theme.spacing(1), + }, + title: { + width: 100, + color: COLOR_SPECTRUM.gray25.color, + }, +})); + +interface MyProps { + children?: React.ReactNode; + title: String; +} +export const Row: React.FC = (props) => { + const styles = useStyles(); + + return ( +
+
+ {props.title} +
+
{props.children}
+
+ ); +}; diff --git a/packages/zapp/console/src/components/Entities/VersionDetails/EntityVersionDetails.tsx b/packages/zapp/console/src/components/Entities/VersionDetails/EntityVersionDetails.tsx new file mode 100644 index 000000000..519b4e13b --- /dev/null +++ b/packages/zapp/console/src/components/Entities/VersionDetails/EntityVersionDetails.tsx @@ -0,0 +1,70 @@ +import * as React from 'react'; +import { Typography } from '@material-ui/core'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import { contentMarginGridUnits } from 'common/layout'; +import { WaitForData } from 'components/common/WaitForData'; +import { useTaskTemplate } from 'components/hooks/useTask'; +import { ResourceIdentifier, Identifier } from 'models/Common/types'; +import { DumpJSON } from 'components/common/DumpJSON'; +import { Row } from '../Row'; +import EnvVarsTable from './EnvVarsTable'; +import t, { patternKey } from '../strings'; +import { entityStrings } from '../constants'; + +const useStyles = makeStyles((theme: Theme) => ({ + header: { + marginBottom: theme.spacing(1), + marginLeft: theme.spacing(contentMarginGridUnits), + }, + table: { + marginLeft: theme.spacing(contentMarginGridUnits), + }, + divider: { + borderBottom: `1px solid ${theme.palette.divider}`, + marginBottom: theme.spacing(1), + }, +})); + +export interface EntityExecutionsProps { + id: ResourceIdentifier; +} + +/** The tab/page content for viewing a workflow's executions */ +export const EntityVersionDetails: React.FC = ({ id }) => { + const styles = useStyles(); + + // NOTE: need to be generic for supporting other type like workflow, etc. + const templateState = useTaskTemplate(id as Identifier); + + const template = templateState?.value?.closure?.compiledTask?.template; + const envVars = template?.container?.env; + const image = template?.container?.image; + + return ( + <> + + {t(patternKey('details', entityStrings[id.resourceType]))} + +
+ +
+ {image && ( + + {image} + + )} + {envVars && ( + + + + )} + {template && ( + + + + )} +
+
+ + ); +}; diff --git a/packages/zapp/console/src/components/Entities/VersionDetails/EntityVersionDetailsContainer.tsx b/packages/zapp/console/src/components/Entities/VersionDetails/EntityVersionDetailsContainer.tsx new file mode 100644 index 000000000..931edeb09 --- /dev/null +++ b/packages/zapp/console/src/components/Entities/VersionDetails/EntityVersionDetailsContainer.tsx @@ -0,0 +1,115 @@ +import * as React from 'react'; +import { withRouteParams } from 'components/common/withRouteParams'; +import { ResourceIdentifier } from 'models/Common/types'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import { WaitForData } from 'components/common/WaitForData'; +import { useProject } from 'components/hooks/useProjects'; +import { StaticGraphContainer } from 'components/Workflow/StaticGraphContainer'; +import { WorkflowId } from 'models/Workflow/types'; +import { entitySections } from 'components/Entities/constants'; +import { EntityDetailsHeader } from 'components/Entities/EntityDetailsHeader'; +import { EntityVersions } from 'components/Entities/EntityVersions'; +import { typeNameToEntityResource } from '../constants'; +import { versionsDetailsSections } from './constants'; +import { EntityVersionDetails } from './EntityVersionDetails'; + +const useStyles = makeStyles((theme: Theme) => ({ + verionDetailsContainer: { + marginTop: theme.spacing(2), + display: 'flex', + flexDirection: 'column', + flexWrap: 'nowrap', + overflow: 'hidden', + height: `calc(100vh - ${theme.spacing(17)}px)`, + }, + staticGraphContainer: { + display: 'flex', + height: '60%', + width: '100%', + flex: '1', + }, + versionDetailsContainer: { + display: 'flex', + flexDirection: 'column', + height: '55%', + width: '100%', + flex: '1', + overflowY: 'scroll', + }, + versionsContainer: { + display: 'flex', + flex: '0 1 auto', + height: '40%', + flexDirection: 'column', + overflowY: 'scroll', + }, +})); + +interface WorkflowVersionDetailsRouteParams { + projectId: string; + domainId: string; + entityType: string; + entityName: string; + entityVersion: string; +} + +/** + * The view component for the Workflow Versions page + * @param projectId + * @param domainId + * @param workflowName + */ +const EntityVersionsDetailsContainerImpl: React.FC = ({ + projectId, + domainId, + entityType, + entityName, + entityVersion, +}) => { + const workflowId = React.useMemo( + () => ({ + resourceType: typeNameToEntityResource[entityType], + project: projectId, + domain: domainId, + name: entityName, + version: entityVersion, + }), + [entityType, projectId, domainId, entityName, entityVersion], + ); + + const id = workflowId as ResourceIdentifier; + const sections = entitySections[id.resourceType]; + const versionsSections = versionsDetailsSections[id.resourceType]; + const project = useProject(workflowId.project); + const styles = useStyles(); + + return ( + + +
+ {versionsSections.details && ( +
+ +
+ )} + {versionsSections.graph && ( +
+ +
+ )} +
+ +
+
+
+ ); +}; + +export const EntityVersionsDetailsContainer = withRouteParams( + EntityVersionsDetailsContainerImpl, +); diff --git a/packages/zapp/console/src/components/Entities/VersionDetails/EnvVarsTable.tsx b/packages/zapp/console/src/components/Entities/VersionDetails/EnvVarsTable.tsx new file mode 100644 index 000000000..17efa7015 --- /dev/null +++ b/packages/zapp/console/src/components/Entities/VersionDetails/EnvVarsTable.tsx @@ -0,0 +1,71 @@ +import * as React from 'react'; +import { + Typography, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Paper, +} from '@material-ui/core'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import { Core } from 'flyteidl'; +import { COLOR_SPECTRUM } from 'components/Theme/colorSpectrum'; +import t from '../strings'; + +const useStyles = makeStyles((theme: Theme) => ({ + container: { + marginBottom: theme.spacing(1), + ['& .MuiTableCell-sizeSmall']: { + paddingLeft: 0, + }, + }, + headerText: { + color: COLOR_SPECTRUM.gray25.color, + }, +})); + +interface EnvVarsTableProps { + rows: Core.IKeyValuePair[]; +} + +export default function EnvVarsTable({ rows }: EnvVarsTableProps) { + const styles = useStyles(); + + if (!rows || rows.length == 0) { + return {t('empty')}; + } + return ( + + + + + + + {t('key')} + + + + + {t('value')} + + + + + + {rows.map((row) => ( + + + {row.key} + + + {row.value} + + + ))} + +
+
+ ); +} diff --git a/packages/zapp/console/src/components/Entities/VersionDetails/VersionDetailsLink.tsx b/packages/zapp/console/src/components/Entities/VersionDetails/VersionDetailsLink.tsx new file mode 100644 index 000000000..480324eba --- /dev/null +++ b/packages/zapp/console/src/components/Entities/VersionDetails/VersionDetailsLink.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import { Identifier } from 'models/Common/types'; +import { NewTargetLink } from 'components/common/NewTargetLink'; +import { versionDetailsUrlGenerator } from 'components/Entities/generators'; +import t from '../strings'; + +const useStyles = makeStyles((theme: Theme) => ({ + link: { + marginBottom: theme.spacing(2), + }, +})); + +interface TaskVersionDetailsLinkProps { + id: Identifier; +} + +export const TaskVersionDetailsLink: React.FC = ({ id }) => { + const styles = useStyles(); + return ( + + {t('details_task')} + + ); +}; diff --git a/packages/zapp/console/src/components/Entities/VersionDetails/constants.ts b/packages/zapp/console/src/components/Entities/VersionDetails/constants.ts new file mode 100644 index 000000000..ad09fb03a --- /dev/null +++ b/packages/zapp/console/src/components/Entities/VersionDetails/constants.ts @@ -0,0 +1,29 @@ +import { ResourceType } from 'models/Common/types'; + +interface VersionsDetailsSectionsFlags { + details: boolean; + graph: boolean; +} + +export const versionsDetailsSections: { [k in ResourceType]: VersionsDetailsSectionsFlags } = { + [ResourceType.DATASET]: { + details: false, + graph: false, + }, + [ResourceType.LAUNCH_PLAN]: { + details: false, + graph: false, + }, + [ResourceType.TASK]: { + details: true, + graph: false, + }, + [ResourceType.UNSPECIFIED]: { + details: false, + graph: false, + }, + [ResourceType.WORKFLOW]: { + details: false, + graph: true, + }, +}; diff --git a/packages/zapp/console/src/components/Entities/constants.ts b/packages/zapp/console/src/components/Entities/constants.ts index 06adada8f..efef36a63 100644 --- a/packages/zapp/console/src/components/Entities/constants.ts +++ b/packages/zapp/console/src/components/Entities/constants.ts @@ -10,12 +10,23 @@ export const entityStrings: EntityStringMap = { [ResourceType.WORKFLOW]: 'workflow', }; +type TypeNameToEntityResourceType = { [key: string]: ResourceType }; + +export const typeNameToEntityResource: TypeNameToEntityResourceType = { + ['dataset']: ResourceType.DATASET, + ['launch plan']: ResourceType.LAUNCH_PLAN, + ['task']: ResourceType.TASK, + ['item']: ResourceType.UNSPECIFIED, + ['workflow']: ResourceType.WORKFLOW, +}; + interface EntitySectionsFlags { description?: boolean; executions?: boolean; launch?: boolean; schedules?: boolean; versions?: boolean; + descriptionInputsAndOutputs?: boolean; } export const entitySections: { [k in ResourceType]: EntitySectionsFlags } = { @@ -26,7 +37,13 @@ export const entitySections: { [k in ResourceType]: EntitySectionsFlags } = { launch: true, schedules: true, }, - [ResourceType.TASK]: { description: true, executions: true, launch: true }, + [ResourceType.TASK]: { + description: true, + executions: true, + launch: true, + versions: true, + descriptionInputsAndOutputs: true, + }, [ResourceType.UNSPECIFIED]: { description: true }, [ResourceType.WORKFLOW]: { description: true, diff --git a/packages/zapp/console/src/components/Entities/generators.ts b/packages/zapp/console/src/components/Entities/generators.ts index 0cc7235e0..f10c41fee 100644 --- a/packages/zapp/console/src/components/Entities/generators.ts +++ b/packages/zapp/console/src/components/Entities/generators.ts @@ -1,6 +1,7 @@ import { FilterOperation, FilterOperationName } from 'models/AdminEntity/types'; -import { ResourceIdentifier, ResourceType } from 'models/Common/types'; +import { ResourceIdentifier, ResourceType, Identifier } from 'models/Common/types'; import { Routes } from 'routes/routes'; +import { entityStrings } from './constants'; const noFilters = () => []; @@ -30,13 +31,66 @@ const workflowListGenerator = ({ project, domain }: ResourceIdentifier) => Routes.ProjectDetails.sections.workflows.makeUrl(project, domain); const taskListGenerator = ({ project, domain }: ResourceIdentifier) => Routes.ProjectDetails.sections.tasks.makeUrl(project, domain); +const unspecifiedGenerator = ({ project, domain }: ResourceIdentifier | Identifier) => { + throw new Error('Unspecified Resourcetype.'); +}; +const unimplementedGenerator = ({ project, domain }: ResourceIdentifier | Identifier) => { + throw new Error('Method not implemented.'); +}; export const backUrlGenerator: { [k in ResourceType]: (id: ResourceIdentifier) => string; } = { - [ResourceType.DATASET]: workflowListGenerator, - [ResourceType.LAUNCH_PLAN]: workflowListGenerator, + [ResourceType.DATASET]: unimplementedGenerator, + [ResourceType.LAUNCH_PLAN]: unimplementedGenerator, [ResourceType.TASK]: taskListGenerator, - [ResourceType.UNSPECIFIED]: workflowListGenerator, + [ResourceType.UNSPECIFIED]: unspecifiedGenerator, [ResourceType.WORKFLOW]: workflowListGenerator, }; + +const workflowDetailGenerator = ({ project, domain, name }: ResourceIdentifier) => + Routes.WorkflowDetails.makeUrl(project, domain, name); +const taskDetailGenerator = ({ project, domain, name }: ResourceIdentifier) => + Routes.TaskDetails.makeUrl(project, domain, name); + +export const backToDetailUrlGenerator: { + [k in ResourceType]: (id: ResourceIdentifier) => string; +} = { + [ResourceType.DATASET]: unimplementedGenerator, + [ResourceType.LAUNCH_PLAN]: unimplementedGenerator, + [ResourceType.TASK]: taskDetailGenerator, + [ResourceType.UNSPECIFIED]: unspecifiedGenerator, + [ResourceType.WORKFLOW]: workflowDetailGenerator, +}; + +const workflowVersopmDetailsGenerator = ({ project, domain, name, version }: Identifier) => + Routes.EntityVersionDetails.makeUrl( + project, + domain, + name, + entityStrings[ResourceType.WORKFLOW], + version, + ); +const taskVersionDetailsGenerator = ({ project, domain, name, version }: Identifier) => + Routes.EntityVersionDetails.makeUrl( + project, + domain, + name, + entityStrings[ResourceType.TASK], + version, + ); + +const entityMapVersionDetailsUrl: { + [k in ResourceType]: (id: Identifier) => string; +} = { + [ResourceType.DATASET]: unimplementedGenerator, + [ResourceType.LAUNCH_PLAN]: unimplementedGenerator, + [ResourceType.TASK]: taskVersionDetailsGenerator, + [ResourceType.UNSPECIFIED]: unspecifiedGenerator, + [ResourceType.WORKFLOW]: workflowVersopmDetailsGenerator, +}; + +export const versionDetailsUrlGenerator = (id: Identifier): string => { + if (id?.resourceType) return entityMapVersionDetailsUrl[id?.resourceType](id); + return ''; +}; diff --git a/packages/zapp/console/src/components/Entities/strings.ts b/packages/zapp/console/src/components/Entities/strings.ts index 9c44f0f79..e12b28fd3 100644 --- a/packages/zapp/console/src/components/Entities/strings.ts +++ b/packages/zapp/console/src/components/Entities/strings.ts @@ -1,15 +1,30 @@ import { createLocalizedString } from '@flyteconsole/locale'; -import { startCase } from 'lodash'; const str = { - allExecutionsChartTitle: 'All Executions in the Workflow', - workflowVersionsTitle: 'Recent Workflow Versions', viewAll: 'View All', schedulesHeader: 'Schedules', collapseButton: (showAll: boolean) => (showAll ? 'Collapse' : 'Expand'), - launchStrings: (typeString: string) => `Launch ${startCase(typeString)}`, - noDescription: (typeString: string) => `This ${typeString} has no description.`, - noSchedules: (typeString: string) => `This ${typeString} has no schedules.`, + launchStrings_workflow: 'Launch Workflow', + launchStrings_task: 'Launch Task', + noDescription_workflow: 'This workflow has no description.', + noDescription_task: 'This task has no description.', + noSchedules_workflow: 'This workflow has no schedules.', + noSchedules_task: 'This task has no schedules.', + allExecutionsChartTitle_workflow: 'All Executions in the Workflow', + allExecutionsChartTitle_task: 'All Execuations in the Task', + versionsTitle_workflow: 'Recent Workflow Versions', + versionsTitle_task: 'Recent Task Versions', + details_task: 'Task Details', + inputsFieldName: 'Inputs', + outputsFieldName: 'Outputs', + imageFieldName: 'Image', + envVarsFieldName: 'Env Vars', + commandsFieldName: 'Commands', + empty: 'Empty', + key: 'Key', + value: 'Value', + basicInformation: 'Basic Information', + description: 'Description', }; export { patternKey } from '@flyteconsole/locale'; diff --git a/packages/zapp/console/src/components/Entities/test/EntityDetails.test.tsx b/packages/zapp/console/src/components/Entities/test/EntityDetails.test.tsx new file mode 100644 index 000000000..179c1d747 --- /dev/null +++ b/packages/zapp/console/src/components/Entities/test/EntityDetails.test.tsx @@ -0,0 +1,72 @@ +import { render, waitFor, screen } from '@testing-library/react'; +import { ResourceIdentifier } from 'models/Common/types'; +import * as React from 'react'; +import { createMockTask } from 'models/__mocks__/taskData'; +import { createMockWorkflow } from 'models/__mocks__/workflowData'; +import { Task } from 'models/Task/types'; +import { Workflow } from 'models/Workflow/types'; +import { projects } from 'mocks/data/projects'; +import * as projectApi from 'models/Project/api'; +import { MemoryRouter } from 'react-router'; +import { EntityDetails } from '../EntityDetails'; + +jest.mock('models/Project/api'); + +describe('EntityDetails', () => { + let mockWorkflow: Workflow; + let mockTask: Task; + + // mock api for listProjects + const mockListProjects = jest.spyOn(projectApi, 'listProjects'); + mockListProjects.mockResolvedValue([projects['flyteTest']]); + + const createMocks = () => { + mockWorkflow = createMockWorkflow('MyWorkflow'); + mockTask = createMockTask('MyTask'); + }; + + const renderDetails = (id: ResourceIdentifier) => { + return render( + + + , + ); + }; + + beforeEach(() => { + createMocks(); + }); + + const checkTextInDetailPage = async ( + id: ResourceIdentifier, + versionsString: string, + executionsString: string, + ) => { + // check text for header + await waitFor(() => { + expect(screen.getByText(`${id.domain} / ${id.name}`)).toBeInTheDocument(); + }); + + // check text for versions + await waitFor(() => { + expect(screen.getByText(versionsString)).toBeInTheDocument(); + }); + + // check text for executions + await waitFor(() => { + expect(screen.getByText(executionsString)).toBeInTheDocument(); + }); + }; + + it('renders Task Details Page', async () => { + const id: ResourceIdentifier = mockTask.id as ResourceIdentifier; + renderDetails(id); + checkTextInDetailPage(id, 'Recent Task Versions', 'All Executions in the Task'); + }); + + it('renders Workflow Details Page', async () => { + const id: ResourceIdentifier = mockWorkflow.id as ResourceIdentifier; + renderDetails(id); + checkTextInDetailPage(id, 'Recent Workflow Versions', 'All Executions in the Workflow'); + }); +}); diff --git a/packages/zapp/console/src/components/Entities/test/EntityVersionDetails.test.tsx b/packages/zapp/console/src/components/Entities/test/EntityVersionDetails.test.tsx new file mode 100644 index 000000000..147a002cf --- /dev/null +++ b/packages/zapp/console/src/components/Entities/test/EntityVersionDetails.test.tsx @@ -0,0 +1,69 @@ +import { render, waitFor, screen } from '@testing-library/react'; +import { ThemeProvider } from '@material-ui/styles'; +import { muiTheme } from 'components/Theme/muiTheme'; +import { ResourceIdentifier } from 'models/Common/types'; +import * as React from 'react'; +import { createMockTask } from 'models/__mocks__/taskData'; +import { Task } from 'models/Task/types'; +import { getTask } from 'models/Task/api'; +import { APIContext } from 'components/data/apiContext'; +import { mockAPIContextValue } from 'components/data/__mocks__/apiContext'; +import { EntityVersionDetails } from '../VersionDetails/EntityVersionDetails'; + +describe('EntityVersionDetails', () => { + let mockTask: Task; + let mockGetTask: jest.Mock>; + + const createMocks = () => { + mockTask = createMockTask('MyTask'); + mockGetTask = jest.fn().mockImplementation(() => Promise.resolve(mockTask)); + }; + + const renderDetails = (id: ResourceIdentifier) => { + return render( + + + + + , + ); + }; + + describe('Task Version Details', () => { + beforeEach(() => { + createMocks(); + }); + + it('renders and checks text', async () => { + const id: ResourceIdentifier = mockTask.id as ResourceIdentifier; + renderDetails(id); + + // check text for Task Details + await waitFor(() => { + expect(screen.getByText('Task Details')).toBeInTheDocument(); + }); + + // check text for image + await waitFor(() => { + expect( + screen.getByText(mockTask.closure.compiledTask.template?.container?.image || ''), + ).toBeInTheDocument(); + }); + + // check for env vars + if (mockTask.closure.compiledTask.template?.container?.env) { + const envVars = mockTask.closure.compiledTask.template?.container?.env; + for (let i = 0; i < envVars.length; i++) { + await waitFor(() => { + expect(screen.getByText(envVars[i].key || '')).toBeInTheDocument(); + expect(screen.getByText(envVars[i].value || '')).toBeInTheDocument(); + }); + } + } + }); + }); +}); diff --git a/packages/zapp/console/src/components/Entities/test/TaskVersionDetailsLink.test.tsx b/packages/zapp/console/src/components/Entities/test/TaskVersionDetailsLink.test.tsx new file mode 100644 index 000000000..19c89cc46 --- /dev/null +++ b/packages/zapp/console/src/components/Entities/test/TaskVersionDetailsLink.test.tsx @@ -0,0 +1,43 @@ +import { render, waitFor, screen } from '@testing-library/react'; +import * as React from 'react'; +import { createMockTask } from 'models/__mocks__/taskData'; +import { Task } from 'models/Task/types'; +import { Identifier } from 'models/Common/types'; +import { versionDetailsUrlGenerator } from 'components/Entities/generators'; +import { TaskVersionDetailsLink } from '../VersionDetails/VersionDetailsLink'; + +describe('TaskVersionDetailsLink', () => { + let mockTask: Task; + + const createMocks = () => { + mockTask = createMockTask('MyTask'); + }; + + const renderLink = (id: Identifier) => { + return render(); + }; + + beforeEach(() => { + createMocks(); + }); + + it('renders and checks text', async () => { + const id: Identifier = mockTask.id; + renderLink(id); + await waitFor(() => { + expect(screen.getByText('Task Details')).toBeInTheDocument(); + }); + }); + + it('renders and checks containing icon', () => { + const id: Identifier = mockTask.id; + const { container } = renderLink(id); + expect(container.querySelector('svg')).not.toBeNull(); + }); + + it('renders and checks url', () => { + const id: Identifier = mockTask.id; + const { container } = renderLink(id); + expect(container.firstElementChild).toHaveAttribute('href', versionDetailsUrlGenerator(id)); + }); +}); diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/NodeExecutionDetailsPanelContent.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/NodeExecutionDetailsPanelContent.tsx index 09ad0f06f..8a11e802c 100644 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/NodeExecutionDetailsPanelContent.tsx +++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/NodeExecutionDetailsPanelContent.tsx @@ -26,6 +26,8 @@ import { DumpJSON } from 'components/common/DumpJSON'; import { dNode } from 'models/Graph/types'; import { NodeExecutionPhase, TaskExecutionPhase } from 'models/Execution/enums'; import { transformWorkflowToKeyedDag, getNodeNameFromDag } from 'components/WorkflowGraph/utils'; +import { TaskVersionDetailsLink } from 'components/Entities/VersionDetails/VersionDetailsLink'; +import { Identifier } from 'models/Common/types'; import { NodeExecutionCacheStatus } from '../NodeExecutionCacheStatus'; import { makeListTaskExecutionsQuery, makeNodeExecutionQuery } from '../nodeExecutionQueries'; import { NodeExecutionDetails } from '../types'; @@ -186,7 +188,6 @@ const WorkflowTabs: React.FC<{ let tabContent: JSX.Element | null = null; const id = nodeId.slice(nodeId.lastIndexOf('-') + 1); const taskTemplate = dagData[id]?.value.template; - switch (tabState.value) { case tabIds.inputs: { tabContent = taskTemplate ? ( @@ -199,6 +200,7 @@ const WorkflowTabs: React.FC<{ case tabIds.task: { tabContent = taskTemplate ? ( + ) : null; diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/NodeExecutionTabs/index.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/NodeExecutionTabs/index.tsx index ec1761afc..0fceb1331 100644 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/NodeExecutionTabs/index.tsx +++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/NodeExecutionTabs/index.tsx @@ -8,6 +8,8 @@ import { PanelSection } from 'components/common/PanelSection'; import { DumpJSON } from 'components/common/DumpJSON'; import { isMapTaskType } from 'models/Task/utils'; import { TaskExecutionPhase } from 'models/Execution/enums'; +import { TaskVersionDetailsLink } from 'components/Entities/VersionDetails/VersionDetailsLink'; +import { Identifier } from 'models/Common/types'; import { TaskExecutionsList } from '../../TaskExecutionsList/TaskExecutionsList'; import { NodeExecutionInputs } from './NodeExecutionInputs'; import { NodeExecutionOutputs } from './NodeExecutionOutputs'; @@ -72,6 +74,7 @@ export const NodeExecutionTabs: React.FC<{ case tabIds.task: { tabContent = taskTemplate ? ( + ) : null; diff --git a/packages/zapp/console/src/components/Executions/Tables/WorkflowVersionsTable.tsx b/packages/zapp/console/src/components/Executions/Tables/EntityVersionsTable.tsx similarity index 59% rename from packages/zapp/console/src/components/Executions/Tables/WorkflowVersionsTable.tsx rename to packages/zapp/console/src/components/Executions/Tables/EntityVersionsTable.tsx index cfb1cc20d..64e96baff 100644 --- a/packages/zapp/console/src/components/Executions/Tables/WorkflowVersionsTable.tsx +++ b/packages/zapp/console/src/components/Executions/Tables/EntityVersionsTable.tsx @@ -1,25 +1,27 @@ import classnames from 'classnames'; -import { noWorkflowVersionsFoundString } from 'common/constants'; +import { noVersionsFoundString } from 'common/constants'; import { useCommonStyles } from 'components/common/styles'; import { ListProps } from 'components/common/types'; import PaginatedDataList from 'components/Tables/PaginatedDataList'; import { Workflow } from 'models/Workflow/types'; -import { Identifier } from 'models/Common/types'; +import { Identifier, ResourceType } from 'models/Common/types'; import * as React from 'react'; import { useParams } from 'react-router'; import { history } from 'routes/history'; import { Routes } from 'routes/routes'; +import { entityStrings } from 'components/Entities/constants'; import { useExecutionTableStyles } from './styles'; import { useWorkflowExecutionsTableState } from './useWorkflowExecutionTableState'; import { useWorkflowVersionsTableColumns } from './useWorkflowVersionsTableColumns'; import { WorkflowVersionRow } from './WorkflowVersionRow'; -interface WorkflowVersionsTableProps extends ListProps { +interface EntityVersionsTableProps extends ListProps { versionView?: boolean; + resourceType: ResourceType; } -interface WorkflowVersionRouteParams { - workflowVersion: string; +interface EntityVersionRouteParams { + entityVersion: string; } /** @@ -27,21 +29,27 @@ interface WorkflowVersionRouteParams { * @param props * @constructor */ -export const WorkflowVersionsTable: React.FC = (props) => { - const { value: workflows, versionView } = props; +export const EntityVersionsTable: React.FC = (props) => { + const { value: versions, versionView, resourceType } = props; const state = useWorkflowExecutionsTableState(); const commonStyles = useCommonStyles(); const tableStyles = useExecutionTableStyles(); - const { workflowVersion } = useParams(); + const { entityVersion } = useParams(); const columns = useWorkflowVersionsTableColumns(); - const retry = () => props.fetch(); - const handleClickRow = React.useCallback( - ({ project, name, domain, version }: Identifier) => + ({ project, name, domain, version, resourceType = ResourceType.UNSPECIFIED }: Identifier) => () => { - history.push(Routes.WorkflowVersionDetails.makeUrl(project, domain, name, version)); + history.push( + Routes.EntityVersionDetails.makeUrl( + project, + domain, + name, + entityStrings[resourceType], + version, + ), + ); }, [], ); @@ -52,8 +60,8 @@ export const WorkflowVersionsTable: React.FC = (prop workflow={row} state={state} versionView={versionView} - onClick={versionView ? handleClickRow(row.id) : undefined} - isChecked={workflowVersion === row.id.version} + onClick={handleClickRow({ ...row.id, resourceType })} + isChecked={entityVersion === row.id.version} key={`workflow-version-row-${row.id.version}`} /> ); @@ -62,11 +70,11 @@ export const WorkflowVersionsTable: React.FC = (prop
diff --git a/packages/zapp/console/src/components/Theme/muiTheme.ts b/packages/zapp/console/src/components/Theme/muiTheme.ts index f959163dd..44ab78f9b 100644 --- a/packages/zapp/console/src/components/Theme/muiTheme.ts +++ b/packages/zapp/console/src/components/Theme/muiTheme.ts @@ -71,9 +71,14 @@ const theme = createMuiTheme({ fontFamily: bodyFontFamily, }, h3: { - fontSize: '1.25rem', + fontSize: '16px', fontWeight: 'bold', - lineHeight: 1.35, + lineHeight: '22px', + }, + h4: { + fontSize: '14px', + fontWeight: 'bold', + lineHeight: '20px', }, h6: { fontSize: '.875rem', diff --git a/packages/zapp/console/src/components/common/BarChart.tsx b/packages/zapp/console/src/components/common/BarChart.tsx index 6e6768af5..380e333a3 100644 --- a/packages/zapp/console/src/components/common/BarChart.tsx +++ b/packages/zapp/console/src/components/common/BarChart.tsx @@ -146,7 +146,7 @@ export const BarChart: React.FC = ({ return (
- + {title}
diff --git a/packages/zapp/console/src/components/common/DumpJSON.tsx b/packages/zapp/console/src/components/common/DumpJSON.tsx index 57349eefb..9fc0676ef 100644 --- a/packages/zapp/console/src/components/common/DumpJSON.tsx +++ b/packages/zapp/console/src/components/common/DumpJSON.tsx @@ -2,7 +2,5 @@ import * as React from 'react'; import { ReactJsonViewWrapper } from 'components/common/ReactJsonView'; export const DumpJSON: React.FC<{ value: any }> = ({ value }) => { - return ( - - ); + return ; }; diff --git a/packages/zapp/console/src/components/common/NewTargetLink.tsx b/packages/zapp/console/src/components/common/NewTargetLink.tsx index 176a7f2db..df2cbfe1a 100644 --- a/packages/zapp/console/src/components/common/NewTargetLink.tsx +++ b/packages/zapp/console/src/components/common/NewTargetLink.tsx @@ -27,22 +27,21 @@ export const NewTargetLink: React.FC = (props) => { const commonStyles = useCommonStyles(); const styles = useStyles(); - const link = ( + const icon = external ? : null; + + return ( - {children} + {inline ? ( + + {children} + {icon} + + ) : ( +
+ {children} + {icon} +
+ )} ); - - const icon = external ? : null; - return inline ? ( - - {link} - {icon} - - ) : ( -
- {link} - {icon} -
- ); }; diff --git a/packages/zapp/console/src/components/common/ReactJsonView.tsx b/packages/zapp/console/src/components/common/ReactJsonView.tsx index ea7e9d1a1..c90618ab0 100644 --- a/packages/zapp/console/src/components/common/ReactJsonView.tsx +++ b/packages/zapp/console/src/components/common/ReactJsonView.tsx @@ -32,6 +32,9 @@ const useStyles = makeStyles((theme: Theme) => ({ '& .object-key': { color: `${theme.palette.grey[500]} !important`, }, + '& .node-ellipsis': { + color: `${theme.palette.grey[500]} !important`, + }, }, })); diff --git a/packages/zapp/console/src/components/common/test/NewTargetLink.spec.tsx b/packages/zapp/console/src/components/common/test/NewTargetLink.spec.tsx index 319200494..ca4a1fbf6 100644 --- a/packages/zapp/console/src/components/common/test/NewTargetLink.spec.tsx +++ b/packages/zapp/console/src/components/common/test/NewTargetLink.spec.tsx @@ -14,9 +14,8 @@ const renderExternalLink = () => ); test('renders a blank target link', () => { - const { getByText } = renderLink(); - const anchor = getByText(linkText); - expect(anchor).toHaveAttribute('target', '_blank'); + const { container } = renderLink(); + expect(container.firstElementChild).toHaveAttribute('target', '_blank'); }); test('renders with additional icon for external links', () => { diff --git a/packages/zapp/console/src/components/hooks/Entity/constants.ts b/packages/zapp/console/src/components/hooks/Entity/constants.ts new file mode 100644 index 000000000..6086866a3 --- /dev/null +++ b/packages/zapp/console/src/components/hooks/Entity/constants.ts @@ -0,0 +1,41 @@ +import { ResourceType } from 'models/Common/types'; +import { listTasks } from 'models/Task/api'; +import { listWorkflows } from 'models/Workflow/api'; +import { listLaunchPlans } from 'models/Launch/api'; +import { Workflow } from 'models/Workflow/types'; +import { Task } from 'models/Task/types'; +import { LaunchPlan } from 'models/Launch/types'; + +interface EntityFunctions { + description?: boolean; + executions?: boolean; + launch?: boolean; + listEntity?: any; +} + +export type EntityType = Workflow | Task | LaunchPlan; + +const unspecifiedFn = () => { + throw new Error('Unspecified Resourcetype.'); +}; +const unimplementedFn = () => { + throw new Error('Method not implemented.'); +}; + +export const entityFunctions: { [k in ResourceType]: EntityFunctions } = { + [ResourceType.DATASET]: { + listEntity: unimplementedFn, + }, + [ResourceType.LAUNCH_PLAN]: { + listEntity: listLaunchPlans, + }, + [ResourceType.TASK]: { + listEntity: listTasks, + }, + [ResourceType.UNSPECIFIED]: { + listEntity: unspecifiedFn, + }, + [ResourceType.WORKFLOW]: { + listEntity: listWorkflows, + }, +}; diff --git a/packages/zapp/console/src/components/hooks/Entity/useEntityVersions.ts b/packages/zapp/console/src/components/hooks/Entity/useEntityVersions.ts new file mode 100644 index 000000000..df2eb9ee3 --- /dev/null +++ b/packages/zapp/console/src/components/hooks/Entity/useEntityVersions.ts @@ -0,0 +1,20 @@ +import { IdentifierScope, Identifier, ResourceIdentifier } from 'models/Common/types'; +import { RequestConfig } from 'models/AdminEntity/types'; +import { entityStrings } from 'components/Entities/constants'; +import { usePagination } from '../usePagination'; +import { EntityType, entityFunctions } from './constants'; + +/** + * A hook for fetching a paginated list of entity versions. + * @param scope + * @param config + */ +export function useEntityVersions(scope: IdentifierScope, config: RequestConfig) { + const id = scope as ResourceIdentifier; + const listEntity = entityFunctions[id.resourceType]?.listEntity; + + return usePagination( + { ...config, cacheItems: true, fetchArg: scope }, + listEntity, + ); +} diff --git a/packages/zapp/console/src/components/hooks/useTask.ts b/packages/zapp/console/src/components/hooks/useTask.ts index 022cbf076..d602ad29c 100644 --- a/packages/zapp/console/src/components/hooks/useTask.ts +++ b/packages/zapp/console/src/components/hooks/useTask.ts @@ -18,7 +18,7 @@ export function useTaskTemplate(id: Identifier): FetchableData { useCache: true, debugName: 'TaskTemplate', defaultValue: {} as TaskTemplate, - doFetch: async (taskId) => (await getTask(taskId)).closure.compiledTask.template, + doFetch: async (taskId) => (await getTask(taskId)) as TaskTemplate, }, id, ); diff --git a/packages/zapp/console/src/components/hooks/useWorkflowVersions.ts b/packages/zapp/console/src/components/hooks/useWorkflowVersions.ts deleted file mode 100644 index a356ee2b0..000000000 --- a/packages/zapp/console/src/components/hooks/useWorkflowVersions.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { IdentifierScope } from 'models/Common/types'; -import { RequestConfig } from 'models/AdminEntity/types'; -import { listWorkflows } from 'models/Workflow/api'; -import { Workflow } from 'models/Workflow/types'; -import { usePagination } from './usePagination'; - -/** - * A hook for fetching a paginated list of workflow versions. - * @param scope - * @param config - */ -export function useWorkflowVersions(scope: IdentifierScope, config: RequestConfig) { - return usePagination( - { ...config, cacheItems: true, fetchArg: scope }, - listWorkflows, - ); -} diff --git a/packages/zapp/console/src/models/Task/types.ts b/packages/zapp/console/src/models/Task/types.ts index bfccb67d0..e4d53af75 100644 --- a/packages/zapp/console/src/models/Task/types.ts +++ b/packages/zapp/console/src/models/Task/types.ts @@ -26,6 +26,7 @@ export interface TaskTemplate extends Core.ITaskTemplate { id: Identifier; interface?: TypedInterface; metadata?: TaskMetadata; + closure?: TaskClosure; type: string; } diff --git a/packages/zapp/console/src/models/__mocks__/taskData.ts b/packages/zapp/console/src/models/__mocks__/taskData.ts index 2f4d1e02c..e044f9a66 100644 --- a/packages/zapp/console/src/models/__mocks__/taskData.ts +++ b/packages/zapp/console/src/models/__mocks__/taskData.ts @@ -1,8 +1,9 @@ import { getCacheKey } from 'components/Cache/utils'; import { Admin } from 'flyteidl'; import { cloneDeep } from 'lodash'; -import { Identifier } from 'models/Common/types'; +import { Identifier, ResourceType } from 'models/Common/types'; import { Task, TaskClosure } from 'models/Task/types'; +import { testDomain, testProject } from 'mocks/data/constants'; import * as simpleClosure from './simpleTaskClosure.json'; const decodedClosure = Admin.TaskClosure.create( @@ -12,8 +13,9 @@ const decodedClosure = Admin.TaskClosure.create( const taskId: (name: string, version: string) => Identifier = (name, version) => ({ name, version, - project: 'flyte', - domain: 'development', + project: testProject, + domain: testDomain, + resourceType: ResourceType.TASK, }); export const createMockTask: (name: string, version?: string) => Task = ( diff --git a/packages/zapp/console/src/models/__mocks__/workflowData.ts b/packages/zapp/console/src/models/__mocks__/workflowData.ts index 6f09c6b38..a7d5ad264 100644 --- a/packages/zapp/console/src/models/__mocks__/workflowData.ts +++ b/packages/zapp/console/src/models/__mocks__/workflowData.ts @@ -1,8 +1,9 @@ import { getCacheKey } from 'components/Cache/utils'; import { Admin } from 'flyteidl'; import { cloneDeep } from 'lodash'; -import { Identifier } from 'models/Common/types'; +import { Identifier, ResourceType } from 'models/Common/types'; import { Workflow, WorkflowClosure } from 'models/Workflow/types'; +import { testDomain, testProject } from 'mocks/data/constants'; import * as simpleClosure from './simpleWorkflowClosure.json'; const decodedClosure = Admin.WorkflowClosure.create( @@ -12,8 +13,9 @@ const decodedClosure = Admin.WorkflowClosure.create( const workflowId: (name: string, version: string) => Identifier = (name, version) => ({ name, version, - project: 'flyte', - domain: 'development', + project: testProject, + domain: testDomain, + resourceType: ResourceType.WORKFLOW, }); export const createMockWorkflow: (name: string, version?: string) => Workflow = ( diff --git a/packages/zapp/console/src/routes/ApplicationRouter.tsx b/packages/zapp/console/src/routes/ApplicationRouter.tsx index 6840edaa3..0336daf18 100644 --- a/packages/zapp/console/src/routes/ApplicationRouter.tsx +++ b/packages/zapp/console/src/routes/ApplicationRouter.tsx @@ -36,8 +36,8 @@ export const ApplicationRouter: React.FC = () => ( component={withSideNavigation(components.workflowDetails)} /> - makeProjectDomainBoundPath(project, domain, `/workflows/${workflowName}/version/${version}`), - path: `${projectDomainBasePath}/workflows/:workflowName/version/:workflowVersion`, + // Entity Version Details + static EntityVersionDetails = { + makeUrl: ( + project: string, + domain: string, + entityName: string, + entityType: string, + version: string, + ) => + makeProjectDomainBoundPath( + project, + domain, + `/${entityType}/${entityName}/version/${version}`, + ), + path: `${projectDomainBasePath}/:entityType/:entityName/version/:entityVersion`, }; // Tasks