Skip to content

Commit

Permalink
add port number support (#3292)
Browse files Browse the repository at this point in the history
* add port number support

Signed-off-by: msivasubramaniaan <[email protected]>

* addressed review comments

Signed-off-by: msivasubramaniaan <[email protected]>

* fix lint errors

Signed-off-by: msivasubramaniaan <[email protected]>

* port number type changed

Signed-off-by: msivasubramaniaan <[email protected]>

* revert portnumber type change

Signed-off-by: msivasubramaniaan <[email protected]>

---------

Signed-off-by: msivasubramaniaan <[email protected]>
  • Loading branch information
msivasubramaniaan authored Sep 23, 2023
1 parent b1ef719 commit 760a1c0
Show file tree
Hide file tree
Showing 16 changed files with 391 additions and 227 deletions.
16 changes: 9 additions & 7 deletions src/odo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,19 +100,21 @@ export interface Odo {
*
* @param componentPath the folder in which to create the project
* @param componentName the name of the component
* @param portNumber the port number used in the component
* @param devfileName the name of the devfile to use
* @param registryName the name of the devfile registry that the devfile comes from
* @param templateProjectName the template project from the devfile to use
*/
createComponentFromTemplateProject(componentPath: string, componentName: string, devfileName: string, registryName: string, templateProjectName: string): Promise<void>;
createComponentFromTemplateProject(componentPath: string, componentName: string, portNumber: number, devfileName: string, registryName: string, templateProjectName: string): Promise<void>;
/**
* Create a component from the given local codebase.
*
* @param devfileName the name of the devfile to use
* @param componentName the name of the component
* @param portNumber the port number used in the component
* @param location the location of the local codebase
*/
createComponentFromLocation(devfileName: string, componentName: string, location: Uri): Promise<void>;
createComponentFromLocation(devfileName: string, componentName: string, portNumber: number, location: Uri): Promise<void>;
}

export class OdoImpl implements Odo {
Expand Down Expand Up @@ -242,7 +244,7 @@ export class OdoImpl implements Odo {
}

public async createComponentFromFolder(type: string, registryName: string, name: string, location: Uri, starter: string = undefined, useExistingDevfile = false, customDevfilePath = ''): Promise<void> {
await this.execute(Command.createLocalComponent(type, registryName, name, starter, useExistingDevfile, customDevfilePath), location.fsPath);
await this.execute(Command.createLocalComponent(type, registryName, name, undefined, starter, useExistingDevfile, customDevfilePath), location.fsPath);
let wsFolder: WorkspaceFolder;
if (workspace.workspaceFolders) {
// could be new or existing folder
Expand All @@ -253,12 +255,12 @@ export class OdoImpl implements Odo {
}
}

public async createComponentFromLocation(devfileName: string, componentName: string, location: Uri): Promise<void> {
await this.execute(Command.createLocalComponent(devfileName, undefined, componentName, undefined, false, ''), location.fsPath);
public async createComponentFromLocation(devfileName: string, componentName: string, portNumber: number, location: Uri): Promise<void> {
await this.execute(Command.createLocalComponent(devfileName, undefined, componentName, portNumber, undefined, false, ''), location.fsPath);
}

public async createComponentFromTemplateProject(componentPath: string, componentName: string, devfileName: string, registryName: string, templateProjectName: string): Promise<void> {
await this.execute(Command.createLocalComponent(devfileName, registryName, componentName, templateProjectName), componentPath);
public async createComponentFromTemplateProject(componentPath: string, componentName: string, portNumber: number, devfileName: string, registryName: string, templateProjectName: string): Promise<void> {
await this.execute(Command.createLocalComponent(devfileName, registryName, componentName, portNumber, templateProjectName), componentPath);
}

public async createService(formData: any): Promise<void> {
Expand Down
4 changes: 4 additions & 0 deletions src/odo/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ export class Command {
type = '', // will use empty string in case of undefined type passed in
registryName: string,
name: string,
portNumber: number,
starter: string = undefined,
useExistingDevfile = false,
customDevfilePath = '',
Expand Down Expand Up @@ -233,6 +234,9 @@ export class Command {
if (devfileVersion) {
cTxt.addOption(new CommandOption('--devfile-version', devfileVersion, false));
}
if (portNumber) {
cTxt.addOption(new CommandOption(' --run-port', portNumber.toString(), false));
}
return cTxt;
}

Expand Down
1 change: 1 addition & 0 deletions src/odo/componentType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export interface AnalyzeResponse {
devfile: string;
devfileRegistry: string;
devfileVersion: string;
ports: number[];
}

export type ComponentTypeDescription = DevfileComponentType & DevfileData;
Expand Down
4 changes: 2 additions & 2 deletions src/openshift/nameValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export function emptyName(message: string, value: string): string | null {
return validator.isEmpty(value) ? message : null;
}

export function lengthName(message: string, value: string, offset: number): string | null {
return validator.isLength(value, {min: 2, max: 63 - offset}) ? null : message;
export function lengthName(message: string, value: string, offset: number, minVal = 2, maxVal = 63): string | null {
return validator.isLength(value, { min: minVal, max: maxVal - offset }) ? null : message;
}

export function validateUrl(message: string, value: string): string | null {
Expand Down
29 changes: 28 additions & 1 deletion src/webview/common-ext/createComponentHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,33 @@ export function validateComponentName(name: string): string {
return validationMessage;
}

/**
* Returns the validation message if the component name is invalid, and undefined otherwise.
*
* @param name the port number to validate
* @returns the validation message if the component name is invalid, and undefined otherwise
*/
export function validatePortNumber(portNumber: number): string {
let validationMessage: string | null;
const port = portNumber.toString();
if (NameValidator.emptyName('Empty', port) === null) {
validationMessage = NameValidator.lengthName(
'Port number length should be between 1-5 digits',
port,
0,
1,
5
);

if (!validationMessage) {
if (portNumber < 1 || portNumber > 65535) {
validationMessage = 'Not a valid port number.'
}
}
}
return validationMessage;
}

/**
* Returns a list of the devfile registries with their devfiles attached.
*
Expand All @@ -130,7 +157,7 @@ export function getDevfileRegistries(): DevfileRegistry[] {
const components = ComponentTypesView.instance.getCompDescriptions();
for (const component of components) {
const devfileRegistry = devfileRegistries.find(
(devfileRegistry) => format(devfileRegistry.url) === format(component.registry.url),
(devfileRegistry) => format(devfileRegistry.url) === format(component.registry.url),
);

devfileRegistry.devfiles.push({
Expand Down
3 changes: 1 addition & 2 deletions src/webview/common/componentNameInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export function ComponentNameInput(props: ComponentNameInputProps) {
data: e.target.value
});
props.setComponentName(e.target.value);
}}
/>
}} />
);
}
8 changes: 5 additions & 3 deletions src/webview/common/createComponentButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ export type CreateComponentButtonProps = {
componentName: string;
componentParentFolder: string;
addToWorkspace: boolean;
portNumber: number;
isComponentNameFieldValid: boolean;
isPortNumberFieldValid: boolean;
isFolderFieldValid: boolean;
isLoading: boolean;
createComponent: (projectFolder: string, componentName: string, isAddToWorkspace: boolean) => void;
createComponent: (projectFolder: string, componentName: string, isAddToWorkspace: boolean, portNumber: number) => void;
setLoading: React.Dispatch<React.SetStateAction<boolean>>;
};

Expand All @@ -23,10 +25,10 @@ export function CreateComponentButton(props: CreateComponentButtonProps) {
<LoadingButton
variant="contained"
onClick={() => {
props.createComponent(props.componentParentFolder, props.componentName, props.addToWorkspace);
props.createComponent(props.componentParentFolder, props.componentName, props.addToWorkspace, props.portNumber);
props.setLoading(true);
}}
disabled={!props.isComponentNameFieldValid || !props.isFolderFieldValid || props.isLoading}
disabled={!props.isComponentNameFieldValid || !props.isPortNumberFieldValid || !props.isFolderFieldValid || props.isLoading}
loading={props.isLoading}
loadingPosition="start"
startIcon={<ConstructionIcon />}
Expand Down
1 change: 1 addition & 0 deletions src/webview/common/devfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { StarterProject } from '../../odo/componentTypeDescription';
export type Devfile = {
name: string;
id: string;
port: number;
registryName: string;
description: string;
logoUrl: string;
Expand Down
3 changes: 2 additions & 1 deletion src/webview/common/fromTemplateProject.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ export function FromTemplateProject(props: FromTemplateProjectProps) {
setCurrentPage((_) => 'setNameAndFolder');
}

function createComponent(projectFolder: string, componentName: string, addToWorkspace: boolean) {
function createComponent(projectFolder: string, componentName: string, addToWorkspace: boolean, portNumber: number) {
window.vscodeApi.postMessage({
action: 'createComponent',
data: {
templateProject: selectedTemplateProject,
projectFolder,
componentName,
portNumber,
isFromTemplateProject: true,
addToWorkspace
},
Expand Down
33 changes: 33 additions & 0 deletions src/webview/common/portNumberInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*-----------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/
import { TextField } from '@mui/material';
import * as React from 'react';

export type PortNumberInputProps = {
portNumber: number,
isPortNumberFieldValid: boolean,
portNumberErrorMessage: string,
setPortNumber: React.Dispatch<React.SetStateAction<number>>
};

export function PortNumberInput(props: PortNumberInputProps) {
return (
<TextField fullWidth
id='portnumber'
variant='outlined'
label='Port'
type='number'
value={props.portNumber}
error={!props.isPortNumberFieldValid}
helperText={props.portNumberErrorMessage}
onChange={(e) => {
window.vscodeApi.postMessage({
action: 'validatePortNumber',
data: e.target.value
});
props.setPortNumber(parseInt(e.target.value, 10));
}} />
);
}
39 changes: 37 additions & 2 deletions src/webview/common/setNameAndFolder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ComponentNameInput } from './componentNameInput';
import { CreateComponentButton, CreateComponentErrorAlert } from './createComponentButton';
import { Devfile } from './devfile';
import { DevfileListItem } from './devfileListItem';
import { PortNumberInput } from './portNumberInput';

type Message = {
action: string;
Expand All @@ -27,19 +28,23 @@ type Message = {

type SetNameAndFolderProps = {
goBack: () => void;
createComponent: (projectFolder: string, componentName: string, addToWorkspace: boolean) => void;
createComponent: (projectFolder: string, componentName: string, addToWorkspace: boolean, portNumber: number) => void;
devfile: Devfile;
templateProject?: string;
initialComponentName?: string;
};

export function SetNameAndFolder(props: SetNameAndFolderProps) {
const [componentName, setComponentName] = React.useState(props.initialComponentName);
const [portNumber, setPortNumber] = React.useState<number>(props.devfile.port);
const [isComponentNameFieldValid, setComponentNameFieldValid] = React.useState(true);
const [componentNameErrorMessage, setComponentNameErrorMessage] = React.useState(
'Please enter a component name.',
);

const [isPortNumberFieldValid, setPortNumberFieldValid] = React.useState(true);
const [portNumberErrorMessage, setPortNumberErrorMessage] = React.useState(
'Port number auto filled based on devfile selection',
);
const [componentParentFolder, setComponentParentFolder] = React.useState('');
const [isFolderFieldValid, setFolderFieldValid] = React.useState(false);
const [folderFieldErrorMessage, setFolderFieldErrorMessage] = React.useState('');
Expand Down Expand Up @@ -73,6 +78,16 @@ export function SetNameAndFolder(props: SetNameAndFolderProps) {
}
break;
}
case 'validatePortNumber': {
if (message.data) {
setPortNumberFieldValid(false);
setPortNumberErrorMessage(message.data);
} else {
setPortNumberFieldValid(true);
setPortNumberErrorMessage('');
}
break;
}
case 'createComponentFailed': {
setLoading(false);
setCreateComponentErrorMessage(message.data);
Expand Down Expand Up @@ -109,6 +124,15 @@ export function SetNameAndFolder(props: SetNameAndFolderProps) {
}
}, []);

React.useEffect(() => {
if (props.devfile.port) {
window.vscodeApi.postMessage({
action: 'validatePortNumber',
data: `${props.devfile.port}`,
});
}
}, []);

return (
<Stack direction="column" spacing={3}>
<div style={{ position: 'relative' }}>
Expand Down Expand Up @@ -138,6 +162,15 @@ export function SetNameAndFolder(props: SetNameAndFolderProps) {
componentName={componentName}
setComponentName={setComponentName}
/>
{
portNumber &&
<PortNumberInput
isPortNumberFieldValid={isPortNumberFieldValid}
portNumberErrorMessage={portNumberErrorMessage}
portNumber={portNumber}
setPortNumber={setPortNumber}
/>
}
<FormControl fullWidth>
<Stack direction="row" spacing={2}>
<TextField
Expand Down Expand Up @@ -195,7 +228,9 @@ export function SetNameAndFolder(props: SetNameAndFolderProps) {
componentName={componentName}
componentParentFolder={componentParentFolder}
addToWorkspace={isAddToWorkspace}
portNumber={portNumber}
isComponentNameFieldValid={isComponentNameFieldValid}
isPortNumberFieldValid={isPortNumberFieldValid}
isFolderFieldValid={isFolderFieldValid}
isLoading={isLoading}
createComponent={props.createComponent}
Expand Down
22 changes: 20 additions & 2 deletions src/webview/create-component/createComponentLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import {
getDevfileTags,
getDevfileRegistries,
isValidProjectFolder,
validateComponentName
validateComponentName,
validatePortNumber
} from '../common-ext/createComponentHelpers';
import { loadWebviewHtml, validateGitURL } from '../common-ext/utils';
import { Devfile, DevfileRegistry, TemplateProjectIdentifier } from '../common/devfile';
Expand Down Expand Up @@ -194,6 +195,17 @@ export default class CreateComponentLoader {
});
break;
}
/**
* The panel requested to validate the entered port number. Respond with error status and message.
*/
case 'validatePortNumber': {
const validationMessage = validatePortNumber(message.data);
void CreateComponentLoader.panel.webview.postMessage({
action: 'validatePortNumber',
data: validationMessage,
});
break;
}
/**
* The panel requested to select a project folder.
*/
Expand Down Expand Up @@ -279,6 +291,7 @@ export default class CreateComponentLoader {
*/
case 'createComponent': {
const componentName: string = message.data.componentName;
const portNumber: number = message.data.portNumber;
let componentFolder: string;
try {
if (message.data.isFromTemplateProject) {
Expand All @@ -291,6 +304,7 @@ export default class CreateComponentLoader {
await OdoImpl.Instance.createComponentFromTemplateProject(
componentFolder,
componentName,
portNumber,
templateProject.devfileId,
templateProject.registryName,
templateProject.templateProjectName,
Expand Down Expand Up @@ -324,6 +338,7 @@ export default class CreateComponentLoader {
await OdoImpl.Instance.createComponentFromLocation(
devfileType,
componentName,
portNumber,
Uri.file(componentFolder),
);
}
Expand Down Expand Up @@ -457,6 +472,9 @@ export default class CreateComponentLoader {
(devfile) => devfile.name === compDescriptions[0].displayName,
)
: undefined;
if (devfile) {
devfile.port = compDescriptions[0].devfileData.devfile.components[0].container?.endpoints[0].targetPort;
}
void CreateComponentLoader.panel.webview.postMessage({
action: 'recommendedDevfile',
data: {
Expand Down Expand Up @@ -567,4 +585,4 @@ function sendUpdatedTags() {
data: getDevfileTags(),
});
}
}
}
Loading

0 comments on commit 760a1c0

Please sign in to comment.