Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vdk-jupyter: introduce Task Runner, a polling mechanism that runs tasks in the background and tracks their status #2869

Merged
merged 39 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
17c967b
vdk-jupyter: introduce task runner class and update handlers
yonitoo Oct 24, 2023
b4d48a4
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Oct 27, 2023
d9174ce
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Oct 30, 2023
c4d9765
vdk-jupyter: adapt the polling mechanism to the frontend
yonitoo Oct 31, 2023
1bee478
vdk-jupyter: fix docstring of convert job
yonitoo Oct 31, 2023
3430f1c
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Oct 31, 2023
27de36a
vdk-jupyter: fix Task Runner API-related problems
yonitoo Nov 1, 2023
9e25e32
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 1, 2023
657fea0
vdk-jupyter:
yonitoo Nov 3, 2023
3ed8caa
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 3, 2023
23c167d
vdk-jupyter: remove Delete Job
yonitoo Nov 6, 2023
e50e46c
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 6, 2023
03bfb2a
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 7, 2023
56a6a69
vdk-jupyter: add Task Runner server tests
yonitoo Nov 7, 2023
d824f9e
vdk-jupyter: fix Convert Job To Notebook case
yonitoo Nov 8, 2023
7fe2234
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 8, 2023
ff67092
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 9, 2023
978aedd
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 9, 2023
8b6e5d2
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 9, 2023
4bb6b40
build(deps): Bump org.mockito:mockito-core
dependabot[bot] Nov 3, 2023
5913607
control-service: integration test for async job deploy (#2829)
Nov 9, 2023
01fa502
build(deps): Bump org.junit.jupiter:junit-jupiter-engine from 5.9.3 t…
dependabot[bot] Nov 10, 2023
a8d9cf6
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 13, 2023
eb00f46
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 13, 2023
5ad5401
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 13, 2023
a7b3c63
vdk-jupyter: add Task Runner docstring and address review comments
yonitoo Nov 13, 2023
3c6c81b
vdk-jupyter: add a comment about the polling default values
yonitoo Nov 13, 2023
a119d57
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 13, 2023
5642ff6
vdk-jupyter: modify failing tests accordingly
yonitoo Nov 14, 2023
3046f1b
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 14, 2023
b499fc9
vdk-jupyter: make task ids unique and remove polling error threshold
yonitoo Nov 14, 2023
945869e
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 14, 2023
26ffefa
vdk-jupyter: pin react-icons dependency version to 4.11.0
yonitoo Nov 14, 2023
0141c9b
vdk-jupyter: extract duplicating task start JSON response code to func
yonitoo Nov 14, 2023
30c07fa
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 14, 2023
1726d2b
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 15, 2023
5016250
vdk-jupyter: restructure a bit the task start request responses
yonitoo Nov 15, 2023
5260eca
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 15, 2023
428a3c3
Merge branch 'main' into topic/ysalambashev/vdk-jupyter-task-runner
yonitoo Nov 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
"@jupyterlab/ui-components": "3.6.3",
"@lumino/widgets": "1.33.0",
"@types/react": "17.0.53",
"react-icons": "^4.11.0",
"react-icons": "4.11.0",
"typescript": "4.1.3",
"word-wrap": "1.2.4",
"yjs": "^13.5.17"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,26 +70,64 @@ describe('jobRunRequest()', () => {
jest.clearAllMocks();
});

it('should call requestAPI with correct arguments and return successful result', async () => {
const expectedMessage = '0';
const expectedResponse = { message: '0', isSuccessful: true };
(requestAPI as jest.Mock).mockResolvedValue(expectedResponse);
it('should call requestAPI to start a task and then poll for its completion, returning a successful result', async () => {
const mockData = {
[VdkOption.PATH]: '/test/job/path'
};
jobData.set(VdkOption.PATH, mockData[VdkOption.PATH]);

const taskId = 'RUN-6266cd99-908c-480b-9a3e-8a30564736a4';
const taskInitiationResponse = {
error: '',
message: `Task ${taskId} started`
};
const taskCompletionResponse = {
task_id: taskId,
status: 'completed',
message: 'Operation completed successfully',
error: null
};

(requestAPI as jest.Mock)
.mockResolvedValueOnce(taskInitiationResponse)
.mockResolvedValue(taskCompletionResponse);

const result = await jobRunRequest();

expect(requestAPI).toHaveBeenCalledTimes(1);
expect(result).toEqual({ message: expectedMessage, isSuccessful: true });
expect(requestAPI).toHaveBeenCalledWith('run', {
body: JSON.stringify(getJobDataJsonObject()),
method: 'POST'
});
expect(requestAPI).toHaveBeenCalledWith(`taskStatus?taskId=${taskId}`, {
method: 'GET'
});
expect(result).toEqual({
message: taskCompletionResponse.message,
isSuccessful: true
});
});

it('should call requestAPI with correct arguments and return unsuccessful result', async () => {
const expectedError = new Error('1');
(requestAPI as jest.Mock).mockResolvedValue(expectedError);
const mockData = {
[VdkOption.PATH]: '/test/job/path'
};
jobData.set(VdkOption.PATH, mockData[VdkOption.PATH]);

const taskId = 'RUN-6266cd99-908c-480b-9a3e-8a30564736a4';
const taskInitiationResponse = {
error: '1',
message: `Task ${taskId} started`
};
(requestAPI as jest.Mock).mockResolvedValueOnce(taskInitiationResponse);

const result = await jobRunRequest();

expect(requestAPI).toHaveBeenCalledTimes(1);
expect(requestAPI).toHaveBeenCalledWith('run', {
body: JSON.stringify(getJobDataJsonObject()),
method: 'POST'
});
expect(result).toEqual({
message: '1',
message: taskInitiationResponse.message,
isSuccessful: false
});
});
Expand All @@ -101,32 +139,79 @@ describe('jobRequest()', () => {
});

it('should call requestAPI with the correct arguments', async () => {
const endPoint = 'your-endpoint-url';
const expectedRequestBody = JSON.stringify(getJobDataJsonObject());
const expectedRequestMethod = 'POST';
const mockData = {
[VdkOption.NAME]: 'Test Job',
[VdkOption.TEAM]: 'Test Team'
};

jobData.set(VdkOption.NAME, mockData[VdkOption.NAME]);
jobData.set(VdkOption.TEAM, mockData[VdkOption.TEAM]);

const endpoint = 'CREATE';
const taskId = endpoint + '-6266cd99-908c-480b-9a3e-8a30564736a4';
const taskInitiationResponse = {
error: '',
message: `Task ${taskId} started`
};
const taskCompletionResponse = {
task_id: taskId,
status: 'completed',
message: 'Task completed successfully',
error: null
};

(requestAPI as jest.Mock)
.mockResolvedValueOnce(taskInitiationResponse)
.mockResolvedValue(taskCompletionResponse);

await jobRequest(endPoint);
const result = await jobRequest(endpoint);

expect(requestAPI).toHaveBeenCalledWith(endPoint, {
body: expectedRequestBody,
method: expectedRequestMethod
// Verify the call for initiating the task
expect(requestAPI).toHaveBeenCalledWith(endpoint, {
body: JSON.stringify(getJobDataJsonObject()),
method: 'POST'
});

// Verify the polling for task status
expect(requestAPI).toHaveBeenCalledWith(`taskStatus?taskId=${taskId}`, {
method: 'GET'
});

// Verify the final result
expect(result).toEqual({
message: taskCompletionResponse.message,
isSuccessful: true
});
});

it('should show a success message if requestAPI returns a serverVdkOperationResult without errors', async () => {
const endPoint = 'your-endpoint-url';
it('should show an error message if requestAPI returns a serverVdkOperationResult with error', async () => {
const mockData = {
[VdkOption.NAME]: 'Test Job',
[VdkOption.TEAM]: 'Test Team'
};

jobData.set(VdkOption.NAME, mockData[VdkOption.NAME]);
jobData.set(VdkOption.TEAM, mockData[VdkOption.TEAM]);

const endpoint = 'DEPLOY';
const serverVdkOperationResultMock = {
error: '',
message: 'Operation completed successfully'
error: '1',
message: `${endpoint} task failed`
};
(requestAPI as jest.Mock).mockResolvedValue(serverVdkOperationResultMock);
(requestAPI as jest.Mock).mockResolvedValueOnce(
serverVdkOperationResultMock
);

await jobRequest(endPoint);
const result = await jobRequest(endpoint);

expect(requestAPI).toHaveBeenCalledWith(endPoint, {
expect(requestAPI).toHaveBeenCalledWith(endpoint, {
body: JSON.stringify(getJobDataJsonObject()),
method: 'POST'
});
expect(result).toEqual({
message: serverVdkOperationResultMock.message,
isSuccessful: false
});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,13 @@ export async function showConvertJobToNotebookDialog(
});
if (confirmation.button.accept) {
statusButton?.show('Convert', jobData.get(VdkOption.PATH)!);
let { message, status } = await jobConvertToNotebookRequest();
if (status) {
const transformjobResult = JSON.parse(message);
let { message, isSuccessful } = await jobConvertToNotebookRequest();
// if the job request is successful, it is not expected to return string but an object with code_structure and
// removed_files fields which will help us to create a notebook and populate it with the content
if (isSuccessful && typeof message !== 'string') {
notebookContent = initializeNotebookContent(
transformjobResult['code_structure'],
transformjobResult['removed_files']
message.code_structure,
message.removed_files
);
await createTranformedNotebook(commands, fileBrowser, notebookTracker);
await showDialog({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ export const findFailingCellInNotebookCells = async (
};

/**
* Seperate handling for notebook errors - option for the user to navigate to the failing cell when error is produced
* Separate handling for notebook errors - option for the user to navigate to the failing cell when error is produced
*/
export const handleErrorsProducedByNotebookCell = async (
message: VdkErrorMessage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ export function getJobDataJsonObject() {
export async function checkIfVdkOptionDataIsDefined(
option: VdkOption
): Promise<boolean> {
if (jobData.get(option)) return true;
else {
if (jobData.get(option)) {
return true;
} else {
await showErrorMessage(
'Encountered an error while trying to execute operation. Error:',
'The ' + option + ' should be defined!',
Expand Down
Loading