Skip to content

Commit

Permalink
Apply comments
Browse files Browse the repository at this point in the history
  • Loading branch information
vanessa committed Sep 13, 2024
1 parent 656ca9a commit 5ba4ebf
Show file tree
Hide file tree
Showing 11 changed files with 200 additions and 58 deletions.
18 changes: 18 additions & 0 deletions client/src/route/digitaltwins/DigitalTwinTabDataPreview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ITabs } from 'route/IData';

const tabs: ITabs[] = [
{
label: 'Create',
body: `Create digital twins from tools provided within user workspaces. Each digital twin will have one directory. It is suggested that user provide one bash shell script to run their digital twin. Users can create the required scripts and other files from tools provided in Workbench page.`,
},
{
label: 'Execute',
body: 'This page demonstrates integration of DTaaS with gitlab CI/CD workflows. The feature is experimental and requires certain gitlab setup in order for it to work.',
},
{
label: 'Analyze',
body: 'The analysis of digital twins requires running of digital twin script from user workspace. The execution results placed within data directory are processed by analysis scripts and results are placed back in the data directory. These scripts can either be executed from VSCode and graphical results or can be executed from VNC GUI.',
},
];

export default tabs;
2 changes: 1 addition & 1 deletion client/src/route/digitaltwins/LogDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function LogDialog({ showLog, setShowLog, name }: LogDialogProps) {
onClose={() => handleCloseLog(setShowLog)}
maxWidth="md"
>
<DialogTitle>{`${formatName(name)} - log (run #${digitalTwin.executionCount})`}</DialogTitle>
<DialogTitle>{`${formatName(name)} log`}</DialogTitle>
<DialogContent dividers>
{digitalTwin.jobLogs.length > 0 ? (
digitalTwin.jobLogs.map((jobLog, index) => (
Expand Down
153 changes: 136 additions & 17 deletions client/src/route/digitaltwins/pipelineChecks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Dispatch, SetStateAction } from 'react';
import { useDispatch } from 'react-redux';
import DigitalTwin from 'util/gitlabDigitalTwin';
import { AlertColor } from '@mui/material';
import DigitalTwin, { formatName } from 'util/gitlabDigitalTwin';
import { fetchJobLogs, updatePipelineStateOnCompletion } from './pipelineUtils';
import { setSnackbar } from './pipelineHandler';

interface PipelineStatusParams {
setButtonText: Dispatch<SetStateAction<string>>;
Expand All @@ -10,35 +12,116 @@ interface PipelineStatusParams {
dispatch: ReturnType<typeof useDispatch>;
}

export const checkFirstPipelineStatus = async ({
const MAX_EXECUTION_TIME = 10 * 60 * 1000;
const delay = (ms: number) =>
new Promise((resolve) => {
setTimeout(resolve, ms);
});

const hasTimedOut = (startTime: number) =>
Date.now() - startTime > MAX_EXECUTION_TIME;

const handleTimeout = (
DTName: string,
setButtonText: Dispatch<SetStateAction<string>>,
setLogButtonDisabled: Dispatch<SetStateAction<boolean>>,
setSnackbarMessage: Dispatch<SetStateAction<string>>,
setSnackbarSeverity: Dispatch<SetStateAction<AlertColor>>,
setSnackbarOpen: Dispatch<SetStateAction<boolean>>,
) => {
setSnackbar(
`Execution timed out for ${formatName(DTName)}`,
'error',
setSnackbarMessage,
setSnackbarSeverity,
setSnackbarOpen,
);
setButtonText('Start');
setLogButtonDisabled(false);
};

const checkFirstPipelineStatus = async ({
setButtonText,
digitalTwin,
setLogButtonDisabled,
dispatch,
}: PipelineStatusParams) => {
startTime,
setSnackbarMessage,
setSnackbarSeverity,
setSnackbarOpen,
}: PipelineStatusParams & {
startTime: number;
setSnackbarMessage: Dispatch<SetStateAction<string>>;
setSnackbarSeverity: Dispatch<SetStateAction<AlertColor>>;
setSnackbarOpen: Dispatch<SetStateAction<boolean>>;
}) => {
const pipelineStatus = await digitalTwin.gitlabInstance.getPipelineStatus(
digitalTwin.gitlabInstance.projectId!,
digitalTwin.pipelineId!,
);
const params = {
setButtonText,
digitalTwin,
setLogButtonDisabled,
dispatch,
};
if (pipelineStatus === 'success' || pipelineStatus === 'failed') {
await checkSecondPipelineStatus(params);

if (pipelineStatus === 'success') {
await checkSecondPipelineStatus({
setButtonText,
digitalTwin,
setLogButtonDisabled,
dispatch,
startTime,
setSnackbarMessage,
setSnackbarSeverity,
setSnackbarOpen,
});
} else if (pipelineStatus === 'failed') {
const jobLogs = await fetchJobLogs(
digitalTwin.gitlabInstance,
digitalTwin.pipelineId!,
);
updatePipelineStateOnCompletion(
digitalTwin,
jobLogs,
setButtonText,
setLogButtonDisabled,
dispatch,
);
} else if (hasTimedOut(startTime)) {
handleTimeout(
digitalTwin.DTName,
setButtonText,
setLogButtonDisabled,
setSnackbarMessage,
setSnackbarSeverity,
setSnackbarOpen,
);
} else {
checkFirstPipelineStatus(params);
await delay(5000);
checkFirstPipelineStatus({
setButtonText,
digitalTwin,
setLogButtonDisabled,
dispatch,
startTime,
setSnackbarMessage,
setSnackbarSeverity,
setSnackbarOpen,
});
}
};

export const checkSecondPipelineStatus = async ({
const checkSecondPipelineStatus = async ({
setButtonText,
digitalTwin,
setLogButtonDisabled,
dispatch,
}: PipelineStatusParams) => {
startTime,
setSnackbarMessage,
setSnackbarSeverity,
setSnackbarOpen,
}: PipelineStatusParams & {
startTime: number;
setSnackbarMessage: Dispatch<SetStateAction<string>>;
setSnackbarSeverity: Dispatch<SetStateAction<AlertColor>>;
setSnackbarOpen: Dispatch<SetStateAction<boolean>>;
}) => {
const pipelineStatus = await digitalTwin.gitlabInstance.getPipelineStatus(
digitalTwin.gitlabInstance.projectId!,
digitalTwin.pipelineId! + 1,
Expand All @@ -57,13 +140,49 @@ export const checkSecondPipelineStatus = async ({
setLogButtonDisabled,
dispatch,
);

if (pipelineStatus === 'failed') {
setSnackbar(
`Execution failed for ${formatName(digitalTwin.DTName)}`,
'error',
setSnackbarMessage,
setSnackbarSeverity,
setSnackbarOpen,
);
}
} else if (hasTimedOut(startTime)) {
handleTimeout(
digitalTwin.DTName,
setButtonText,
setLogButtonDisabled,
setSnackbarMessage,
setSnackbarSeverity,
setSnackbarOpen,
);
} else {
const params = {
await delay(5000);
checkSecondPipelineStatus({
setButtonText,
digitalTwin,
setLogButtonDisabled,
dispatch,
};
checkSecondPipelineStatus(params);
startTime,
setSnackbarMessage,
setSnackbarSeverity,
setSnackbarOpen,
});
}
};

const startPipelineStatusCheck = (
params: PipelineStatusParams & {
setSnackbarMessage: Dispatch<SetStateAction<string>>;
setSnackbarSeverity: Dispatch<SetStateAction<AlertColor>>;
setSnackbarOpen: Dispatch<SetStateAction<boolean>>;
},
) => {
const startTime = Date.now();
checkFirstPipelineStatus({ ...params, startTime });
};

export default startPipelineStatusCheck;
15 changes: 8 additions & 7 deletions client/src/route/digitaltwins/pipelineHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
updatePipelineState,
updatePipelineStateOnStop,
} from './pipelineUtils';
import { checkFirstPipelineStatus } from './pipelineChecks';
import startPipelineStatusCheck from './pipelineChecks';

export const handleButtonClick = (
buttonText: string,
Expand Down Expand Up @@ -61,16 +61,17 @@ export const handleStart = async (
setSnackbarMessage,
setSnackbarSeverity,
setSnackbarOpen,
setLogButtonDisabled,
dispatch,
);
const params = {
setButtonText,
digitalTwin,
setLogButtonDisabled,
dispatch,
setSnackbarMessage,
setSnackbarSeverity,
setSnackbarOpen,
};
checkFirstPipelineStatus(params);
startPipelineStatusCheck(params);
} else {
setButtonText('Start');
}
Expand All @@ -87,15 +88,15 @@ export const handleStop = async (
try {
await stopPipelines(digitalTwin);
setSnackbar(
`Execution stopped successfully for ${formatName(digitalTwin.DTName)} (Run #${digitalTwin.executionCount})`,
`Execution stopped successfully for ${formatName(digitalTwin.DTName)}`,
'success',
setSnackbarMessage,
setSnackbarSeverity,
setSnackbarOpen,
);
} catch (error) {
setSnackbar(
`Execution stop failed for ${digitalTwin.DTName} (Run #${digitalTwin.executionCount})`,
`Execution stop failed for ${digitalTwin.DTName})`,
'error',
setSnackbarMessage,
setSnackbarSeverity,
Expand All @@ -119,7 +120,7 @@ const stopPipelines = async (digitalTwin: DigitalTwin) => {
}
};

const setSnackbar = (
export const setSnackbar = (
message: string,
severity: AlertColor,
setSnackbarMessage: Dispatch<SetStateAction<string>>,
Expand Down
19 changes: 9 additions & 10 deletions client/src/route/digitaltwins/pipelineUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { AlertColor } from '@mui/material';
import DigitalTwin, { formatName } from 'util/gitlabDigitalTwin';
import { GitlabInstance } from 'util/gitlab';
import {
incrementExecutionCount,
setJobLogs,
setPipelineCompleted,
setPipelineLoading,
Expand All @@ -15,15 +14,12 @@ export const startPipeline = async (
setSnackbarMessage: Dispatch<SetStateAction<string>>,
setSnackbarSeverity: Dispatch<SetStateAction<AlertColor>>,
setSnackbarOpen: Dispatch<SetStateAction<boolean>>,
setLogButtonDisabled: Dispatch<SetStateAction<boolean>>,
dispatch: ReturnType<typeof useDispatch>,
) => {
await digitalTwin.execute();
dispatch(incrementExecutionCount({ assetName: digitalTwin.DTName }));
const executionStatusMessage =
digitalTwin.lastExecutionStatus === 'success'
? `Execution started successfully for ${formatName(digitalTwin.DTName)} (Run #${digitalTwin.executionCount}). Wait until completion for the logs...`
: `Execution ${digitalTwin.lastExecutionStatus} for ${formatName(digitalTwin.DTName)} (Run #${digitalTwin.executionCount})`;
? `Execution started successfully for ${formatName(digitalTwin.DTName)}. Wait until completion for the logs...`
: `Execution ${digitalTwin.lastExecutionStatus} for ${formatName(digitalTwin.DTName)}`;
setSnackbarMessage(executionStatusMessage);
setSnackbarSeverity(
digitalTwin.lastExecutionStatus === 'success' ? 'success' : 'error',
Expand Down Expand Up @@ -107,10 +103,13 @@ export const fetchJobLogs = async (
job.id,
);
if (typeof log === 'string') {
// TODO: Fix ansi character stripping
// eslint-disable-next-line no-control-regex
log.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
'',)
log
.replace(
// TODO: Fix ansi character stripping
// eslint-disable-next-line no-control-regex
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
'',
)
.split('\n')
.map((line: string) =>
line
Expand Down
10 changes: 0 additions & 10 deletions client/src/store/digitalTwin.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,6 @@ const digitalTwinSlice = createSlice({
) => {
state[action.payload.assetName] = action.payload.digitalTwin;
},
incrementExecutionCount: (
state,
action: PayloadAction<{ assetName: string }>,
) => {
const digitalTwin = state[action.payload.assetName];
if (digitalTwin) {
digitalTwin.executionCount += 1;
}
},
setJobLogs: (
state,
action: PayloadAction<{ assetName: string; jobLogs: JobLog[] }>,
Expand Down Expand Up @@ -63,7 +54,6 @@ export const selectDigitalTwinByName = (name: string) => (state: RootState) =>

export const {
setDigitalTwin,
incrementExecutionCount,
setJobLogs,
setPipelineCompleted,
setPipelineLoading,
Expand Down
2 changes: 0 additions & 2 deletions client/src/util/gitlabDigitalTwin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ class DigitalTwin {

public lastExecutionStatus: string | null = null;

public executionCount: number = 0;

public jobLogs: { jobName: string; log: string }[] = [];

public pipelineLoading: boolean = false;
Expand Down
6 changes: 2 additions & 4 deletions client/test/unit/components/asset/StartStopButton.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useSelector, useDispatch } from 'react-redux';
import { RootState } from 'store/store';
import '@testing-library/jest-dom';

jest.mock('route/digitaltwins/ExecutionFunctions', () => ({
jest.mock('route/digitaltwins/pipelineHandler', () => ({
handleButtonClick: jest.fn(),
}));

Expand Down Expand Up @@ -54,7 +54,7 @@ describe('StartStopButton', () => {
expect(screen.getByText('Start')).toBeInTheDocument();
});

test('renders circular progress when pipelineLoading is true', async () => {
test('renders circular progress when pipelineLoading is true', () => {
(useSelector as jest.Mock).mockImplementationOnce((selector) =>
selector({
digitalTwin: {
Expand All @@ -74,7 +74,6 @@ describe('StartStopButton', () => {
);

expect(screen.getByTestId('circular-progress')).toBeInTheDocument();

expect(screen.getByText('Start')).toBeInTheDocument();
});

Expand All @@ -90,7 +89,6 @@ describe('StartStopButton', () => {
);

expect(screen.queryByTestId('circular-progress')).not.toBeInTheDocument();

expect(screen.getByText('Start')).toBeInTheDocument();
});

Expand Down
Loading

0 comments on commit 5ba4ebf

Please sign in to comment.