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: add Convert Job To Notebook UI button #2329

Merged
merged 21 commits into from
Jun 29, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d2115eb
vdk-jupyter: add Transform Job UI component
yonitoo Jun 21, 2023
38f116d
Merge branch 'main' into topic/ysalambashev/jupyter-add-transform-job…
yonitoo Jun 26, 2023
0690a89
Merge branch 'main' into topic/ysalambashev/jupyter-add-transform-job…
yonitoo Jun 26, 2023
061ca60
vdk-jupyter: add API request for the transform job
yonitoo Jun 26, 2023
4011094
Merge branch 'main' into topic/ysalambashev/jupyter-add-transform-job…
yonitoo Jun 26, 2023
ed7bc06
Merge branch 'main' into topic/ysalambashev/jupyter-add-transform-job…
yonitoo Jun 27, 2023
dc3d802
vdk-jupyter: add jest tests
yonitoo Jun 27, 2023
97ac5bf
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 27, 2023
ce7a82e
build(deps): Bump io.swagger.core.v3:swagger-models from 2.2.12 to 2.…
dependabot[bot] Jun 27, 2023
401386c
vdk-core: add vdk sdk secrets api - part III (#2325)
Jun 27, 2023
b9e2b27
vdk-jupyter: address review comments
yonitoo Jun 27, 2023
b3f456f
Merge branch 'main' into topic/ysalambashev/jupyter-add-transform-job…
yonitoo Jun 27, 2023
62674a1
vdk-jupyter: remove job and team name
yonitoo Jun 27, 2023
49c0e6a
vdk-jupyter: add confirmation dialog upon transform job operation
yonitoo Jun 27, 2023
3c1c922
vdk-jupyter: extract repeated strings into constants
yonitoo Jun 28, 2023
252fdee
Merge branch 'main' into topic/ysalambashev/jupyter-add-transform-job…
yonitoo Jun 28, 2023
33d6e6e
vdk-jupyter: fix lint issues
yonitoo Jun 28, 2023
913ddef
change formatting in utils.ts
duyguHsnHsn Jun 28, 2023
18d718d
vdk-jupyter: rename to transform job to notebook
yonitoo Jun 28, 2023
a7a81c8
vdk-jupyter: rename to convert
yonitoo Jun 29, 2023
b4360c9
Merge branch 'main' into topic/ysalambashev/jupyter-add-transform-job…
yonitoo Jun 29, 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
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
{
"command": "jp-vdk:menu-download"
},
{
"command": "jp-vdk:menu-transform"
},
{
"command": "jp-vdk:menu-create-deployment"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2023-2023 VMware, Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import TransformJobDialog from '../components/TransformJob';
import { render, fireEvent } from '@testing-library/react';
import { jobData } from '../jobData';
import { VdkOption } from '../vdkOptions/vdk_options';
import { IJobNameAndTeamProps, IJobPathProp } from '../components/props';

const defaultProps: IJobNameAndTeamProps & IJobPathProp = {
jobName: 'test-name',
jobTeam: 'test-team',
jobPath: 'test-path'
};

// created with the expectation to compare a rendered value with expected value parsed from config.ini
// yet to be implemented
describe('#render()', () => {
it('should return contain job name input with placeholder equal to jobName from props', () => {
const component = render(new TransformJobDialog(defaultProps).render());
const input = component.getByPlaceholderText(defaultProps.jobName);
expect(input).toBe(component.getAllByLabelText('Job Name:')[0]);
});

it('should return contain job team input with placeholder equal to jobTeam from props', () => {
const component = render(new TransformJobDialog(defaultProps).render());
const input = component.getByPlaceholderText(defaultProps.jobTeam);
expect(input).toBe(component.getAllByLabelText('Job Team:')[0]);
});

it('should return contain job path input with placeholder equal to jobPath from props', () => {
const component = render(new TransformJobDialog(defaultProps).render());
const input = component.getByPlaceholderText(defaultProps.jobPath);
expect(input).toBe(
component.getAllByLabelText('Path to job directory:')[0]
);
});
});

describe('#onNameChange', () => {
it('should change the job name in jobData', () => {
const component = render(new TransformJobDialog(defaultProps).render());
const input = component.getByPlaceholderText(defaultProps.jobName);
fireEvent.change(input, { target: { value: 'second-name' } });
expect(jobData.get(VdkOption.NAME)).toEqual('second-name');
});
});

describe('#onTeamChange', () => {
it('should change the job team in jobData', () => {
const component = render(new TransformJobDialog(defaultProps).render());
const input = component.getByPlaceholderText(defaultProps.jobTeam);
fireEvent.change(input, { target: { value: 'second-team' } });
expect(jobData.get(VdkOption.TEAM)).toEqual('second-team');
});
});

describe('#onPathChange', () => {
it('should change the path in jobData', () => {
const component = render(new TransformJobDialog(defaultProps).render());
const input = component.getByPlaceholderText(defaultProps.jobPath);
fireEvent.change(input, { target: { value: 'other/path' } });
expect(jobData.get(VdkOption.PATH)).toEqual('other/path');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { jobData, setJobDataToDefault } from './jobData';
import { showCreateDeploymentDialog } from './components/DeployJob';
import { showCreateJobDialog } from './components/CreateJob';
import { showDownloadJobDialog } from './components/DownloadJob';
import { showTransformJobDialog } from './components/TransformJob';
import { showDeleteJobDialog } from './components/DeleteJob';
import { jobdDataRequest } from './serverRequests';
import { VdkOption } from './vdkOptions/vdk_options';
Expand All @@ -26,6 +27,9 @@ export function updateVDKMenu(commands: CommandRegistry, docManager: IDocumentMa
// Add Download job command
add_command(commands, 'jp-vdk:menu-download','Download','Execute VDK Download Command', showDownloadJobDialog);

// Add Transform job command
add_command(commands, 'jp-vdk:menu-transform','Transform','Execute VDK Transform Command', showTransformJobDialog);

// Add Create Deployment command
add_command(commands, 'jp-vdk:menu-create-deployment','Deploy','Create deployment of a VDK job', showCreateDeploymentDialog);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React, { Component } from 'react';
import { jobData } from '../jobData';
import { VdkOption } from '../vdkOptions/vdk_options';
import VDKTextInput from './VdkTextInput';
import { Dialog, showDialog } from '@jupyterlab/apputils';
import { jobTransformRequest } from '../serverRequests';
import { IJobPathProp } from './props';
import { VdkErrorMessage } from './VdkErrorMessage';

export default class TransformJobDialog extends Component<IJobPathProp> {
/**
* Returns a React component for rendering a transform menu.
*
* @param props - component properties
* @returns React component
*/
constructor(props: IJobPathProp) {
super(props);
}
/**
* Renders a dialog for transforming a data job.
*
* @returns React element
*/
render(): React.ReactElement {
return (
<>
<VDKTextInput
option={VdkOption.NAME}
value="default-name"
label="Job Name:"
></VDKTextInput>
<VDKTextInput
option={VdkOption.TEAM}
value="default-team"
label="Job Team:"
></VDKTextInput>
<VDKTextInput
option={VdkOption.PATH}
value={this.props.jobPath}
label="Path to job directory:"
></VDKTextInput>
</>
);
}
}

export async function showTransformJobDialog() {
const result = await showDialog({
title: 'Transform Job',
body: (
<TransformJobDialog
jobPath={jobData.get(VdkOption.PATH)!}
></TransformJobDialog>
),
buttons: [Dialog.okButton(), Dialog.cancelButton()]
});
if (result.button.accept) {
let { message, status } = await jobTransformRequest();
if (status) {
showDialog({
title: 'Transform Job',
body: (
<div className="vdk-transform-dialog-message-container">
<p className="vdk-transform-dialog-message">
The job was transformed successfully!
</p>
</div>
),
buttons: [Dialog.okButton()]
});
} else {
message = 'ERROR : ' + message;
const errorMessage = new VdkErrorMessage(message);
showDialog({
title: 'Transform Job',
body: (
<div className="vdk-transform-error-message ">
<p>{errorMessage.exception_message}</p>
<p>{errorMessage.what_happened}</p>
<p>{errorMessage.why_it_happened}</p>
<p>{errorMessage.consequences}</p>
<p>{errorMessage.countermeasures}</p>
</div>
),
buttons: [Dialog.okButton()]
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,30 @@ export async function jobRequest(endPoint: string): Promise<void> {
}
}

export async function jobTransformRequest(): Promise<{
message: String;
status: boolean;
}> {
if (
(await checkIfVdkOptionDataIsDefined(VdkOption.NAME)) &&
(await checkIfVdkOptionDataIsDefined(VdkOption.TEAM)) &&
(await checkIfVdkOptionDataIsDefined(VdkOption.PATH))
) {
try {
const data = await requestAPI<serverVdkOperationResult>('transform', {
body: JSON.stringify(getJobDataJsonObject()),
method: 'POST'
});
return { message: data['message'], status: data['message'] == '0' };
} catch (error) {
showError(error);
return { message: '', status: false };
}
} else {
return { message: '', status: false };
}
}

/**
* Sent a POST request to the server to get information about the data job of current directory
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,29 @@ def post(self):
self.finish(json.dumps({"message": f"{e}", "error": "true"}))


class TransformJobHandler(APIHandler):
"""
Class responsible for handling POST request for transforming a Data Job given its name, team,
Rest API URL, and the path to the data job's directory
Response: return a json formatted str including:
::error field with error message if an error exists
::message field with status of the Vdk operation
"""

@tornado.web.authenticated
def post(self):
input_data = self.get_json_body()
try:
status = VdkUI.transform_job(
input_data[VdkOption.NAME.value],
input_data[VdkOption.TEAM.value],
input_data[VdkOption.PATH.value],
)
self.finish(json.dumps({"message": f"{status}", "error": ""}))
except Exception as e:
self.finish(json.dumps({"message": f"{e}", "error": "true"}))


class CreateJobHandler(APIHandler):
"""
Class responsible for handling POST request for creating a Data Job given its name, team,
Expand Down Expand Up @@ -178,6 +201,7 @@ def add_handler(handler, endpoint):
add_handler(RunJobHandler, "run")
add_handler(DeleteJobHandler, "delete")
add_handler(DownloadJobHandler, "download")
add_handler(TransformJobHandler, "transform")
add_handler(CreateJobHandler, "create")
add_handler(LoadJobDataHandler, "job")
add_handler(CreateDeploymentHandler, "deploy")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,19 @@ def download_job(name: str, team: str, path: str):
cmd.download(team, name, path)
return f"Downloaded the job with name {name} to {path}. "

@staticmethod
def transform_job(name: str, team: str, path: str):
"""
Execute `transform job`.
:param name: the name of the data job that will be transformed
:param team: the team of the data job that will be transformed
:param path: the path to the data job's directory
:return: message that the job is transformed
"""
cmd = JobDownloadSource(RestApiUrlConfiguration.get_rest_api_url())
cmd.transform(team, name, path)
return f"Transformed the job with name {name} from {team} team. "

@staticmethod
def create_job(name: str, team: str, path: str, local: bool, cloud: bool):
"""
Expand Down