diff --git a/src/components/Entities/EntityDetailsHeader.tsx b/src/components/Entities/EntityDetailsHeader.tsx
index ed9a3375b..ce41b9ce6 100644
--- a/src/components/Entities/EntityDetailsHeader.tsx
+++ b/src/components/Entities/EntityDetailsHeader.tsx
@@ -24,7 +24,7 @@ const useStyles = makeStyles((theme: Theme) => ({
width: '100%',
},
headerText: {
- margin: `0 ${theme.spacing(1)}px`,
+ margin: theme.spacing(0, 1),
},
headerTextContainer: {
display: 'flex',
diff --git a/src/components/Executions/ExecutionDetails/NodeExecutionTabs/index.tsx b/src/components/Executions/ExecutionDetails/NodeExecutionTabs/index.tsx
index c3c763845..0ba94c0f3 100644
--- a/src/components/Executions/ExecutionDetails/NodeExecutionTabs/index.tsx
+++ b/src/components/Executions/ExecutionDetails/NodeExecutionTabs/index.tsx
@@ -6,6 +6,7 @@ import { TaskTemplate } from 'models/Task/types';
import { useTabState } from 'components/hooks/useTabState';
import { PanelSection } from 'components/common/PanelSection';
import { DumpJSON } from 'components/common/DumpJSON';
+import { isMapTaskType } from 'models/Task/utils';
import { TaskExecutionsList } from '../../TaskExecutionsList/TaskExecutionsList';
import { NodeExecutionInputs } from './NodeExecutionInputs';
import { NodeExecutionOutputs } from './NodeExecutionOutputs';
@@ -17,6 +18,12 @@ const useStyles = makeStyles((theme) => {
},
tabs: {
borderBottom: `1px solid ${theme.palette.divider}`,
+ '& .MuiTabs-flexContainer': {
+ justifyContent: 'space-around',
+ },
+ },
+ tabItem: {
+ margin: theme.spacing(0, 1),
},
};
});
@@ -68,13 +75,15 @@ export const NodeExecutionTabs: React.FC<{
break;
}
}
+
+ const executionLabel = isMapTaskType(taskTemplate?.type) ? 'Map Execution' : 'Executions';
return (
<>
-
-
-
- {!!taskTemplate && }
+
+
+
+ {!!taskTemplate && }
{tabContent}
>
diff --git a/src/components/Executions/ExecutionDetails/Timeline/BarChart/utils.ts b/src/components/Executions/ExecutionDetails/Timeline/BarChart/utils.ts
index a8b1d9510..b4da4a72a 100644
--- a/src/components/Executions/ExecutionDetails/Timeline/BarChart/utils.ts
+++ b/src/components/Executions/ExecutionDetails/Timeline/BarChart/utils.ts
@@ -45,7 +45,6 @@ export const formatSecondsToHmsFormat = (seconds: number) => {
return `${seconds}s`;
};
-// narusina - check if exports are still needed
export const getOffsetColor = (isCachedValue: boolean[]) => {
const colors = isCachedValue.map((val) => (val === true ? CASHED_GREEN : TRANSPARENT));
return colors;
diff --git a/src/components/Executions/TaskExecutionsList/MapTaskExecutionListItem.tsx b/src/components/Executions/TaskExecutionsList/MapTaskExecutionListItem.tsx
new file mode 100644
index 000000000..078d4b680
--- /dev/null
+++ b/src/components/Executions/TaskExecutionsList/MapTaskExecutionListItem.tsx
@@ -0,0 +1,107 @@
+import * as React from 'react';
+import { makeStyles, Theme } from '@material-ui/core/styles';
+import Typography from '@material-ui/core/Typography';
+import classnames from 'classnames';
+import { PanelSection } from 'components/common/PanelSection';
+import { useCommonStyles } from 'components/common/styles';
+import { TaskExecutionPhase } from 'models/Execution/enums';
+import { TaskExecution } from 'models/Execution/types';
+import { MapTaskStatusInfo } from 'components/common/MapTaskExecutionsList/MapTaskStatusInfo';
+import { TaskExecutionDetails } from './TaskExecutionDetails';
+import { TaskExecutionError } from './TaskExecutionError';
+import { TaskExecutionLogs } from './TaskExecutionLogs';
+import { formatRetryAttempt, getGroupedLogs } from './utils';
+
+const useStyles = makeStyles((theme: Theme) => ({
+ detailsLink: {
+ fontWeight: 'normal',
+ },
+ header: {
+ marginBottom: theme.spacing(1),
+ },
+ title: {
+ marginBottom: theme.spacing(1),
+ },
+ showDetailsButton: {
+ marginTop: theme.spacing(1),
+ },
+ section: {
+ marginBottom: theme.spacing(2),
+ },
+}));
+
+interface MapTaskExecutionsListItemProps {
+ taskExecution: TaskExecution;
+ showAttempts: boolean;
+}
+
+const RENDER_ORDER: TaskExecutionPhase[] = [
+ TaskExecutionPhase.UNDEFINED,
+ TaskExecutionPhase.INITIALIZING,
+ TaskExecutionPhase.WAITING_FOR_RESOURCES,
+ TaskExecutionPhase.QUEUED,
+ TaskExecutionPhase.RUNNING,
+ TaskExecutionPhase.SUCCEEDED,
+ TaskExecutionPhase.ABORTED,
+ TaskExecutionPhase.FAILED,
+];
+
+/** Renders an individual `TaskExecution` record as part of a list */
+export const MapTaskExecutionsListItem: React.FC = ({
+ taskExecution,
+ showAttempts,
+}) => {
+ const commonStyles = useCommonStyles();
+ const styles = useStyles();
+
+ const { closure } = taskExecution;
+ const taskHasStarted = closure.phase >= TaskExecutionPhase.QUEUED;
+ const headerText = formatRetryAttempt(taskExecution.id.retryAttempt);
+ const logsInfo = getGroupedLogs(closure.metadata?.externalResources ?? []);
+
+ // Set UI elements in a proper rendering order
+ const logsSections: JSX.Element[] = [];
+ for (const key of RENDER_ORDER) {
+ const values = logsInfo.get(key);
+ if (values) {
+ logsSections.push();
+ }
+ }
+
+ return (
+
+ {/* Attempts header is ahown only if there is more than one attempt */}
+ {showAttempts ? (
+
+ ) : null}
+ {/* Error info is shown only if there is an error present for this map task */}
+ {closure.error ? (
+
+ ) : null}
+
+ {/* If main map task has log attached - show it here */}
+ {closure.logs && closure.logs.length > 0 ? (
+
+ ) : null}
+ {/* child/array logs separated by subtasks phase */}
+ {logsSections}
+
+ {/* If map task is actively started - show 'started' and 'run time' details */}
+ {taskHasStarted && (
+
+ )}
+
+ );
+};
diff --git a/src/components/Executions/TaskExecutionsList/TaskExecutionLogs.tsx b/src/components/Executions/TaskExecutionsList/TaskExecutionLogs.tsx
index f8b298d57..8bbe85496 100644
--- a/src/components/Executions/TaskExecutionsList/TaskExecutionLogs.tsx
+++ b/src/components/Executions/TaskExecutionsList/TaskExecutionLogs.tsx
@@ -1,9 +1,9 @@
import * as React from 'react';
import { Typography } from '@material-ui/core';
import { makeStyles, Theme } from '@material-ui/core/styles';
+import { Core } from 'flyteidl';
import { NewTargetLink } from 'components/common/NewTargetLink';
import { useCommonStyles } from 'components/common/styles';
-import { TaskLog } from 'models/Common/types';
import { noLogsFoundString } from '../constants';
const useStyles = makeStyles((theme: Theme) => ({
@@ -13,9 +13,12 @@ const useStyles = makeStyles((theme: Theme) => ({
sectionHeader: {
marginTop: theme.spacing(1),
},
+ logName: {
+ fontWeight: 'lighter',
+ },
}));
-const TaskLogList: React.FC<{ logs: TaskLog[] }> = ({ logs }) => {
+export const TaskLogList: React.FC<{ logs: Core.ITaskLog[] }> = ({ logs }) => {
const styles = useStyles();
const commonStyles = useCommonStyles();
if (!(logs && logs.length > 0)) {
@@ -23,11 +26,16 @@ const TaskLogList: React.FC<{ logs: TaskLog[] }> = ({ logs }) => {
}
return (
<>
- {logs.map(({ name, uri }) => (
-
- {name}
-
- ))}
+ {logs.map(({ name, uri }) =>
+ uri ? (
+
+ {name}
+
+ ) : (
+ // If there is no url, show item a a name string only, as it's not really clickable
+ {name}
+ ),
+ )}
>
);
};
@@ -35,12 +43,15 @@ const TaskLogList: React.FC<{ logs: TaskLog[] }> = ({ logs }) => {
/** Renders log links from a `taskLogs`(aka taskExecution.closure.logs), if they exist.
* Otherwise renders a message indicating that no logs are available.
*/
-export const TaskExecutionLogs: React.FC<{ taskLogs: TaskLog[] }> = ({ taskLogs }) => {
+export const TaskExecutionLogs: React.FC<{ taskLogs: Core.ITaskLog[]; title?: string }> = ({
+ taskLogs,
+ title,
+}) => {
const styles = useStyles();
return (
- Logs
+ {title ?? 'Logs'}
diff --git a/src/components/Executions/TaskExecutionsList/TaskExecutions.mocks.ts b/src/components/Executions/TaskExecutionsList/TaskExecutions.mocks.ts
index 8fc2432f3..5d7ead286 100644
--- a/src/components/Executions/TaskExecutionsList/TaskExecutions.mocks.ts
+++ b/src/components/Executions/TaskExecutionsList/TaskExecutions.mocks.ts
@@ -1,9 +1,10 @@
-import { Protobuf } from 'flyteidl';
-import { MessageFormat, ResourceType } from 'models/Common/types';
+import { Protobuf, Event } from 'flyteidl';
+import { MessageFormat, ResourceType, TaskLog } from 'models/Common/types';
import { TaskExecutionPhase } from 'models/Execution/enums';
import { TaskExecution } from 'models/Execution/types';
import * as Long from 'long';
+import { TaskType } from 'models/Task/constants';
// we probably will create a new helper function in future, to make testing/storybooks closer to what we see in API Json responses
const getProtobufTimestampFromIsoTime = (isoDateTime: string): Protobuf.ITimestamp => {
@@ -14,6 +15,19 @@ const getProtobufTimestampFromIsoTime = (isoDateTime: string): Protobuf.ITimesta
return timestamp;
};
+const getProtobufDurationFromString = (durationSec: string): Protobuf.Duration => {
+ const secondsInt = parseInt(durationSec, 10);
+ const duration = new Protobuf.Duration();
+ duration.seconds = Long.fromInt(secondsInt);
+ return duration;
+};
+
+export const MockTaskExceutionLog: TaskLog = {
+ uri: '#',
+ name: 'Cloudwatch Logs (User)',
+ messageFormat: MessageFormat.JSON,
+};
+
export const MockPythonTaskExecution: TaskExecution = {
id: {
taskId: {
@@ -38,16 +52,79 @@ export const MockPythonTaskExecution: TaskExecution = {
outputUri:
's3://flyte-demo/metadata/propeller/flytesnacks-development-ogaayir2e3/athenaworkflowsexamplesayhello/data/0/outputs.pb',
phase: TaskExecutionPhase.SUCCEEDED,
- logs: [
- {
- uri: 'https://console.aws.amazon.com/cloudwatch/home?region=us-east-2#logEventViewer:group=/aws/containerinsights/flyte-demo-2/application;stream=var.log.containers.ogaayir2e3-ff65vi3y-0_flytesnacks-development_ogaayir2e3-ff65vi3y-0-380d210ccaac45a6e2314a155822b36a67e044914069d01323bc18832487ac4a.log',
- name: 'Cloudwatch Logs (User)',
- messageFormat: MessageFormat.JSON,
- },
- ],
+ logs: [MockTaskExceutionLog],
createdAt: getProtobufTimestampFromIsoTime('2022-03-17T21:30:53.469624134Z'),
updatedAt: getProtobufTimestampFromIsoTime('2022-03-17T21:31:04.011303736Z'),
reason: 'task submitted to K8s',
- taskType: 'python-task',
+ taskType: TaskType.PYTHON,
+ },
+};
+
+export const getMockMapTaskLogItem = (
+ phase: TaskExecutionPhase,
+ hasLogs: boolean,
+ index?: number,
+ retryAttempt?: number,
+): Event.IExternalResourceInfo => {
+ const retryString = retryAttempt && retryAttempt > 0 ? `-${retryAttempt}` : '';
+ return {
+ externalId: `y286hpfvwh-n0-0-${index ?? 0}`,
+ index: index,
+ phase: phase,
+ retryAttempt: retryAttempt,
+ logs: hasLogs
+ ? [
+ {
+ uri: '#',
+ name: `Kubernetes Logs #0-${index ?? 0}${retryString} (State)`,
+ messageFormat: MessageFormat.JSON,
+ },
+ ]
+ : [],
+ };
+};
+
+export const MockMapTaskExecution: TaskExecution = {
+ id: {
+ taskId: {
+ resourceType: ResourceType.TASK,
+ project: 'flytesnacks',
+ domain: 'development',
+ name: 'flyte.workflows.example.mapper_a_mappable_task_0',
+ version: 'v2',
+ },
+ nodeExecutionId: {
+ nodeId: 'n0',
+ executionId: {
+ project: 'flytesnacks',
+ domain: 'development',
+ name: 'y286hpfvwh',
+ },
+ },
+ },
+ inputUri:
+ 's3://my-s3-bucket/metadata/propeller/sandbox/flytesnacks-development-y286hpfvwh/n0/data/inputs.pb',
+ closure: {
+ outputUri:
+ 's3://my-s3-bucket/metadata/propeller/sandbox/flytesnacks-development-y286hpfvwh/n0/data/0/outputs.pb',
+ phase: TaskExecutionPhase.SUCCEEDED,
+ startedAt: getProtobufTimestampFromIsoTime('2022-03-30T19:31:09.487343Z'),
+ duration: getProtobufDurationFromString('190.302384340s'),
+ createdAt: getProtobufTimestampFromIsoTime('2022-03-30T19:31:09.487343693Z'),
+ updatedAt: getProtobufTimestampFromIsoTime('2022-03-30T19:34:19.789727340Z'),
+ taskType: 'container_array',
+ metadata: {
+ generatedName: 'y286hpfvwh-n0-0',
+ externalResources: [
+ getMockMapTaskLogItem(TaskExecutionPhase.SUCCEEDED, true),
+ getMockMapTaskLogItem(TaskExecutionPhase.SUCCEEDED, true, 1),
+ getMockMapTaskLogItem(TaskExecutionPhase.SUCCEEDED, true, 2),
+ getMockMapTaskLogItem(TaskExecutionPhase.FAILED, true, 3),
+ getMockMapTaskLogItem(TaskExecutionPhase.SUCCEEDED, true, 3, 1),
+ getMockMapTaskLogItem(TaskExecutionPhase.SUCCEEDED, true, 4),
+ getMockMapTaskLogItem(TaskExecutionPhase.FAILED, false, 5),
+ ],
+ pluginIdentifier: 'k8s-array',
+ },
},
};
diff --git a/src/components/Executions/TaskExecutionsList/TaskExecutionsList.tsx b/src/components/Executions/TaskExecutionsList/TaskExecutionsList.tsx
index 0a991672b..16915dac9 100644
--- a/src/components/Executions/TaskExecutionsList/TaskExecutionsList.tsx
+++ b/src/components/Executions/TaskExecutionsList/TaskExecutionsList.tsx
@@ -1,10 +1,12 @@
+import * as React from 'react';
import { makeStyles, Theme } from '@material-ui/core/styles';
import { noExecutionsFoundString } from 'common/constants';
import { NonIdealState } from 'components/common/NonIdealState';
import { WaitForData } from 'components/common/WaitForData';
import { NodeExecution, TaskExecution } from 'models/Execution/types';
-import * as React from 'react';
+import { isMapTaskType } from 'models/Task/utils';
import { useTaskExecutions, useTaskExecutionsRefresher } from '../useTaskExecutions';
+import { MapTaskExecutionsListItem } from './MapTaskExecutionListItem';
import { TaskExecutionsListItem } from './TaskExecutionsListItem';
import { getUniqueTaskExecutionName } from './utils';
@@ -31,14 +33,26 @@ export const TaskExecutionsListContent: React.FC<{
/>
);
}
+
return (
<>
- {taskExecutions.map((taskExecution) => (
-
- ))}
+ {taskExecutions.map((taskExecution) => {
+ const taskType = taskExecution.closure.taskType ?? undefined;
+ const useNewMapTaskView =
+ isMapTaskType(taskType) && taskExecution.closure.metadata?.externalResources;
+ return useNewMapTaskView ? (
+ 1}
+ />
+ ) : (
+
+ );
+ })}
>
);
};
diff --git a/src/components/Executions/TaskExecutionsList/TaskExecutionsListContent.stories.tsx b/src/components/Executions/TaskExecutionsList/TaskExecutionsListContent.stories.tsx
index 4b7823ecd..e712c2441 100644
--- a/src/components/Executions/TaskExecutionsList/TaskExecutionsListContent.stories.tsx
+++ b/src/components/Executions/TaskExecutionsList/TaskExecutionsListContent.stories.tsx
@@ -1,9 +1,16 @@
import * as React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
+import { Core } from 'flyteidl';
import { TaskExecutionPhase } from 'models/Execution/enums';
import { PanelViewDecorator } from 'components/common/__stories__/Decorators';
+import { TaskType } from 'models/Task/constants';
import { TaskExecutionsListContent } from './TaskExecutionsList';
-import { MockPythonTaskExecution } from './TaskExecutions.mocks';
+import {
+ getMockMapTaskLogItem,
+ MockMapTaskExecution,
+ MockPythonTaskExecution,
+ MockTaskExceutionLog,
+} from './TaskExecutions.mocks';
export default {
title: 'Task/NodeExecutionTabs',
@@ -29,3 +36,64 @@ PythonTaskWithRetry.args = {
{ ...MockPythonTaskExecution, id: { ...MockPythonTaskExecution.id, retryAttempt: 1 } },
],
};
+
+export const MapTaskOldView = Template.bind({});
+MapTaskOldView.args = {
+ taskExecutions: [
+ {
+ ...MockPythonTaskExecution,
+ closure: {
+ ...MockPythonTaskExecution.closure,
+ taskType: TaskType.ARRAY,
+ logs: new Array(5).fill(MockTaskExceutionLog),
+ },
+ },
+ ],
+};
+
+export const MapTaskBase = Template.bind({});
+MapTaskBase.args = {
+ taskExecutions: [MockMapTaskExecution],
+};
+
+export const MapTaskEverything = Template.bind({});
+MapTaskEverything.decorators = [(Story) => PanelViewDecorator(Story)];
+MapTaskEverything.args = {
+ taskExecutions: [
+ {
+ ...MockMapTaskExecution,
+ closure: {
+ ...MockMapTaskExecution.closure,
+ logs: [MockTaskExceutionLog],
+ error: {
+ code: '666',
+ message: 'Fake error occured, if you know what I mean',
+ errorUri: '#',
+ kind: Core.ExecutionError.ErrorKind.USER,
+ },
+ metadata: {
+ ...MockMapTaskExecution.closure.metadata,
+ externalResources: [
+ getMockMapTaskLogItem(TaskExecutionPhase.ABORTED, true, 1),
+ getMockMapTaskLogItem(TaskExecutionPhase.ABORTED, true, 5),
+ getMockMapTaskLogItem(TaskExecutionPhase.FAILED, true, 2),
+ getMockMapTaskLogItem(TaskExecutionPhase.FAILED, true, 2, 1),
+ getMockMapTaskLogItem(TaskExecutionPhase.FAILED, true, 10),
+ getMockMapTaskLogItem(TaskExecutionPhase.FAILED, true, 10, 1),
+ getMockMapTaskLogItem(TaskExecutionPhase.INITIALIZING, false, 3),
+ getMockMapTaskLogItem(TaskExecutionPhase.INITIALIZING, false, 12),
+ getMockMapTaskLogItem(TaskExecutionPhase.WAITING_FOR_RESOURCES, false, 4),
+ getMockMapTaskLogItem(TaskExecutionPhase.QUEUED, true, 6),
+ getMockMapTaskLogItem(TaskExecutionPhase.QUEUED, true, 7),
+ getMockMapTaskLogItem(TaskExecutionPhase.RUNNING, true, 8),
+ getMockMapTaskLogItem(TaskExecutionPhase.UNDEFINED, false, 9),
+ getMockMapTaskLogItem(TaskExecutionPhase.SUCCEEDED, true),
+ getMockMapTaskLogItem(TaskExecutionPhase.SUCCEEDED, true, 2, 2),
+ getMockMapTaskLogItem(TaskExecutionPhase.SUCCEEDED, true, 11),
+ ],
+ },
+ },
+ },
+ { ...MockMapTaskExecution, id: { ...MockMapTaskExecution.id, retryAttempt: 1 } },
+ ],
+};
diff --git a/src/components/Executions/TaskExecutionsList/test/utils.spec.ts b/src/components/Executions/TaskExecutionsList/test/utils.spec.ts
index 831820d7b..53b0a4599 100644
--- a/src/components/Executions/TaskExecutionsList/test/utils.spec.ts
+++ b/src/components/Executions/TaskExecutionsList/test/utils.spec.ts
@@ -1,5 +1,8 @@
+import { Event } from 'flyteidl';
+import { TaskExecutionPhase } from 'models/Execution/enums';
import { obj } from 'test/utils';
-import { formatRetryAttempt, getUniqueTaskExecutionName } from '../utils';
+import { getMockMapTaskLogItem } from '../TaskExecutions.mocks';
+import { formatRetryAttempt, getGroupedLogs, getUniqueTaskExecutionName } from '../utils';
describe('getUniqueTaskExecutionName', () => {
const cases: [{ name: string; retryAttempt: number }, string][] = [
@@ -33,3 +36,29 @@ describe('formatRetryAttempt', () => {
expect(formatRetryAttempt(input)).toEqual(expected)),
);
});
+
+describe('getGroupedLogs', () => {
+ const resources: Event.IExternalResourceInfo[] = [
+ getMockMapTaskLogItem(TaskExecutionPhase.SUCCEEDED, true),
+ getMockMapTaskLogItem(TaskExecutionPhase.FAILED, true, 1),
+ getMockMapTaskLogItem(TaskExecutionPhase.FAILED, true, 1, 1),
+ getMockMapTaskLogItem(TaskExecutionPhase.SUCCEEDED, true, 1, 2),
+ getMockMapTaskLogItem(TaskExecutionPhase.FAILED, false, 2),
+ ];
+
+ it(`Should properly group to Success and Failed`, () => {
+ const logs = getGroupedLogs(resources);
+ // Do not have key which was not in the logs
+ expect(logs.get(TaskExecutionPhase.QUEUED)).toBeUndefined();
+
+ // To have keys which were in the logs
+ expect(logs.get(TaskExecutionPhase.SUCCEEDED)).not.toBeUndefined();
+ expect(logs.get(TaskExecutionPhase.FAILED)).not.toBeUndefined();
+
+ // to include all items with last retry iterations
+ expect(logs.get(TaskExecutionPhase.SUCCEEDED)?.length).toEqual(2);
+
+ // to filter our previous retry attempt
+ expect(logs.get(TaskExecutionPhase.FAILED)?.length).toEqual(1);
+ });
+});
diff --git a/src/components/Executions/TaskExecutionsList/utils.ts b/src/components/Executions/TaskExecutionsList/utils.ts
index 8d71d2809..356d51b6d 100644
--- a/src/components/Executions/TaskExecutionsList/utils.ts
+++ b/src/components/Executions/TaskExecutionsList/utils.ts
@@ -1,4 +1,6 @@
import { leftPaddedNumber } from 'common/formatters';
+import { Core, Event } from 'flyteidl';
+import { TaskExecutionPhase } from 'models/Execution/enums';
import { TaskExecution } from 'models/Execution/types';
/** Generates a unique name for a task execution, suitable for display in a
@@ -22,3 +24,42 @@ export function formatRetryAttempt(attempt: number | string | undefined): string
// Retry attempts are zero-based, so incrementing before formatting
return `Attempt ${leftPaddedNumber(parsed + 1, 2)}`;
}
+
+export const getGroupedLogs = (
+ resources: Event.IExternalResourceInfo[],
+): Map => {
+ const logsInfo = new Map();
+
+ // sort output sample [0-2, 0-1, 0, 1, 2], where 0-1 means index = 0 retry = 1
+ resources.sort((a, b) => {
+ const aIndex = a.index ?? 0;
+ const bIndex = b.index ?? 0;
+ if (aIndex !== bIndex) {
+ // return smaller index first
+ return aIndex - bIndex;
+ }
+
+ const aRetry = a.retryAttempt ?? 0;
+ const bRetry = b.retryAttempt ?? 0;
+ return bRetry - aRetry;
+ });
+
+ let lastIndex = -1;
+ for (const item of resources) {
+ if (item.index === lastIndex) {
+ // skip, as we already added final retry to data
+ continue;
+ }
+ const phase = item.phase ?? TaskExecutionPhase.UNDEFINED;
+ const currentValue = logsInfo.get(phase);
+ lastIndex = item.index ?? 0;
+ if (item.logs) {
+ // if there is no log with active url, just create an item with externalId,
+ // for user to understand which array items are in this state
+ const newLogs = item.logs.length > 0 ? item.logs : [{ name: item.externalId }];
+ logsInfo.set(phase, currentValue ? [...currentValue, ...newLogs] : [...newLogs]);
+ }
+ }
+
+ return logsInfo;
+};
diff --git a/src/components/common/MapTaskExecutionsList/MapTaskStatusInfo.stories.tsx b/src/components/common/MapTaskExecutionsList/MapTaskStatusInfo.stories.tsx
index 1927630cf..f5fdfc86b 100644
--- a/src/components/common/MapTaskExecutionsList/MapTaskStatusInfo.stories.tsx
+++ b/src/components/common/MapTaskExecutionsList/MapTaskStatusInfo.stories.tsx
@@ -1,11 +1,11 @@
import * as React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
-import { NodeExecutionPhase } from 'models/Execution/enums';
+import { TaskExecutionPhase } from 'models/Execution/enums';
import { MapTaskStatusInfo } from './MapTaskStatusInfo';
import { PanelViewDecorator } from '../__stories__/Decorators';
export default {
- title: 'Task/MapTaskExecutionList/MapTaskStatusInfo',
+ title: 'Task/NodeExecutionTabs/MapTaskStatusInfo',
component: MapTaskStatusInfo,
parameters: { actions: { argTypesRegex: 'toggleExpanded' } },
} as ComponentMeta;
@@ -24,12 +24,12 @@ Default.args = {
{ uri: '#', name: 'Kubernetes Logs #0-3' },
{ uri: '#', name: 'Kubernetes Logs #0-4' },
],
- status: NodeExecutionPhase.QUEUED,
+ status: TaskExecutionPhase.QUEUED,
expanded: true,
};
export const AllSpace = Template.bind({});
AllSpace.args = {
taskLogs: [],
- status: NodeExecutionPhase.SUCCEEDED,
+ status: TaskExecutionPhase.SUCCEEDED,
};
diff --git a/src/components/common/MapTaskExecutionsList/MapTaskStatusInfo.test.tsx b/src/components/common/MapTaskExecutionsList/MapTaskStatusInfo.test.tsx
index 768535f50..219cd4d32 100644
--- a/src/components/common/MapTaskExecutionsList/MapTaskStatusInfo.test.tsx
+++ b/src/components/common/MapTaskExecutionsList/MapTaskStatusInfo.test.tsx
@@ -1,7 +1,7 @@
import { fireEvent, render, waitFor } from '@testing-library/react';
import { noLogsFoundString } from 'components/Executions/constants';
-import { getNodeExecutionPhaseConstants } from 'components/Executions/utils';
-import { NodeExecutionPhase } from 'models/Execution/enums';
+import { getTaskExecutionPhaseConstants } from 'components/Executions/utils';
+import { TaskExecutionPhase } from 'models/Execution/enums';
import * as React from 'react';
import { MapTaskStatusInfo } from './MapTaskStatusInfo';
@@ -14,8 +14,8 @@ const taskLogs = [
describe('MapTaskStatusInfo', () => {
it('Phase and amount of links rendered correctly', async () => {
- const status = NodeExecutionPhase.RUNNING;
- const phaseData = getNodeExecutionPhaseConstants(status);
+ const status = TaskExecutionPhase.RUNNING;
+ const phaseData = getTaskExecutionPhaseConstants(status);
const { queryByText, getByTitle } = render(
,
@@ -29,13 +29,13 @@ describe('MapTaskStatusInfo', () => {
const buttonEl = getByTitle('Expand row');
fireEvent.click(buttonEl);
await waitFor(() => {
- expect(queryByText('Logs')).toBeInTheDocument();
+ expect(queryByText(taskLogs[0].name)).toBeInTheDocument();
});
});
it('Phase with no links show proper texts when opened', () => {
- const status = NodeExecutionPhase.ABORTED;
- const phaseData = getNodeExecutionPhaseConstants(status);
+ const status = TaskExecutionPhase.ABORTED;
+ const phaseData = getTaskExecutionPhaseConstants(status);
const { queryByText } = render(
,
diff --git a/src/components/common/MapTaskExecutionsList/MapTaskStatusInfo.tsx b/src/components/common/MapTaskExecutionsList/MapTaskStatusInfo.tsx
index 451fbac29..7ed9fca1a 100644
--- a/src/components/common/MapTaskExecutionsList/MapTaskStatusInfo.tsx
+++ b/src/components/common/MapTaskExecutionsList/MapTaskStatusInfo.tsx
@@ -1,12 +1,12 @@
import * as React from 'react';
import { useState } from 'react';
+import { Typography } from '@material-ui/core';
import { makeStyles, Theme } from '@material-ui/core/styles';
import { RowExpander } from 'components/Executions/Tables/RowExpander';
-import { NodeExecutionPhase } from 'models/Execution/enums';
-import { getNodeExecutionPhaseConstants } from 'components/Executions/utils';
-import { Typography } from '@material-ui/core';
-import { TaskExecutionLogs } from 'components/Executions/TaskExecutionsList/TaskExecutionLogs';
-import { TaskLog } from 'models/Common/types';
+import { TaskExecutionPhase } from 'models/Execution/enums';
+import { getTaskExecutionPhaseConstants } from 'components/Executions/utils';
+import { TaskLogList } from 'components/Executions/TaskExecutionsList/TaskExecutionLogs';
+import { Core } from 'flyteidl';
const useStyles = makeStyles((_theme: Theme) => ({
mainWrapper: {
@@ -36,8 +36,8 @@ const useStyles = makeStyles((_theme: Theme) => ({
}));
interface MapTaskStatusInfoProps {
- taskLogs: TaskLog[];
- status: NodeExecutionPhase;
+ taskLogs: Core.ITaskLog[];
+ status: TaskExecutionPhase;
expanded: boolean;
}
@@ -49,7 +49,7 @@ export const MapTaskStatusInfo = (props: MapTaskStatusInfoProps) => {
setExpanded(!expanded);
};
- const phaseData = getNodeExecutionPhaseConstants(props.status);
+ const phaseData = getTaskExecutionPhaseConstants(props.status);
return (
@@ -62,7 +62,7 @@ export const MapTaskStatusInfo = (props: MapTaskStatusInfoProps) => {
{expanded && (
-
+
)}
diff --git a/src/flyteidl/index.ts b/src/flyteidl/index.ts
index 48a53823d..6071ed9e9 100644
--- a/src/flyteidl/index.ts
+++ b/src/flyteidl/index.ts
@@ -4,8 +4,9 @@ import { flyteidl, google } from '@flyteorg/flyteidl/gen/pb-js/flyteidl';
import admin = flyteidl.admin;
import core = flyteidl.core;
import service = flyteidl.service;
+import event = flyteidl.event;
/** Message classes for built-in Protobuf types */
import protobuf = google.protobuf;
-export { admin as Admin, core as Core, service as Service, protobuf as Protobuf };
+export { admin as Admin, core as Core, service as Service, protobuf as Protobuf, event as Event };