Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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(
Comment thread
msivasubramaniaan marked this conversation as resolved.
'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