From ca69d0c3646e54e6517381b08df64abbc12b87b0 Mon Sep 17 00:00:00 2001 From: zZHorizonZz Date: Thu, 3 Aug 2023 14:06:29 +0200 Subject: [PATCH] enhancements(app): added and fixes methods related to project app creation and updating --- .../dev/shiperist/exception/ErrorMessage.java | 5 +++ .../project/ProjectAppRepository.java | 5 +++ .../resource/project/ProjectAppResource.java | 42 ++++++++++++++----- .../service/project/ProjectAppService.java | 13 ++++-- .../app/BaseProjectAppResourceTest.java | 9 +++- .../dev/shiperist/app/CreateEndpointTest.java | 37 +++++++++++++++- 6 files changed, 93 insertions(+), 18 deletions(-) diff --git a/server/src/main/java/dev/shiperist/exception/ErrorMessage.java b/server/src/main/java/dev/shiperist/exception/ErrorMessage.java index 153927e..a0f9cd0 100644 --- a/server/src/main/java/dev/shiperist/exception/ErrorMessage.java +++ b/server/src/main/java/dev/shiperist/exception/ErrorMessage.java @@ -12,7 +12,12 @@ public class ErrorMessage { public static final ErrorMessage INVALID_GRANT_TYPE = new ErrorMessage("grant_type", "Invalid grant type"); public static final ErrorMessage INVALID_REFRESH_TOKEN = new ErrorMessage("refresh_token", "Invalid refresh token"); public static final ErrorMessage PROJECT_ALREADY_EXISTS = new ErrorMessage("project_name", "Project already exists"); + public static final ErrorMessage PROJECT_NOT_MEMBER = new ErrorMessage("project_id", "You are not a member of this project"); public static final ErrorMessage PROJECT_NOT_FOUND = new ErrorMessage("project_id", "Project not found"); + public static final ErrorMessage PROJECT_APP_ALREADY_EXISTS = new ErrorMessage("project_app_name", "Project app already exists"); + public static final ErrorMessage PROJECT_APP_NOT_FOUND = new ErrorMessage("project_app_id", "Project app not found"); + public static final ErrorMessage PROJECT_APP_RELEASE_NOT_FOUND = new ErrorMessage("project_app_release_id", "Project app release not found"); + public static final ErrorMessage PROJECT_APP_UPLOAD_NOT_FOUND = new ErrorMessage("project_app_upload_id", "Project app upload not found"); private final String error; private final String description; diff --git a/server/src/main/java/dev/shiperist/repository/project/ProjectAppRepository.java b/server/src/main/java/dev/shiperist/repository/project/ProjectAppRepository.java index 8c78e27..f0b8aea 100644 --- a/server/src/main/java/dev/shiperist/repository/project/ProjectAppRepository.java +++ b/server/src/main/java/dev/shiperist/repository/project/ProjectAppRepository.java @@ -3,9 +3,14 @@ import dev.shiperist.entity.project.ProjectAppEntity; import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; import io.quarkus.hibernate.reactive.panache.common.WithSession; +import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; @WithSession @ApplicationScoped public class ProjectAppRepository implements PanacheRepositoryBase { + + public Uni findByName(String name) { + return find("name", name).firstResult(); + } } diff --git a/server/src/main/java/dev/shiperist/resource/project/ProjectAppResource.java b/server/src/main/java/dev/shiperist/resource/project/ProjectAppResource.java index 82bb2f5..eb20603 100644 --- a/server/src/main/java/dev/shiperist/resource/project/ProjectAppResource.java +++ b/server/src/main/java/dev/shiperist/resource/project/ProjectAppResource.java @@ -1,8 +1,8 @@ package dev.shiperist.resource.project; +import dev.shiperist.exception.ErrorMessage; import dev.shiperist.model.project.ProjectApp; import dev.shiperist.service.project.ProjectAppService; -import dev.shiperist.service.project.ProjectMemberService; import io.quarkus.security.Authenticated; import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.RequestScoped; @@ -19,8 +19,6 @@ import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; import org.eclipse.microprofile.openapi.annotations.tags.Tag; -import java.util.function.Function; - @RequestScoped @Authenticated @@ -28,7 +26,7 @@ @Tag(name = "Project Apps") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) -public class ProjectAppResource extends BaseProjectResource{ +public class ProjectAppResource extends BaseProjectResource { //TODO Add project member to have permission to create project app @@ -39,6 +37,8 @@ public class ProjectAppResource extends BaseProjectResource{ @Claim(standard = Claims.sub) String sub; + //TODO Is this is the correct way to do it I mean to work with entities possibly in here or move it to the service layer? + @PUT @Operation(summary = "Create a project app") @APIResponse( @@ -47,9 +47,20 @@ public class ProjectAppResource extends BaseProjectResource{ content = @Content(schema = @Schema(implementation = ProjectApp.class)) ) public Uni createProjectApp(@Parameter(description = "Project ID") @PathParam("projectId") Long projectId, ProjectApp app) { - return ifMember(Long.parseLong(sub), projectId, isMember -> - projectAppService.createProjectApp(projectId, app.getName(), app.getDisplayName(), app.getDescription(), app.getImage(), app.getOs(), app.getReleaseType()) - .onItem().ifNotNull().transform(projectApp -> Response.status(Response.Status.CREATED).entity(projectApp).build())); + return ifMember(Long.parseLong(sub), projectId, isMember -> { + if (isMember) { + return projectAppService.doesProjectAppExist(app.getName()).flatMap(exists -> { + if (!exists) { + return projectAppService.createProjectApp(projectId, app.getName(), app.getDisplayName(), app.getDescription(), app.getImage(), app.getOs(), app.getReleaseType()) + .onItem().ifNotNull().transform(projectApp -> Response.status(Response.Status.CREATED).entity(projectApp).build()); + } else { + return Uni.createFrom().item(Response.status(Response.Status.BAD_REQUEST).entity(ErrorMessage.PROJECT_APP_ALREADY_EXISTS).build()); + } + }); + } else { + return Uni.createFrom().item(Response.status(Response.Status.FORBIDDEN).entity(ErrorMessage.PROJECT_NOT_MEMBER).build()); + } + }); } @PATCH @@ -61,9 +72,20 @@ public Uni createProjectApp(@Parameter(description = "Project ID") @Pa content = @Content(schema = @Schema(implementation = ProjectApp.class)) ) public Uni updateProjectApp(@Parameter(description = "Project App ID") @PathParam("id") Long id, ProjectApp app) { - return ifMember(Long.parseLong(sub), app.getProjectId(), isMember -> - projectAppService.updateProjectApp(id, app.getName(), app.getDisplayName(), app.getDescription(), app.getImage()) - .onItem().ifNotNull().transform(projectApp -> Response.status(Response.Status.OK).entity(projectApp).build())); + return ifMember(Long.parseLong(sub), app.getProjectId(), isMember -> { + if (isMember) { + return projectAppService.doesProjectAppExist(app.getName()).flatMap(exists -> { + if (!exists) { + return projectAppService.updateProjectApp(id, app.getName(), app.getDisplayName(), app.getDescription(), app.getImage()) + .onItem().ifNotNull().transform(projectApp -> Response.status(Response.Status.OK).entity(projectApp).build()); + } else { + return Uni.createFrom().item(Response.status(Response.Status.BAD_REQUEST).entity(ErrorMessage.PROJECT_APP_ALREADY_EXISTS).build()); + } + }); + } else { + return Uni.createFrom().item(Response.status(Response.Status.FORBIDDEN).entity(ErrorMessage.PROJECT_NOT_MEMBER).build()); + } + }); } @GET diff --git a/server/src/main/java/dev/shiperist/service/project/ProjectAppService.java b/server/src/main/java/dev/shiperist/service/project/ProjectAppService.java index 0ab23b1..65084d2 100644 --- a/server/src/main/java/dev/shiperist/service/project/ProjectAppService.java +++ b/server/src/main/java/dev/shiperist/service/project/ProjectAppService.java @@ -12,6 +12,7 @@ import jakarta.inject.Inject; import java.util.List; +import java.util.Objects; @ApplicationScoped public class ProjectAppService { @@ -28,10 +29,10 @@ public class ProjectAppService { public Uni createProjectApp(Long projectId, String name, String displayName, String description, String image, OsType os, ReleaseType releaseType) { return projectService.findById(projectId) - .onItem().ifNull().failWith(() -> new RuntimeException("Project not found")) .map(project -> { + String appName = project.getName() + "/" + name; ProjectAppEntity projectApp = new ProjectAppEntity(); - projectApp.setName(name); + projectApp.setName(appName); projectApp.setDisplayName(displayName); projectApp.setDescription(description); projectApp.setImage(image); @@ -46,9 +47,9 @@ public Uni createProjectApp(Long projectId, String name, String disp public Uni updateProjectApp(Long id, String name, String displayName, String description, String image) { return projectAppRepository.findById(id) - .onItem().ifNull().failWith(() -> new RuntimeException("Project App not found")) .map(projectApp -> { - projectApp.setName(name); + String appName = projectApp.getProject().getName() + "/" + name; + projectApp.setName(appName); projectApp.setDisplayName(displayName); projectApp.setDescription(description); projectApp.setImage(image); @@ -66,6 +67,10 @@ public Uni> getProjectApps(Long projectId) { return projectAppRepository.list("projectId", projectId).map(projectAppMapper::toDomainList); } + public Uni doesProjectAppExist(String name) { + return projectAppRepository.findByName(name).map(Objects::nonNull); + } + public Uni deleteProjectApp(Long id) { return projectAppRepository.deleteById(id); } diff --git a/server/src/test/java/dev/shiperist/app/BaseProjectAppResourceTest.java b/server/src/test/java/dev/shiperist/app/BaseProjectAppResourceTest.java index d43d6dd..5230525 100644 --- a/server/src/test/java/dev/shiperist/app/BaseProjectAppResourceTest.java +++ b/server/src/test/java/dev/shiperist/app/BaseProjectAppResourceTest.java @@ -7,7 +7,9 @@ import dev.shiperist.entity.project.ProjectEntity; import dev.shiperist.entity.project.ProjectMemberEntity; import dev.shiperist.mapper.account.UserMapper; +import dev.shiperist.mapper.project.ProjectMapper; import dev.shiperist.mapper.project.ProjectMemberMapper; +import dev.shiperist.model.project.Project; import dev.shiperist.model.project.ProjectApp; import dev.shiperist.model.project.ProjectMember; import dev.shiperist.repository.account.UserRepository; @@ -36,8 +38,11 @@ public abstract class BaseProjectAppResourceTest extends BaseTest { @Inject ProjectMemberRepository projectMemberRepository; + @Inject + ProjectMapper projectMapper; + protected ProjectApp app; - protected long projectId; + protected Project project; @BeforeAll public void beforeAll() throws Throwable { @@ -81,7 +86,7 @@ public void beforeAll() throws Throwable { VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() -> projectMemberRepository.persistAndFlush(projectMember))); - projectId = project.getId(); + this.project = projectMapper.toDomain(project); } @BeforeEach diff --git a/server/src/test/java/dev/shiperist/app/CreateEndpointTest.java b/server/src/test/java/dev/shiperist/app/CreateEndpointTest.java index accbd41..c89efc1 100644 --- a/server/src/test/java/dev/shiperist/app/CreateEndpointTest.java +++ b/server/src/test/java/dev/shiperist/app/CreateEndpointTest.java @@ -1,5 +1,6 @@ package dev.shiperist.app; +import dev.shiperist.exception.ErrorMessage; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.security.TestSecurity; import io.quarkus.test.security.jwt.Claim; @@ -24,13 +25,45 @@ public void testCreateApp() { given() .contentType(ContentType.JSON) .body(app) - .when().put("/projects/{projectId}/apps", projectId) + .when().put("/projects/{projectId}/apps", project.getId()) .then().statusCode(201) - .body("name", equalTo(app.getName()), + .body("name", equalTo(project.getName() + "/" + app.getName()), "displayName", equalTo(app.getDisplayName()), "description", equalTo(app.getDescription()), "image", equalTo(app.getImage()), "os", equalTo(app.getOs().name()), "releaseType", equalTo(app.getReleaseType().name())); } + + @Test + @DisplayName("PUT /projects/{projectId}/apps - create app - unauthorized") + public void testCreateAppUnauthorized() { + given() + .contentType(ContentType.JSON) + .body(app) + .when().put("/projects/{projectId}/apps", project.getId()) + .then().statusCode(401); + } + + @Test + @DisplayName("PUT /projects/{projectId}/apps - create app - already exists") + @TestSecurity(user = "app@test.com") + @JwtSecurity(claims = { + @Claim(key = "sub", value = "1") + }) + public void testCreateAppAlreadyExists() { + given() + .contentType(ContentType.JSON) + .body(app) + .when().put("/projects/{projectId}/apps", project.getId()) + .then().statusCode(201); + + given() + .contentType(ContentType.JSON) + .body(app) + .when().put("/projects/{projectId}/apps", project.getId()) + .then().statusCode(400) + .body("error", equalTo(ErrorMessage.PROJECT_APP_ALREADY_EXISTS.getError()), + "description", equalTo(ErrorMessage.PROJECT_APP_ALREADY_EXISTS.getDescription())); + } }