Skip to content

Commit

Permalink
Introduce validation framework (#1041)
Browse files Browse the repository at this point in the history
* 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.

* Introduced validation framework and used it for create and copy project actions

---------

Co-authored-by: mvolf <[email protected]>
  • Loading branch information
mvolf and mvolf authored Jan 3, 2024
1 parent 7bf6e43 commit 37a0d29
Show file tree
Hide file tree
Showing 25 changed files with 863 additions and 178 deletions.
2 changes: 1 addition & 1 deletion karavan-space/package-lock.json

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

4 changes: 4 additions & 0 deletions karavan-web/karavan-app/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-scheduler</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
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;
Expand Down Expand Up @@ -82,17 +81,8 @@ public Project get(@PathParam("project") String project) throws Exception {
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
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();
}
public Project save(Project project) {
return projectService.save(project);
}

@DELETE
Expand Down Expand Up @@ -180,16 +170,7 @@ public Response getCamelTracesForProjectAndEnv(@PathParam("projectId") String pr
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/copy/{sourceProject}")
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();
}
public Project copy(@PathParam("sourceProject") String sourceProject, Project project) {
return projectService.copy(sourceProject, project);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

import java.time.Instant;

import jakarta.validation.constraints.NotBlank;

public class Project {
public static final String CACHE = "projects";

Expand All @@ -36,10 +38,13 @@ public enum Type {
}

@ProtoField(number = 1)
@NotBlank
String projectId;
@ProtoField(number = 2)
@NotBlank
String name;
@ProtoField(number = 3)
@NotBlank
String description;
@ProtoField(number = 4)
String lastCommit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
import org.apache.camel.karavan.registry.RegistryService;
import org.apache.camel.karavan.shared.Constants;
import org.apache.camel.karavan.shared.Property;
import org.apache.camel.karavan.shared.exception.ProjectExistsException;
import org.apache.camel.karavan.validation.project.ProjectModifyValidator;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.microprofile.config.inject.ConfigProperty;
Expand All @@ -62,6 +62,10 @@ public class ProjectService implements HealthCheck {
@ConfigProperty(name = "karavan.environment")
String environment;


@Inject
ProjectModifyValidator projectModifyValidator;

@Inject
InfinispanService infinispanService;

Expand Down Expand Up @@ -167,11 +171,8 @@ private String getImage(List<ProjectFile> files, String projectId) {
}
}

public Project save(Project project) throws Exception {
boolean projectExists = infinispanService.getProject(project.getProjectId()) != null;
if(projectExists) {
throw new ProjectExistsException("Project with project id [" + project.getProjectId() + "] already exists");
}
public Project save(Project project) {
projectModifyValidator.validate(project).failOnError();

infinispanService.saveProject(project);

Expand All @@ -188,11 +189,9 @@ public Project save(Project project) throws Exception {
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");
}
public Project copy(String sourceProjectId, Project project) {
projectModifyValidator.validate(project).failOnError();

Project sourceProject = infinispanService.getProject(sourceProjectId);

// Save project
Expand All @@ -208,7 +207,7 @@ public Project copy(String sourceProjectId, Project project) throws Exception {
e -> {
ProjectFile file = e.getValue();
file.setProjectId(project.getProjectId());
if(Objects.equals(file.getName(), APPLICATION_PROPERTIES_FILENAME)) {
if (Objects.equals(file.getName(), APPLICATION_PROPERTIES_FILENAME)) {
modifyPropertyFileOnProjectCopy(file, sourceProject, project);
}
return file;
Expand All @@ -219,7 +218,7 @@ public Project copy(String sourceProjectId, Project project) throws Exception {
if (!ConfigService.inKubernetes()) {
ProjectFile projectCompose = codeService.createInitialProjectCompose(project);
infinispanService.saveProjectFile(projectCompose);
} else if (kubernetesService.isOpenshift()){
} else if (kubernetesService.isOpenshift()) {
ProjectFile projectCompose = codeService.createInitialDeployment(project);
infinispanService.saveProjectFile(projectCompose);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.apache.camel.karavan.shared.error;

import java.util.Objects;

import com.fasterxml.jackson.annotation.JsonInclude;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class Error {
private String field;
private String message;
private String code;

public Error(String message) {
this.message = message;
}

public Error(String field, String message) {
this.field = field;
this.message = message;
}

public Error(String field, String message, String code) {
this.field = field;
this.message = message;
this.code = code;
}

public String getField() {
return field;
}

public void setField(String field) {
this.field = field;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Error error = (Error) o;
return Objects.equals(field, error.field) && Objects.equals(message, error.message)
&& Objects.equals(code, error.code);
}

@Override
public int hashCode() {
return Objects.hash(field, message, code);
}

@Override
public String toString() {
return "Error{" +
"field='" + field + '\'' +
", message='" + message + '\'' +
", code='" + code + '\'' +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.apache.camel.karavan.shared.error;

import java.util.Collection;
import java.util.Date;

import com.fasterxml.jackson.annotation.JsonInclude;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class ErrorResponse {
private final Date timestamp;
private final int status;
private final String error;
private final String message;
private Collection<Error> errors;

public ErrorResponse(
int httpStatus,
String reasonPhrase,
String message
) {
this.timestamp = new Date();
this.status = httpStatus;
this.error = reasonPhrase;
this.message = message;
}

public ErrorResponse(
int httpStatus,
String reasonPhrase,
String message,
Collection<Error> errors
) {
this.timestamp = new Date();
this.status = httpStatus;
this.error = reasonPhrase;
this.message = message;
this.errors = errors;
}

public Date getTimestamp() {
return timestamp;
}

public int getStatus() {
return status;
}

public String getError() {
return error;
}

public String getMessage() {
return message;
}

public Collection<Error> getErrors() {
return errors;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.apache.camel.karavan.shared.exception;

import java.util.Collection;
import java.util.List;

import org.apache.camel.karavan.shared.error.Error;
import org.apache.camel.karavan.shared.error.ErrorResponse;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.RestResponse;
import org.jboss.resteasy.reactive.server.ServerExceptionMapper;

import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

public class ExceptionToResponseMapper {
private static final Logger LOGGER = Logger.getLogger(ExceptionToResponseMapper.class.getName());

@ServerExceptionMapper
public RestResponse<Object> validationException(ValidationException exception) {
List<Error> errors = (exception).getErrors()
.stream()
.map(fieldError -> new Error(fieldError.getField(), fieldError.getMessage()))
.toList();

return logAndBuildResponse(
exception,
Response.Status.BAD_REQUEST.getStatusCode(),
Response.Status.BAD_REQUEST.getReasonPhrase(),
errors
);
}

private RestResponse<Object> logAndBuildResponse(
Throwable exception,
int status,
String reasonPhrase,
Collection<Error> errors
) {
LOGGER.error("Error occurred", exception);

String cause = (exception.getCause() != null) ? exception.getCause().getMessage() : null;
String message = (cause != null) ? exception.getMessage() + ", caused by: " + cause : exception.getMessage();

if (message == null) {
message = exception.getClass().toString();
}

// Hide errors array if there are no errors and leave just error message
if (errors != null && errors.isEmpty()) {
errors = null;
}

ErrorResponse responseBody = new ErrorResponse(
status,
reasonPhrase,
message,
errors
);

return RestResponse.ResponseBuilder
.create(status)
.entity(responseBody)
.type(MediaType.APPLICATION_JSON)
.build();
}
}

This file was deleted.

Loading

0 comments on commit 37a0d29

Please sign in to comment.