diff --git a/server/src/main/java/dev/shiperist/exception/ErrorMessageException.java b/server/src/main/java/dev/shiperist/exception/ErrorMessageException.java new file mode 100644 index 0000000..27b8e40 --- /dev/null +++ b/server/src/main/java/dev/shiperist/exception/ErrorMessageException.java @@ -0,0 +1,13 @@ +package dev.shiperist.exception; + +import lombok.Getter; + +@Getter +public class ErrorMessageException extends RuntimeException{ + + private final ErrorMessage errorMessage; + + public ErrorMessageException(ErrorMessage errorMessage) { + this.errorMessage = errorMessage; + } +} diff --git a/server/src/main/java/dev/shiperist/exception/ErrorMessageHandler.java b/server/src/main/java/dev/shiperist/exception/ErrorMessageHandler.java new file mode 100644 index 0000000..9bea788 --- /dev/null +++ b/server/src/main/java/dev/shiperist/exception/ErrorMessageHandler.java @@ -0,0 +1,13 @@ +package dev.shiperist.exception; + +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; + +@Provider +public class ErrorMessageHandler implements ExceptionMapper { + @Override + public Response toResponse(ErrorMessageException e) { + return Response.status(Response.Status.BAD_REQUEST).entity(e.getErrorMessage()).build(); + } +} diff --git a/server/src/main/java/dev/shiperist/exception/HibernateExceptionHandler.java b/server/src/main/java/dev/shiperist/exception/HibernateExceptionHandler.java index 5f3c0a8..4237573 100644 --- a/server/src/main/java/dev/shiperist/exception/HibernateExceptionHandler.java +++ b/server/src/main/java/dev/shiperist/exception/HibernateExceptionHandler.java @@ -10,14 +10,6 @@ public class HibernateExceptionHandler implements ExceptionMapper { @@ -13,4 +16,16 @@ public class ProjectAppRepository implements PanacheRepositoryBase findByName(String name) { return find("name", name).firstResult(); } + + public Uni> findByProject(Long projectId) { + return find("project.id", projectId).list(); + } + + public Uni hasPermission(Long appId, Long userId) { + return find("id = ?1 and project.projectMembers.user.id = ?2", appId, userId).firstResult().map(Objects::nonNull); + } + + public Uni getIfMember(Long appId, Long userId) { + return find("select p from ProjectApp p join p.project.projectMembers m where p.id = ?1 and m.user.id = ?2", appId, userId).firstResult(); + } } diff --git a/server/src/main/java/dev/shiperist/repository/project/ProjectRepository.java b/server/src/main/java/dev/shiperist/repository/project/ProjectRepository.java index 9dbbafa..bba5b68 100644 --- a/server/src/main/java/dev/shiperist/repository/project/ProjectRepository.java +++ b/server/src/main/java/dev/shiperist/repository/project/ProjectRepository.java @@ -6,6 +6,8 @@ import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; +import java.util.Objects; + @WithSession @ApplicationScoped public class ProjectRepository implements PanacheRepositoryBase { @@ -13,4 +15,12 @@ public class ProjectRepository implements PanacheRepositoryBase findByName(String name) { return find("name", name).firstResult(); } + + public Uni hasPermission(Long projectId, Long userId) { + return find("id = ?1 and projectMembers.user.id = ?2", projectId, userId).firstResult().map(Objects::nonNull); + } + + public Uni getIfMember(Long projectId, Long userId) { + return find("select p from Project p join p.projectMembers m where p.id = ?1 and m.user.id = ?2", projectId, userId).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 eb20603..6feb283 100644 --- a/server/src/main/java/dev/shiperist/resource/project/ProjectAppResource.java +++ b/server/src/main/java/dev/shiperist/resource/project/ProjectAppResource.java @@ -47,20 +47,8 @@ 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 -> { - 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()); - } - }); + return projectAppService.createProjectApp(projectId, Long.parseLong(sub), app.getName(), app.getDisplayName(), app.getDescription(), app.getImage(), app.getOs(), app.getReleaseType()) + .onItem().ifNotNull().transform(projectApp -> Response.status(Response.Status.CREATED).entity(projectApp).build()); } @PATCH @@ -72,20 +60,8 @@ 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 -> { - 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()); - } - }); + return projectAppService.updateProjectApp(id, Long.parseLong(sub), app.getName(), app.getDisplayName(), app.getDescription(), app.getImage()) + .onItem().ifNotNull().transform(projectApp -> Response.status(Response.Status.OK).entity(projectApp).build()); } @GET @@ -97,9 +73,8 @@ public Uni updateProjectApp(@Parameter(description = "Project App ID") content = @Content(schema = @Schema(implementation = ProjectApp.class)) ) public Uni getProjectApp(@Parameter(description = "Project App ID") @PathParam("id") Long id) { - return ifMember(Long.parseLong(sub), id, isMember -> - projectAppService.getProjectApp(id) - .onItem().ifNotNull().transform(projectApp -> Response.status(Response.Status.OK).entity(projectApp).build())); + return projectAppService.getProjectApp(id, Long.parseLong(sub)) + .onItem().ifNotNull().transform(projectApp -> Response.status(Response.Status.OK).entity(projectApp).build()); } @GET @@ -110,8 +85,7 @@ public Uni getProjectApp(@Parameter(description = "Project App ID") @P content = @Content(schema = @Schema(implementation = ProjectApp.class)) ) public Uni getProjectApps(@Parameter(description = "Project ID") @PathParam("projectId") Long projectId) { - return ifMember(Long.parseLong(sub), projectId, isMember -> - projectAppService.getProjectApps(projectId) - .onItem().ifNotNull().transform(projectApps -> Response.status(Response.Status.OK).entity(projectApps).build())); + return projectAppService.getProjectApps(projectId, Long.parseLong(sub)) + .onItem().ifNotNull().transform(projectApps -> Response.status(Response.Status.OK).entity(projectApps).build()); } } 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 65084d2..ac8d8d8 100644 --- a/server/src/main/java/dev/shiperist/service/project/ProjectAppService.java +++ b/server/src/main/java/dev/shiperist/service/project/ProjectAppService.java @@ -3,6 +3,9 @@ import dev.shiperist.data.OsType; import dev.shiperist.data.ReleaseType; import dev.shiperist.entity.project.ProjectAppEntity; +import dev.shiperist.entity.project.ProjectEntity; +import dev.shiperist.exception.ErrorMessage; +import dev.shiperist.exception.ErrorMessageException; import dev.shiperist.mapper.project.ProjectAppMapper; import dev.shiperist.model.project.ProjectApp; import dev.shiperist.repository.project.ProjectAppRepository; @@ -10,9 +13,9 @@ import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import jakarta.ws.rs.NotFoundException; import java.util.List; -import java.util.Objects; @ApplicationScoped public class ProjectAppService { @@ -27,51 +30,64 @@ public class ProjectAppService { @Inject ProjectAppMapper projectAppMapper; - public Uni createProjectApp(Long projectId, String name, String displayName, String description, String image, OsType os, ReleaseType releaseType) { - return projectService.findById(projectId) - .map(project -> { - String appName = project.getName() + "/" + name; - ProjectAppEntity projectApp = new ProjectAppEntity(); - projectApp.setName(appName); - projectApp.setDisplayName(displayName); - projectApp.setDescription(description); - projectApp.setImage(image); - projectApp.setOs(os); - projectApp.setReleaseType(releaseType); - projectApp.setProject(project); - return projectApp; - }) - .onItem().ifNotNull().transformToUni(projectAppRepository::persistAndFlush) + public Uni createProjectApp(Long projectId, Long userId, String name, String displayName, String description, String image, OsType os, ReleaseType releaseType) { + return projectService.getIfMember(projectId, userId) + .onItem().ifNull().failWith(new ErrorMessageException(ErrorMessage.PROJECT_NOT_FOUND)) + .map(project -> buildProjectAppEntity(project, name, displayName, description, image, os, releaseType)) + .onItem().ifNotNull().transformToUni(this::persistIfNotExists) .onItem().ifNotNull().transform(projectAppMapper::toDomain); } - public Uni updateProjectApp(Long id, String name, String displayName, String description, String image) { - return projectAppRepository.findById(id) - .map(projectApp -> { - String appName = projectApp.getProject().getName() + "/" + name; - projectApp.setName(appName); - projectApp.setDisplayName(displayName); - projectApp.setDescription(description); - projectApp.setImage(image); - return projectApp; - }) - .onItem().ifNotNull().transformToUni(projectAppRepository::persistAndFlush) + public Uni updateProjectApp(Long id, Long userId, String name, String displayName, String description, String image) { + return projectAppRepository.getIfMember(id, userId) + .onItem().ifNull().failWith(new ErrorMessageException(ErrorMessage.PROJECT_APP_NOT_FOUND)) + .map(projectApp -> updateProjectAppEntity(projectApp, name, displayName, description, image)) + .onItem().ifNotNull().transformToUni(this::persistIfNotExists) .onItem().ifNotNull().transform(projectAppMapper::toDomain); } - public Uni getProjectApp(Long id) { - return projectAppRepository.findById(id).map(projectAppMapper::toDomain); + public Uni getProjectApp(Long id, Long userId) { + return projectAppRepository.getIfMember(id, userId) + .onItem().ifNull().failWith(new ErrorMessageException(ErrorMessage.PROJECT_APP_NOT_FOUND)) + .map(projectAppMapper::toDomain); } - public Uni> getProjectApps(Long projectId) { - return projectAppRepository.list("projectId", projectId).map(projectAppMapper::toDomainList); + public Uni> getProjectApps(Long projectId, Long userId) { + return projectService.getIfMember(projectId, userId) + .onItem().ifNull().failWith(new ErrorMessageException(ErrorMessage.PROJECT_NOT_FOUND)) + .onItem().ifNotNull().transformToUni(project -> projectAppRepository.findByProject(projectId)) + .onItem().ifNotNull().transform(projectAppMapper::toDomainList); } - public Uni doesProjectAppExist(String name) { - return projectAppRepository.findByName(name).map(Objects::nonNull); + private Uni persistIfNotExists(ProjectAppEntity projectApp) { + return projectAppRepository.findByName(projectApp.getName()) + .onItem().ifNotNull().failWith(new ErrorMessageException(ErrorMessage.PROJECT_APP_ALREADY_EXISTS)) + .onItem().ifNull().switchTo(() -> projectAppRepository.persistAndFlush(projectApp)); } - public Uni deleteProjectApp(Long id) { - return projectAppRepository.deleteById(id); + private ProjectAppEntity buildProjectAppEntity(ProjectEntity project, String name, String displayName, String description, String image, OsType os, ReleaseType releaseType) { + String appName = constructAppName(project.getName(), name); + ProjectAppEntity projectApp = new ProjectAppEntity(); + projectApp.setName(appName); + projectApp.setDisplayName(displayName); + projectApp.setDescription(description); + projectApp.setImage(image); + projectApp.setOs(os); + projectApp.setReleaseType(releaseType); + projectApp.setProject(project); + return projectApp; + } + + private ProjectAppEntity updateProjectAppEntity(ProjectAppEntity projectApp, String name, String displayName, String description, String image) { + String appName = constructAppName(projectApp.getProject().getName(), name); + projectApp.setName(appName); + projectApp.setDisplayName(displayName); + projectApp.setDescription(description); + projectApp.setImage(image); + return projectApp; + } + + private String constructAppName(String projectName, String appName) { + return projectName + "/" + appName; } }