You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2023/12/08 15:17:52 UTC
(camel-karavan) branch main updated: #973 - Don't copy project if existing project-id is used (#992)
This is an automated email from the ASF dual-hosted git repository.
marat pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
The following commit(s) were added to refs/heads/main by this push:
new 0be5a10e #973 - Don't copy project if existing project-id is used (#992)
0be5a10e is described below
commit 0be5a10ed26c5dc2da6e3e14385c95961856a0f7
Author: Mario Volf <mv...@users.noreply.github.com>
AuthorDate: Fri Dec 8 16:17:47 2023 +0100
#973 - Don't copy project if existing project-id is used (#992)
* mvolf - web - Implement copy project functionality
* mvolf - web - Implement copy project functionality
* #973 - Copy project
* #973 - Copy project
* #973 - Don't copy project if existing project-id is used.
* #973 - Fixed code formatting
* #973 - Handle duplicate project id on project create and copy. Some additional small modifications and fixes.
---------
Co-authored-by: mvolf <ma...@lateral-thinking.co>
---
karavan-designer/src/designer/utils/CamelUi.tsx | 2 +-
karavan-space/src/designer/utils/CamelUi.tsx | 2 +-
.../apache/camel/karavan/api/ProjectResource.java | 27 ++++++++++++---
.../camel/karavan/service/ProjectService.java | 32 ++++++++++++------
.../shared/exception/ProjectExistsException.java | 7 ++++
.../src/main/webui/src/api/KaravanApi.tsx | 39 ++++++++++++++++------
.../src/main/webui/src/api/ProjectService.ts | 23 +++----------
.../src/main/webui/src/designer/utils/CamelUi.tsx | 2 +-
.../main/webui/src/projects/CreateProjectModal.tsx | 39 ++++++++++++++++------
.../main/webui/src/services/CreateServiceModal.tsx | 34 ++++++++++++++-----
.../webui/src/shared/error/ProjectExistsError.ts | 6 ++++
.../webui/src/templates/CreateProjectModal.tsx | 39 ++++++++++++++++------
.../src/main/webui/src/util/StringUtils.ts | 2 +-
13 files changed, 176 insertions(+), 78 deletions(-)
diff --git a/karavan-designer/src/designer/utils/CamelUi.tsx b/karavan-designer/src/designer/utils/CamelUi.tsx
index 318c729a..c9090e18 100644
--- a/karavan-designer/src/designer/utils/CamelUi.tsx
+++ b/karavan-designer/src/designer/utils/CamelUi.tsx
@@ -303,7 +303,7 @@ export class CamelUi {
}
static nameFromTitle = (title: string): string => {
- return title.replace(/[^a-z0-9+]+/gi, "-").toLowerCase();
+ return title.trim().replace(/[^a-z0-9+]+/gi, "-").toLowerCase();
}
static javaNameFromTitle = (title: string): string => {
diff --git a/karavan-space/src/designer/utils/CamelUi.tsx b/karavan-space/src/designer/utils/CamelUi.tsx
index 318c729a..c9090e18 100644
--- a/karavan-space/src/designer/utils/CamelUi.tsx
+++ b/karavan-space/src/designer/utils/CamelUi.tsx
@@ -303,7 +303,7 @@ export class CamelUi {
}
static nameFromTitle = (title: string): string => {
- return title.replace(/[^a-z0-9+]+/gi, "-").toLowerCase();
+ return title.trim().replace(/[^a-z0-9+]+/gi, "-").toLowerCase();
}
static javaNameFromTitle = (title: string): string => {
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java
index 52cfe36e..6acc57f5 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectResource.java
@@ -29,6 +29,7 @@ import org.apache.camel.karavan.infinispan.model.Project;
import org.apache.camel.karavan.kubernetes.KubernetesService;
import org.apache.camel.karavan.service.ConfigService;
import org.apache.camel.karavan.service.ProjectService;
+import org.apache.camel.karavan.shared.exception.ProjectExistsException;
import org.jboss.logging.Logger;
import java.net.URLDecoder;
@@ -71,8 +72,17 @@ public class ProjectResource {
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
- public Project save(Project project) throws Exception {
- return projectService.save(project);
+ public Response save(Project project) throws Exception {
+ try {
+ Project createdProject = projectService.save(project);
+ return Response.ok().entity(createdProject).build();
+ } catch (ProjectExistsException exception) {
+ LOGGER.error(exception.getMessage());
+ return Response.status(Response.Status.CONFLICT).entity(exception.getMessage()).build();
+ } catch (Exception exception) {
+ LOGGER.error(exception.getMessage());
+ return Response.serverError().entity(exception.getMessage()).build();
+ }
}
@DELETE
@@ -153,7 +163,16 @@ public class ProjectResource {
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/copy/{sourceProject}")
- public Project copy(@PathParam("sourceProject") String sourceProject, Project project) throws Exception {
- return projectService.copy(sourceProject, project);
+ public Response copy(@PathParam("sourceProject") String sourceProject, Project project) throws Exception {
+ try {
+ Project copiedProject = projectService.copy(sourceProject, project);
+ return Response.ok().entity(copiedProject).build();
+ } catch (ProjectExistsException exception) {
+ LOGGER.error(exception.getMessage());
+ return Response.status(Response.Status.CONFLICT).entity(exception.getMessage()).build();
+ } catch (Exception exception) {
+ LOGGER.error(exception.getMessage());
+ return Response.serverError().entity(exception.getMessage()).build();
+ }
}
}
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java
index eeeb6c08..3634fee3 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java
@@ -35,6 +35,7 @@ import org.apache.camel.karavan.infinispan.model.ProjectFile;
import org.apache.camel.karavan.kubernetes.KubernetesService;
import org.apache.camel.karavan.registry.RegistryService;
import org.apache.camel.karavan.shared.Property;
+import org.apache.camel.karavan.shared.exception.ProjectExistsException;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.microprofile.config.inject.ConfigProperty;
@@ -168,24 +169,33 @@ public class ProjectService implements HealthCheck {
}
public Project save(Project project) throws Exception {
- boolean isNew = infinispanService.getProject(project.getProjectId()) == null;
+ boolean projectExists = infinispanService.getProject(project.getProjectId()) != null;
+ if(projectExists) {
+ throw new ProjectExistsException("Project with project id [" + project.getProjectId() + "] already exists");
+ }
+
infinispanService.saveProject(project);
- if (isNew) {
- ProjectFile appProp = codeService.getApplicationProperties(project);
- infinispanService.saveProjectFile(appProp);
- if (!ConfigService.inKubernetes()) {
- ProjectFile projectCompose = codeService.createInitialProjectCompose(project);
- infinispanService.saveProjectFile(projectCompose);
- } else if (kubernetesService.isOpenshift()){
- ProjectFile projectCompose = codeService.createInitialDeployment(project);
- infinispanService.saveProjectFile(projectCompose);
- }
+
+ ProjectFile appProp = codeService.getApplicationProperties(project);
+ infinispanService.saveProjectFile(appProp);
+ if (!ConfigService.inKubernetes()) {
+ ProjectFile projectCompose = codeService.createInitialProjectCompose(project);
+ infinispanService.saveProjectFile(projectCompose);
+ } else if (kubernetesService.isOpenshift()){
+ ProjectFile projectCompose = codeService.createInitialDeployment(project);
+ infinispanService.saveProjectFile(projectCompose);
}
+
return project;
}
public Project copy(String sourceProjectId, Project project) throws Exception {
+ boolean projectExists = infinispanService.getProject(project.getProjectId()) != null;
+ if(projectExists) {
+ throw new ProjectExistsException("Project with project id [" + project.getProjectId() + "] already exists");
+ }
Project sourceProject = infinispanService.getProject(sourceProjectId);
+
// Save project
infinispanService.saveProject(project);
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/exception/ProjectExistsException.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/exception/ProjectExistsException.java
new file mode 100644
index 00000000..478028bb
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/exception/ProjectExistsException.java
@@ -0,0 +1,7 @@
+package org.apache.camel.karavan.shared.exception;
+
+public class ProjectExistsException extends RuntimeException {
+ public ProjectExistsException(String message) {
+ super(message);
+ }
+}
diff --git a/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx b/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx
index 00b3441f..5c01bd6f 100644
--- a/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-import axios, {AxiosResponse } from "axios";
+import axios, {AxiosResponse} from "axios";
import {
AppConfig,
CamelStatus,
@@ -28,6 +28,7 @@ import {Buffer} from 'buffer';
import {SsoApi} from "./SsoApi";
import {EventStreamContentType, fetchEventSource} from "@microsoft/fetch-event-source";
import {ProjectEventBus} from "./ProjectEventBus";
+import {ProjectExistsError} from "../shared/error/ProjectExistsError";
axios.defaults.headers.common['Accept'] = 'application/json';
axios.defaults.headers.common['Content-Type'] = 'application/json';
@@ -219,22 +220,38 @@ export class KaravanApi {
});
}
- static async postProject(project: Project, after: (res: AxiosResponse<any>) => void) {
- instance.post('/api/project', project)
+ static async postProject(project: Project): Promise<[Error | null, Project | null]> {
+ return instance.post('/api/project', project)
.then(res => {
- after(res);
+ if(res.status === 200 || res.status === 201) {
+ return [null, res.data as Project] as [null, Project]
+ } else {
+ return [Error("Error while creating project"), null] as [Error, null]
+ }
}).catch(err => {
- after(err);
- });
+ if(err.response?.status === 409) {
+ return [new ProjectExistsError("Project with id " + project.projectId + " already exists."), null] as [Error, null]
+ } else {
+ return [err as Error, null] as [Error, null];
+ }
+ });
}
- static async copyProject(sourceProject: string, project: Project, after: (res: AxiosResponse<any>) => void) {
- instance.post('/api/project/copy/' + sourceProject, project)
+ static async copyProject(sourceProject: string, project: Project): Promise<[Error | null, Project | null]> {
+ return instance.post('/api/project/copy/' + sourceProject, project)
.then(res => {
- after(res);
+ if(res.status === 200 || res.status === 201) {
+ return [null, res.data as Project] as [null, Project]
+ } else {
+ return [Error("Error while copying project"), null] as [Error, null]
+ }
}).catch(err => {
- after(err);
- });
+ if(err.response?.status === 409) {
+ return [new ProjectExistsError("Project with id " + project.projectId + " already exists."), null] as [Error, null]
+ } else {
+ return [err as Error, null] as [Error, null];
+ }
+ });
}
static async deleteProject(project: Project, after: (res: AxiosResponse<any>) => void) {
diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
index 66f9c88f..9f64e7f0 100644
--- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
@@ -236,27 +236,12 @@ export class ProjectService {
});
}
- public static createProject(project: Project) {
- KaravanApi.postProject(project, res => {
- if (res.status === 200 || res.status === 201) {
- ProjectService.refreshProjectData(project.projectId);
- ProjectService.refreshProjects();
- // this.props.toast?.call(this, 'Success', 'Project created', 'success');
- } else {
- // this.props.toast?.call(this, 'Error', res.status + ', ' + res.statusText, 'danger');
- }
- });
+ public static async createProject(project: Project) {
+ return KaravanApi.postProject(project);
}
- public static copyProject(sourceProject: string, project: Project) {
- KaravanApi.copyProject(sourceProject, project, res => {
- if (res.status === 200 || res.status === 201) {
- EventBus.sendAlert( 'Success', 'Project copied', 'success');
- ProjectService.refreshProjects();
- } else {
- EventBus.sendAlert( 'Warning', 'Error when copying project:' + res.statusText, 'warning');
- }
- });
+ public static async copyProject(sourceProject: string, project: Project) {
+ return KaravanApi.copyProject(sourceProject, project);
}
public static createFile(file: ProjectFile) {
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx
index 318c729a..c9090e18 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx
@@ -303,7 +303,7 @@ export class CamelUi {
}
static nameFromTitle = (title: string): string => {
- return title.replace(/[^a-z0-9+]+/gi, "-").toLowerCase();
+ return title.trim().replace(/[^a-z0-9+]+/gi, "-").toLowerCase();
}
static javaNameFromTitle = (title: string): string => {
diff --git a/karavan-web/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx b/karavan-web/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx
index d35b9fdc..b92a2218 100644
--- a/karavan-web/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx
@@ -19,7 +19,7 @@ import React, {useState} from 'react';
import {
Button, Form, FormGroup, FormHelperText, HelperText, HelperTextItem,
Modal,
- ModalVariant, TextInput
+ ModalVariant, TextInput, Text
} from '@patternfly/react-core';
import '../designer/karavan.css';
import {useProjectStore} from "../api/ProjectStore";
@@ -28,6 +28,8 @@ import {Project} from "../api/ProjectModels";
import {CamelUi} from "../designer/utils/CamelUi";
import {shallow} from "zustand/shallow";
import {isEmpty} from "../util/StringUtils";
+import {EventBus} from "../designer/utils/EventBus";
+import {ProjectExistsError} from "../shared/error/ProjectExistsError";
export function CreateProjectModal () {
@@ -35,6 +37,7 @@ export function CreateProjectModal () {
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const [projectId, setProjectId] = useState('');
+ const [isValidationError, setIsValidationError] = useState(false);
function cleanValues() {
setName("");
@@ -47,17 +50,30 @@ export function CreateProjectModal () {
cleanValues();
}
- function confirmAndCloseModal() {
- operation !== 'copy' ?
- ProjectService.createProject(new Project({name: name, description: description, projectId: projectId})) :
- ProjectService.copyProject(project?.projectId, new Project({name: name, description: description, projectId: projectId}));
- setOperation('none');
- cleanValues();
+ async function handleFormSubmit() {
+ setIsValidationError(false);
+ const [ err, createdProject ] = operation !== 'copy' ?
+ await ProjectService.createProject(new Project({name: name, description: description, projectId: projectId})) :
+ await ProjectService.copyProject(project?.projectId, new Project({name: name, description: description, projectId: projectId}));
+
+ if (createdProject !== null) {
+ EventBus.sendAlert( 'Success', 'Project created', 'success');
+ ProjectService.refreshProjectData(project.projectId);
+ ProjectService.refreshProjects();
+ setOperation('none');
+ cleanValues();
+ } else if (err !== null && err instanceof ProjectExistsError) {
+ setIsValidationError(true);
+ } else {
+ operation !== 'copy' ?
+ EventBus.sendAlert( 'Warning', 'Error when creating project:' + err?.message, 'warning') :
+ EventBus.sendAlert( 'Warning', 'Error when copying project:' + err?.message, 'warning');
+ }
}
function onKeyDown(event: React.KeyboardEvent<HTMLDivElement>): void {
if (event.key === 'Enter' && !isEmpty(name) && !isEmpty(description) && !isEmpty(projectId)) {
- confirmAndCloseModal();
+ handleFormSubmit();
}
}
@@ -71,7 +87,7 @@ export function CreateProjectModal () {
onKeyDown={onKeyDown}
actions={[
<Button key="confirm" variant="primary" isDisabled={!isReady}
- onClick={confirmAndCloseModal}>Save</Button>,
+ onClick={handleFormSubmit}>Save</Button>,
<Button key="cancel" variant="secondary" onClick={closeModal}>Cancel</Button>
]}
className="new-project"
@@ -91,7 +107,10 @@ export function CreateProjectModal () {
<TextInput className="text-field" type="text" id="projectId" name="projectId"
value={projectId}
onFocus={e => setProjectId(projectId === '' ? CamelUi.nameFromTitle(name) : projectId)}
- onChange={(_, e) => setProjectId(CamelUi.nameFromTitle(e))}/>
+ onChange={(_, e) => setProjectId(CamelUi.nameFromTitle(e))}
+ validated={isValidationError ? 'error' : 'default'}
+ />
+ {isValidationError && <Text style={{ color: 'red', fontStyle: 'italic'}}>Project ID must be unique</Text>}
<FormHelperText>
<HelperText>
<HelperTextItem>Unique project name</HelperTextItem>
diff --git a/karavan-web/karavan-app/src/main/webui/src/services/CreateServiceModal.tsx b/karavan-web/karavan-app/src/main/webui/src/services/CreateServiceModal.tsx
index 80f34c5e..38977138 100644
--- a/karavan-web/karavan-app/src/main/webui/src/services/CreateServiceModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/services/CreateServiceModal.tsx
@@ -19,14 +19,15 @@ import React, {useState} from 'react';
import {
Button, Form, FormGroup, FormHelperText, HelperText, HelperTextItem,
Modal,
- ModalVariant, Radio, TextInput,
+ ModalVariant, TextInput, Text
} from '@patternfly/react-core';
import '../designer/karavan.css';
-import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
import {useAppConfigStore, useProjectStore} from "../api/ProjectStore";
import {ProjectService} from "../api/ProjectService";
import {Project} from "../api/ProjectModels";
import {CamelUi} from "../designer/utils/CamelUi";
+import {EventBus} from "../designer/utils/EventBus";
+import {ProjectExistsError} from "../shared/error/ProjectExistsError";
export function CreateServiceModal () {
@@ -37,6 +38,7 @@ export function CreateServiceModal () {
const [runtime, setRuntime] = useState('');
const [projectId, setProjectId] = useState('');
const {config} = useAppConfigStore();
+ const [isValidationError, setIsValidationError] = useState(false);
function cleanValues() {
setName("");
@@ -50,15 +52,26 @@ export function CreateServiceModal () {
cleanValues();
}
- function confirmAndCloseModal () {
- ProjectService.createProject(new Project({name: name, description: description, projectId: projectId}));
- useProjectStore.setState({operation: "none"});
- cleanValues();
+ async function handleFormSubmit () {
+ setIsValidationError(false);
+ const [ err, createdProject ] = await ProjectService.createProject(new Project({name: name, description: description, projectId: projectId}));
+
+ if (createdProject !== null) {
+ EventBus.sendAlert( 'Success', 'Project created', 'success');
+ ProjectService.refreshProjectData(project.projectId);
+ ProjectService.refreshProjects();
+ useProjectStore.setState({operation: "none"});
+ cleanValues();
+ } else if (err !== null && err instanceof ProjectExistsError) {
+ setIsValidationError(true);
+ } else {
+ EventBus.sendAlert( 'Warning', 'Error when creating project:' + err?.message, 'warning');
+ }
}
function onKeyDown (event: React.KeyboardEvent<HTMLDivElement>): void {
if (event.key === 'Enter' && name !== undefined && description !== undefined && projectId !== undefined) {
- confirmAndCloseModal();
+ handleFormSubmit();
}
}
@@ -72,7 +85,7 @@ export function CreateServiceModal () {
onKeyDown={onKeyDown}
actions={[
<Button key="confirm" variant="primary" isDisabled={!isReady}
- onClick={confirmAndCloseModal}>Save</Button>,
+ onClick={handleFormSubmit}>Save</Button>,
<Button key="cancel" variant="secondary" onClick={closeModal}>Cancel</Button>
]}
className="new-project"
@@ -92,7 +105,10 @@ export function CreateServiceModal () {
<TextInput className="text-field" type="text" id="projectId" name="projectId"
value={projectId}
onFocus={e => setProjectId(projectId === '' ? CamelUi.nameFromTitle(name) : projectId)}
- onChange={(_, e) => setProjectId(CamelUi.nameFromTitle(e))}/>
+ onChange={(_, e) => setProjectId(CamelUi.nameFromTitle(e))}
+ validated={isValidationError ? 'error' : 'default'}
+ />
+ {isValidationError && <Text style={{ color: 'red', fontStyle: 'italic'}}>Project ID must be unique</Text>}
<FormHelperText>
<HelperText>
<HelperTextItem>Unique service name</HelperTextItem>
diff --git a/karavan-web/karavan-app/src/main/webui/src/shared/error/ProjectExistsError.ts b/karavan-web/karavan-app/src/main/webui/src/shared/error/ProjectExistsError.ts
new file mode 100644
index 00000000..6adee70b
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/webui/src/shared/error/ProjectExistsError.ts
@@ -0,0 +1,6 @@
+export class ProjectExistsError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'ProjectExistsError';
+ }
+}
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/templates/CreateProjectModal.tsx b/karavan-web/karavan-app/src/main/webui/src/templates/CreateProjectModal.tsx
index 09850a90..b6300be9 100644
--- a/karavan-web/karavan-app/src/main/webui/src/templates/CreateProjectModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/templates/CreateProjectModal.tsx
@@ -19,13 +19,15 @@ import React, {useState} from 'react';
import {
Button, Form, FormGroup, FormHelperText, HelperText, HelperTextItem,
Modal,
- ModalVariant, TextInput
+ ModalVariant, TextInput, Text
} from '@patternfly/react-core';
import '../designer/karavan.css';
import {useProjectStore} from "../api/ProjectStore";
import {ProjectService} from "../api/ProjectService";
import {Project} from "../api/ProjectModels";
import {CamelUi} from "../designer/utils/CamelUi";
+import {EventBus} from "../designer/utils/EventBus";
+import {ProjectExistsError} from "../shared/error/ProjectExistsError";
export function CreateProjectModal () {
@@ -34,6 +36,7 @@ export function CreateProjectModal () {
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const [projectId, setProjectId] = useState('');
+ const [isValidationError, setIsValidationError] = useState(false);
function cleanValues() {
setName("");
@@ -46,17 +49,30 @@ export function CreateProjectModal () {
cleanValues();
}
- function confirmAndCloseModal() {
- operation !== 'copy' ?
- ProjectService.createProject(new Project({name: name, description: description, projectId: projectId})) :
- ProjectService.copyProject(project?.projectId, new Project({name: name, description: description, projectId: projectId}));
- useProjectStore.setState({operation: "none"});
- cleanValues();
+ async function handleFormSubmit() {
+ setIsValidationError(false);
+ const [ err, createdProject ] = operation !== 'copy' ?
+ await ProjectService.createProject(new Project({name: name, description: description, projectId: projectId})) :
+ await ProjectService.copyProject(project?.projectId, new Project({name: name, description: description, projectId: projectId}));
+
+ if (createdProject !== null) {
+ EventBus.sendAlert( 'Success', 'Project created', 'success');
+ ProjectService.refreshProjectData(project.projectId);
+ ProjectService.refreshProjects();
+ useProjectStore.setState({operation: "none"});
+ cleanValues();
+ } else if (err !== null && err instanceof ProjectExistsError) {
+ setIsValidationError(true);
+ } else {
+ operation !== 'copy' ?
+ EventBus.sendAlert( 'Warning', 'Error when creating project:' + err?.message, 'warning') :
+ EventBus.sendAlert( 'Warning', 'Error when copying project:' + err?.message, 'warning');
+ }
}
function onKeyDown(event: React.KeyboardEvent<HTMLDivElement>): void {
if (event.key === 'Enter' && name !== undefined && description !== undefined && projectId !== undefined) {
- confirmAndCloseModal();
+ handleFormSubmit();
}
}
@@ -70,7 +86,7 @@ export function CreateProjectModal () {
onKeyDown={onKeyDown}
actions={[
<Button key="confirm" variant="primary" isDisabled={!isReady}
- onClick={confirmAndCloseModal}>Save</Button>,
+ onClick={handleFormSubmit}>Save</Button>,
<Button key="cancel" variant="secondary" onClick={closeModal}>Cancel</Button>
]}
className="new-project"
@@ -90,7 +106,10 @@ export function CreateProjectModal () {
<TextInput className="text-field" type="text" id="projectId" name="projectId"
value={projectId}
onFocus={e => setProjectId(projectId === '' ? CamelUi.nameFromTitle(name) : projectId)}
- onChange={(_, e) => setProjectId(CamelUi.nameFromTitle(e))}/>
+ onChange={(_, e) => setProjectId(CamelUi.nameFromTitle(e))}
+ validated={isValidationError ? 'error' : 'default'}
+ />
+ {isValidationError && <Text style={{ color: 'red', fontStyle: 'italic'}}>Project ID must be unique</Text>}
<FormHelperText>
<HelperText>
<HelperTextItem>Unique project name</HelperTextItem>
diff --git a/karavan-web/karavan-app/src/main/webui/src/util/StringUtils.ts b/karavan-web/karavan-app/src/main/webui/src/util/StringUtils.ts
index 5bf90a49..407778f1 100644
--- a/karavan-web/karavan-app/src/main/webui/src/util/StringUtils.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/util/StringUtils.ts
@@ -1,3 +1,3 @@
export function isEmpty(str: string) {
- return str === null || str === undefined || str.trim() === '';
+ return !str?.trim();
}
\ No newline at end of file