diff --git a/src/main/java/com/example/demo/controller/AdaptiveTrainingDefinitionsRestController.java b/src/main/java/com/example/demo/controller/AdaptiveTrainingDefinitionsRestController.java index b35ba070fb3ec05eeba25d7a601a86c44de2e196..6e4aee12776752327eb9d9442b704822207f050c 100644 --- a/src/main/java/com/example/demo/controller/AdaptiveTrainingDefinitionsRestController.java +++ b/src/main/java/com/example/demo/controller/AdaptiveTrainingDefinitionsRestController.java @@ -147,21 +147,21 @@ public class AdaptiveTrainingDefinitionsRestController { levelOperationsService.updatePhaseLevel(phaseLevelUpdateDto); } - @ApiOperation(httpMethod = "PUT", - value = "Update task", - nickname = "updateTask", - consumes = MediaType.APPLICATION_JSON_VALUE - ) - @ApiResponses(value = { - @ApiResponse(code = 200, message = "Task updated"), - @ApiResponse(code = 500, message = "Unexpected application error") - }) - @PutMapping(path = "/tasks") - public void updateTask( - @ApiParam(value = "Task to be updated") @RequestBody TaskUpdateDto taskUpdateDto) { - - levelOperationsService.updateTask(taskUpdateDto); - } +// @ApiOperation(httpMethod = "PUT", +// value = "Update task", +// nickname = "updateTask", +// consumes = MediaType.APPLICATION_JSON_VALUE +// ) +// @ApiResponses(value = { +// @ApiResponse(code = 200, message = "Task updated"), +// @ApiResponse(code = 500, message = "Unexpected application error") +// }) +// @PutMapping(path = "/tasks") +// public void updateTask( +// @ApiParam(value = "Task to be updated") @RequestBody TaskUpdateDto taskUpdateDto) { +// +// levelOperationsService.updateTask(taskUpdateDto); +// } // @ApiOperation(httpMethod = "POST", // value = "Create a new task", diff --git a/src/main/java/com/example/demo/controller/TasksController.java b/src/main/java/com/example/demo/controller/TasksController.java index a5fc6b08f9a50caaec63a7189dfe4e91e83906e5..ca50097adf8f119a1a697060b972f01510dd5d56 100644 --- a/src/main/java/com/example/demo/controller/TasksController.java +++ b/src/main/java/com/example/demo/controller/TasksController.java @@ -1,6 +1,7 @@ package com.example.demo.controller; import com.example.demo.dto.TaskDto; +import com.example.demo.dto.TaskUpdateDto; import com.example.demo.service.TaskService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -12,12 +13,17 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import javax.validation.Valid; + @RestController @RequestMapping(value = "/training-definitions/{definitionId}/phases/{phaseId}/tasks", produces = MediaType.APPLICATION_JSON_VALUE) @Api(value = "/training-definitions/{definitionId}/phases", @@ -102,4 +108,53 @@ public class TasksController { return new ResponseEntity<>(createdTask, HttpStatus.CREATED); } + + @ApiOperation(httpMethod = "PUT", + value = "Update task", + notes = "Update the specified task", + nickname = "updateTask", + consumes = MediaType.APPLICATION_JSON_VALUE + ) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Task updated"), + @ApiResponse(code = 500, message = "Unexpected application error") + }) + @PutMapping + public ResponseEntity<TaskDto> updateTask( + @ApiParam(value = "Training definition ID", required = true) + @PathVariable(name = "definitionId") Long definitionId, + @ApiParam(value = "Game phase ID", required = true) + @PathVariable(name = "phaseId") Long phaseId, + @ApiParam(value = "Task to be updated") + @RequestBody @Valid TaskUpdateDto taskUpdateDto) { + + TaskDto updatedTask = taskService.updateTask(definitionId, phaseId, taskUpdateDto); + + return new ResponseEntity<>(updatedTask, HttpStatus.OK); + } + + @ApiOperation(httpMethod = "DELETE", + value = "Remove a task", + notes = "Remove the specified task", + response = TaskDto.class, + nickname = "removeTask", + produces = MediaType.APPLICATION_JSON_VALUE + ) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Task removed"), + @ApiResponse(code = 500, message = "Unexpected application error") + }) + @DeleteMapping(path = "/{taskId}") + public ResponseEntity<Void> removeTask( + @ApiParam(value = "Training definition ID", required = true) + @PathVariable(name = "definitionId") Long definitionId, + @ApiParam(value = "Game phase ID", required = true) + @PathVariable(name = "phaseId") Long phaseId, + @ApiParam(value = "Task ID", required = true) + @PathVariable(name = "taskId") Long taskId) { + + taskService.removeTask(definitionId, phaseId, taskId); + + return ResponseEntity.ok().build(); + } } diff --git a/src/main/java/com/example/demo/dto/TaskUpdateDto.java b/src/main/java/com/example/demo/dto/TaskUpdateDto.java index 6ad71c15498f8f71f6a1cdce7ef7238589b7a339..a1726bef3cc7b5834ffc2dcc0f99b47e56993d73 100644 --- a/src/main/java/com/example/demo/dto/TaskUpdateDto.java +++ b/src/main/java/com/example/demo/dto/TaskUpdateDto.java @@ -1,15 +1,38 @@ package com.example.demo.dto; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.PositiveOrZero; import java.util.List; public class TaskUpdateDto { + @ApiModelProperty(value = "Task ID", required = true, example = "1") + @NotNull(message = "Task ID must be specified") private Long id; + + @ApiModelProperty(value = "Short description of task", required = true, example = "Task title") + @NotEmpty(message = "Task title must not be blank") private String title; + + @ApiModelProperty(value = "The information that are displayed to a player", required = true, example = "Capture the flag") + @NotEmpty(message = "Task content must not be blank") private String content; + + @ApiModelProperty(value = "Keyword that must be found in the task. Necessary in order to get to the next level", required = true, example = "secretFlag") + @NotEmpty(message = "Flag of task cannot be null") private String flag; + + @ApiModelProperty(value = "Description how to get the flag", required = true, example = "Open secret.txt") + @NotEmpty(message = "Solution of task cannot be null") private String solution; - private Long incorrectFlagLimit; + + @ApiModelProperty(value = "It defines the allowed number of incorrect flags submitted by the player", required = true, example = "5") + @NotNull(message = "Limit of the number of provided incorrect flags must be specified") + @PositiveOrZero(message = "Limit of the number of provided incorrect flags must not be a negative number") + private Integer incorrectFlagLimit; // private List<AttachmentDto> attachments; @@ -62,11 +85,11 @@ public class TaskUpdateDto { // this.attachments = attachments; // } - public Long getIncorrectFlagLimit() { + public Integer getIncorrectFlagLimit() { return incorrectFlagLimit; } - public void setIncorrectFlagLimit(Long incorrectFlagLimit) { + public void setIncorrectFlagLimit(Integer incorrectFlagLimit) { this.incorrectFlagLimit = incorrectFlagLimit; } diff --git a/src/main/java/com/example/demo/repository/TaskRepository.java b/src/main/java/com/example/demo/repository/TaskRepository.java index 03121acc8c9e914277f2d338512223480133a2d9..7fa3776911456960bd06bbdfe42897c17724a43e 100644 --- a/src/main/java/com/example/demo/repository/TaskRepository.java +++ b/src/main/java/com/example/demo/repository/TaskRepository.java @@ -2,11 +2,21 @@ package com.example.demo.repository; import com.example.demo.domain.Task; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; public interface TaskRepository extends JpaRepository<Task, Long> { @Query("SELECT COALESCE(MAX(g.order), -1) FROM Task g WHERE g.phaseLevel.id = :phaseId") Integer getCurrentMaxOrder(@Param("phaseId") Long phaseId); + + @Transactional + @Modifying + @Query("UPDATE Task t SET t.order = t.order - 1 " + + "WHERE t.phaseLevel.id = :gamePhaseId " + + "AND t.order > :order ") + void decreaseOrderAfterTaskWasDeleted(@Param("gamePhaseId") Long gamePhaseId, + @Param("order") int order); } diff --git a/src/main/java/com/example/demo/service/LevelOperationsService.java b/src/main/java/com/example/demo/service/LevelOperationsService.java index fd947acc0147949ba87f1c352e075d6b7eb251f1..fdf521269bd6ce00e50a867ab41ebdcf348bcb62 100644 --- a/src/main/java/com/example/demo/service/LevelOperationsService.java +++ b/src/main/java/com/example/demo/service/LevelOperationsService.java @@ -6,7 +6,6 @@ import com.example.demo.domain.PhaseLevel; import com.example.demo.domain.Question; import com.example.demo.domain.QuestionChoice; import com.example.demo.domain.QuestionnaireLevel; -import com.example.demo.domain.Task; import com.example.demo.dto.BaseLevelDto; import com.example.demo.dto.InfoLevelUpdateDto; import com.example.demo.dto.PhaseCreateDTO; @@ -16,7 +15,6 @@ import com.example.demo.dto.QuestionChoiceUpdateDto; import com.example.demo.dto.QuestionDto; import com.example.demo.dto.QuestionUpdateDto; import com.example.demo.dto.QuestionnaireUpdateDto; -import com.example.demo.dto.TaskUpdateDto; import com.example.demo.enums.PhaseType; import com.example.demo.enums.QuestionType; import com.example.demo.mapper.BeanMapper; @@ -156,11 +154,6 @@ public class LevelOperationsService { phaseLevelService.updatePhaseLevel(phaseLevel); } - public void updateTask(TaskUpdateDto taskUpdateDto) { - Task task = BeanMapper.INSTANCE.toEntity(taskUpdateDto); - taskService.updateTask(task); - } - public QuestionDto createQuestion(Long questionnaireId, QuestionType questionType) { QuestionDto createdQuestion = questionService.createDefaultQuestion(questionnaireId, questionType); diff --git a/src/main/java/com/example/demo/service/TaskService.java b/src/main/java/com/example/demo/service/TaskService.java index be7c887c33618887a215d56a5fe46c74a00670a8..166e48abfe38af1f3e5f943d3bb9ba0f577d40da 100644 --- a/src/main/java/com/example/demo/service/TaskService.java +++ b/src/main/java/com/example/demo/service/TaskService.java @@ -4,6 +4,7 @@ import com.example.demo.domain.PhaseLevel; import com.example.demo.domain.Task; import com.example.demo.dto.TaskCreateDto; import com.example.demo.dto.TaskDto; +import com.example.demo.dto.TaskUpdateDto; import com.example.demo.mapper.BeanMapper; import com.example.demo.repository.PhaseLevelRepository; import com.example.demo.repository.TaskRepository; @@ -16,7 +17,6 @@ import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; -import java.util.Optional; @Service public class TaskService { @@ -105,25 +105,33 @@ public class TaskService { return BeanMapper.INSTANCE.toDto(task); } - public TaskDto updateTask(Task taskUpdate) { - Optional<Task> persistedTask = taskRepository.findById(taskUpdate.getId()); + public TaskDto updateTask(Long trainingDefinitionId, Long phaseId, TaskUpdateDto taskUpdateDto) { + Task taskUpdate = BeanMapper.INSTANCE.toEntity(taskUpdateDto); - if (persistedTask.isEmpty()) { - // TODO return 404 - LOG.error("No task found with ID {}.", taskUpdate.getId()); - return new TaskDto(); - } + Task persistedTask = taskRepository.findById(taskUpdate.getId()) + .orElseThrow(() -> new RuntimeException("Task was not found")); + // TODO throw proper exception once kypo2-training is migrated - taskUpdate.setPhaseLevel(persistedTask.get().getPhaseLevel()); - taskUpdate.setTrainingDefinitionId(persistedTask.get().getTrainingDefinitionId()); - taskUpdate.setOrder(persistedTask.get().getOrder()); + // TODO add check to trainingDefinitionId and phaseId (field structure will be probably changed) + + taskUpdate.setPhaseLevel(persistedTask.getPhaseLevel()); + taskUpdate.setTrainingDefinitionId(persistedTask.getTrainingDefinitionId()); + taskUpdate.setOrder(persistedTask.getOrder()); Task savedEntity = taskRepository.save(taskUpdate); return BeanMapper.INSTANCE.toDto(savedEntity); } - public void removeTaskLevel(Long id) { - taskRepository.deleteById(id); + public void removeTask(Long trainingDefinitionId, Long phaseId, Long taskId) { + Task task = taskRepository.findById(taskId) + .orElseThrow(() -> new RuntimeException("Task was not found")); + // TODO throw proper exception once kypo2-training is migrated + + // TODO add check to trainingDefinitionId and phaseId (field structure will be probably changed) + + taskRepository.decreaseOrderAfterTaskWasDeleted(phaseId, task.getOrder()); + + taskRepository.delete(task); } }