diff --git a/backend/pom.xml b/backend/pom.xml index ed1fd00..a75708c 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 org.example @@ -8,9 +9,6 @@ app quarkus-demo-app-backend - @@ -95,12 +93,10 @@ com.querydsl querydsl-apt - provided ${querydsl.version} jakarta + provided - - io.quarkus quarkus-junit5 @@ -207,7 +203,6 @@ org.jboss.logmanager.LogManager - ${maven.home} @@ -225,7 +220,6 @@ ${project.build.directory}/${project.build.finalName}-runner org.jboss.logmanager.LogManager - ${maven.home} diff --git a/backend/src/main/java/org/example/app/task/common/TaskItem.java b/backend/src/main/java/org/example/app/task/common/TaskItem.java new file mode 100644 index 0000000..5533528 --- /dev/null +++ b/backend/src/main/java/org/example/app/task/common/TaskItem.java @@ -0,0 +1,58 @@ +package org.example.app.task.common; + +import org.example.app.general.common.ApplicationEntity; + +import java.time.LocalDateTime; + +public interface TaskItem extends ApplicationEntity { + + /** + * @return the title of this task item. Gives a brief summary to describe what to do or buy. + */ + String getTitle(); + + /** + * @param title new value of {@link #getTitle()}. + */ + void setTitle(String title); + + /** + * @return {@code true} if this task is completed (done), {@code false} otherwise. + */ + boolean isCompleted(); + + /** + * @param completed new value of {@link #isCompleted()}. + */ + void setCompleted(boolean completed); + + /** + * @return {@code true} if this task is starred (marked as favorite), {@code false} otherwise. + */ + boolean isStarred(); + + /** + * @param starred new value of {@link #isStarred()}. + */ + void setStarred(boolean starred); + + /** + * @return the deadline as until this task shall be {@link #isCompleted() completed}. + */ + LocalDateTime getDeadline(); + + /** + * @param deadline the new value of {@link #getDeadline()}. + */ + void setDeadline(LocalDateTime deadline); + + /** + * @return the {@link TaskList#getId() primary key} of the owning {@link TaskList}. + */ + Long getTaskListId(); + + /** + * @param taskListId the new value of {@link #getTaskListId()}. + */ + void setTaskListId(Long taskListId); +} diff --git a/backend/src/main/java/org/example/app/task/common/TaskList.java b/backend/src/main/java/org/example/app/task/common/TaskList.java new file mode 100644 index 0000000..74cb341 --- /dev/null +++ b/backend/src/main/java/org/example/app/task/common/TaskList.java @@ -0,0 +1,18 @@ +package org.example.app.task.common; + +import org.example.app.general.common.ApplicationEntity; + +public interface TaskList extends ApplicationEntity { + + /** + * @return the title of this {@link TaskList}. Gives a brief summary to describe this list of tasks (e.g. "Shopping + * list", "Packing list" or "Things to buy at construction market"). + */ + String getTitle(); + + /** + * @param title new value of {@link #getTitle()}. + */ + void setTitle(String title); +} + diff --git a/backend/src/main/java/org/example/app/task/dataaccess/TaskItemEntity.java b/backend/src/main/java/org/example/app/task/dataaccess/TaskItemEntity.java new file mode 100644 index 0000000..5a9dd69 --- /dev/null +++ b/backend/src/main/java/org/example/app/task/dataaccess/TaskItemEntity.java @@ -0,0 +1,90 @@ +package org.example.app.task.dataaccess; + +import jakarta.persistence.*; +import org.example.app.general.dataaccess.ApplicationPersistenceEntity; +import org.example.app.task.common.TaskItem; + +import java.time.LocalDateTime; + +@Entity +@Table(name = "task_item") +public class TaskItemEntity extends ApplicationPersistenceEntity implements TaskItem { + + @Column + private String title; + + @Column + private boolean completed; + + @Column + private boolean starred; + + @JoinColumn(name = "LIST_ID") + @ManyToOne(fetch = FetchType.LAZY) + private TaskListEntity taskListEntity; + + @Column + private LocalDateTime deadline; + @Override + public String getTitle() { + return title; + } + @Override + public void setTitle(String title) { + this.title = title; + } + @Override + public boolean isCompleted() { + return completed; + } + @Override + public void setCompleted(boolean completed) { + this.completed = completed; + } + @Override + public boolean isStarred() { + return starred; + } + @Override + public void setStarred(boolean starred) { + this.starred = starred; + } + @Override + public LocalDateTime getDeadline() { + return deadline; + } + @Override + public void setDeadline(LocalDateTime deadline) { + this.deadline = deadline; + } + + @Override + public Long getTaskListId() { + + if (this.taskListEntity == null) { + return null; + } + return this.taskListEntity.getId(); + } + + @Override + public void setTaskListId(Long taskListId) { + + if (taskListId == null) { + this.taskListEntity = null; + } else { + TaskListEntity taskListEntity = new TaskListEntity(); + taskListEntity.setId(taskListId); + taskListEntity.setVersion(Integer.valueOf(0)); + this.taskListEntity = taskListEntity; + } + } + + public TaskListEntity getTaskListEntity() { + return taskListEntity; + } + + public void setTaskListEntity(TaskListEntity taskListEntity) { + this.taskListEntity = taskListEntity; + } +} diff --git a/backend/src/main/java/org/example/app/task/dataaccess/TaskItemRepository.java b/backend/src/main/java/org/example/app/task/dataaccess/TaskItemRepository.java new file mode 100644 index 0000000..7774112 --- /dev/null +++ b/backend/src/main/java/org/example/app/task/dataaccess/TaskItemRepository.java @@ -0,0 +1,7 @@ +package org.example.app.task.dataaccess; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TaskItemRepository extends JpaRepository { + +} \ No newline at end of file diff --git a/backend/src/main/java/org/example/app/task/dataaccess/TaskListEntity.java b/backend/src/main/java/org/example/app/task/dataaccess/TaskListEntity.java new file mode 100644 index 0000000..7af1446 --- /dev/null +++ b/backend/src/main/java/org/example/app/task/dataaccess/TaskListEntity.java @@ -0,0 +1,50 @@ +package org.example.app.task.dataaccess; + +import jakarta.persistence.*; +import org.example.app.general.dataaccess.ApplicationPersistenceEntity; +import org.example.app.task.common.TaskList; + +import java.util.List; + +@Entity +@Table(name = "task_list") +public class TaskListEntity extends ApplicationPersistenceEntity implements TaskList { + + @Column + private String title; + + @OneToMany(fetch = FetchType.LAZY, mappedBy = "taskListEntity") + List taskItemEntities; + @Override + public String getTitle() { + return title; + } + @Override + public void setTitle(String title) { + this.title = title; + } + + /** + * @return the {@link List} of {@link TaskItemEntity task-items} in this task-list. + */ + public List getItems() { + + return this.taskItemEntities; + } + + /** + * @param taskItems the new value of {@link #getItems()}. + */ + public void setItems(List taskItems) { + + this.taskItemEntities = taskItems; + } + + public List getTaskItemEntities() { + return taskItemEntities; + } + + public void setTaskItemEntities(List taskItemEntities) { + this.taskItemEntities = taskItemEntities; + } +} diff --git a/backend/src/main/java/org/example/app/task/dataaccess/TaskListRepository.java b/backend/src/main/java/org/example/app/task/dataaccess/TaskListRepository.java new file mode 100644 index 0000000..777d0c7 --- /dev/null +++ b/backend/src/main/java/org/example/app/task/dataaccess/TaskListRepository.java @@ -0,0 +1,13 @@ +package org.example.app.task.dataaccess; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; + +public interface TaskListRepository extends JpaRepository { + + @Query("SELECT list FROM TaskListEntity list JOIN list.taskItemEntities items WHERE items.completed = true") + List findByCompleted(); + +} \ No newline at end of file diff --git a/backend/src/main/java/org/example/app/task/logic/TaskItemEto.java b/backend/src/main/java/org/example/app/task/logic/TaskItemEto.java new file mode 100644 index 0000000..1ef2dbc --- /dev/null +++ b/backend/src/main/java/org/example/app/task/logic/TaskItemEto.java @@ -0,0 +1,68 @@ +package org.example.app.task.logic; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.example.app.general.common.AbstractEto; +import org.example.app.task.common.TaskItem; +import org.example.app.task.dataaccess.TaskListEntity; + +import java.time.LocalDateTime; +@Schema(name = "TaskItem", description = "Object that represents a task item") +public class TaskItemEto extends AbstractEto implements TaskItem { + @NotBlank(message = "A task item must always have a title") + @Schema(required = true, example = "Buy eggs", description = "The task title or description") + private String title; + @Schema(required = false, description = "Until when the task must be completed") + private boolean completed; + @Schema(required = false, description = "Until when the task must be completed") + private boolean starred; + + @Min(value = 1, message = "A task item must always be associated with a task list") + @NotNull(message = "A task item must always be associated with a task list") + @Schema(required = true, example = "1", description = "The id of the task list to which this item belongs") + private Long taskListId; + @Schema(required = false, description = "Until when the task must be completed") + private LocalDateTime deadline; + @Override + public String getTitle() { + return title; + } + @Override + public void setTitle(String title) { + this.title = title; + } + @Override + public boolean isCompleted() { + return completed; + } + @Override + public void setCompleted(boolean completed) { + this.completed = completed; + } + @Override + public boolean isStarred() { + return starred; + } + @Override + public void setStarred(boolean starred) { + this.starred = starred; + } + @Override + public LocalDateTime getDeadline() { + return deadline; + } + @Override + public void setDeadline(LocalDateTime deadline) { + this.deadline = deadline; + } + @Override + public Long getTaskListId() { + return taskListId; + } + @Override + public void setTaskListId(Long id) { + this.taskListId = id; + } +} diff --git a/backend/src/main/java/org/example/app/task/logic/TaskItemMapper.java b/backend/src/main/java/org/example/app/task/logic/TaskItemMapper.java new file mode 100644 index 0000000..734273c --- /dev/null +++ b/backend/src/main/java/org/example/app/task/logic/TaskItemMapper.java @@ -0,0 +1,24 @@ +package org.example.app.task.logic; + +import org.example.app.task.dataaccess.TaskItemEntity; +import org.mapstruct.Mapper; + +import java.util.ArrayList; +import java.util.List; + +@Mapper(componentModel = "cdi") +public interface TaskItemMapper { + + default List toEtos(List items) { + List result = new ArrayList<>(); + for (TaskItemEntity item : items) { + result.add(toEto(item)); + } + + return result; + } + + TaskItemEto toEto(TaskItemEntity item); + + TaskItemEntity toEntity(TaskItemEto item); +} diff --git a/backend/src/main/java/org/example/app/task/logic/TaskListCto.java b/backend/src/main/java/org/example/app/task/logic/TaskListCto.java new file mode 100644 index 0000000..ae617a9 --- /dev/null +++ b/backend/src/main/java/org/example/app/task/logic/TaskListCto.java @@ -0,0 +1,50 @@ +package org.example.app.task.logic; + +import java.util.List; + +public class TaskListCto { + private TaskListEto list; + + private List items; + + /** + * The constructor. + */ + public TaskListCto() { + + super(); + } + + /** + * @return the {@link TaskListEto}. + */ + public TaskListEto getList() { + + return this.list; + } + + /** + * @param taskListEto new value of {@link #getList()}. + */ + public void setList(TaskListEto taskListEto) { + + this.list = taskListEto; + } + + /** + * @return the {@link List} of {@link TaskItemEto items} contained in the #get. + */ + public List getItems() { + + return this.items; + } + + /** + * @param items new value of {@link #getItems()}. + */ + public void setItems(List items) { + + this.items = items; + } + +} diff --git a/backend/src/main/java/org/example/app/task/logic/TaskListEto.java b/backend/src/main/java/org/example/app/task/logic/TaskListEto.java new file mode 100644 index 0000000..7ccf2cf --- /dev/null +++ b/backend/src/main/java/org/example/app/task/logic/TaskListEto.java @@ -0,0 +1,22 @@ +package org.example.app.task.logic; + +import org.example.app.general.common.AbstractEto; +import org.example.app.task.common.TaskList; +import org.example.app.task.dataaccess.TaskItemEntity; + +import java.util.List; + +public class TaskListEto extends AbstractEto implements TaskList { + + private String title; + + @Override + public String getTitle() { + return title; + } + @Override + public void setTitle(String title) { + this.title = title; + } + +} diff --git a/backend/src/main/java/org/example/app/task/logic/TaskListMapper.java b/backend/src/main/java/org/example/app/task/logic/TaskListMapper.java new file mode 100644 index 0000000..1745014 --- /dev/null +++ b/backend/src/main/java/org/example/app/task/logic/TaskListMapper.java @@ -0,0 +1,16 @@ +package org.example.app.task.logic; + +import org.example.app.task.dataaccess.TaskListEntity; +import org.mapstruct.Mapper; + +import java.util.List; + +@Mapper(componentModel = "cdi") +public interface TaskListMapper { + + List toEtos(List item); + + TaskListEto toEto(TaskListEntity item); + + TaskListEntity toEntity(TaskListEto item); +} diff --git a/backend/src/main/java/org/example/app/task/logic/UcDeleteTaskItem.java b/backend/src/main/java/org/example/app/task/logic/UcDeleteTaskItem.java new file mode 100644 index 0000000..b5b909f --- /dev/null +++ b/backend/src/main/java/org/example/app/task/logic/UcDeleteTaskItem.java @@ -0,0 +1,21 @@ +package org.example.app.task.logic; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import jakarta.transaction.Transactional; +import org.example.app.task.dataaccess.TaskItemRepository; + +@ApplicationScoped +@Named +@Transactional +public class UcDeleteTaskItem { + + @Inject + TaskItemRepository taskItemRepository; + + + public void deleteById(Long itemId) { + this.taskItemRepository.deleteById(itemId); + } +} diff --git a/backend/src/main/java/org/example/app/task/logic/UcDeleteTaskList.java b/backend/src/main/java/org/example/app/task/logic/UcDeleteTaskList.java new file mode 100644 index 0000000..5ede7f6 --- /dev/null +++ b/backend/src/main/java/org/example/app/task/logic/UcDeleteTaskList.java @@ -0,0 +1,21 @@ +package org.example.app.task.logic; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import jakarta.transaction.Transactional; +import org.example.app.task.dataaccess.TaskListRepository; + +@ApplicationScoped +@Named +@Transactional +public class UcDeleteTaskList { + + @Inject + TaskListRepository taskListRepository; + + + public void deleteById(Long itemId) { + this.taskListRepository.deleteById(itemId); + } +} diff --git a/backend/src/main/java/org/example/app/task/logic/UcFindTaskItem.java b/backend/src/main/java/org/example/app/task/logic/UcFindTaskItem.java new file mode 100644 index 0000000..12c0b55 --- /dev/null +++ b/backend/src/main/java/org/example/app/task/logic/UcFindTaskItem.java @@ -0,0 +1,27 @@ +package org.example.app.task.logic; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import jakarta.transaction.Transactional; +import org.example.app.task.dataaccess.TaskItemEntity; +import org.example.app.task.dataaccess.TaskItemRepository; + +import java.util.Optional; + +@ApplicationScoped +@Named +@Transactional +public class UcFindTaskItem { + + @Inject + TaskItemRepository taskItemRepository; + + @Inject + TaskItemMapper taskItemMapper; + + public TaskItemEto findById(Long itemId) { + Optional item = this.taskItemRepository.findById(itemId); + return item.map(ety -> this.taskItemMapper.toEto(ety)).orElse(null); + } +} diff --git a/backend/src/main/java/org/example/app/task/logic/UcFindTaskList.java b/backend/src/main/java/org/example/app/task/logic/UcFindTaskList.java new file mode 100644 index 0000000..19b34f7 --- /dev/null +++ b/backend/src/main/java/org/example/app/task/logic/UcFindTaskList.java @@ -0,0 +1,50 @@ +package org.example.app.task.logic; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import jakarta.transaction.Transactional; +import org.example.app.task.dataaccess.TaskListEntity; +import org.example.app.task.dataaccess.TaskListRepository; + +import java.util.List; +import java.util.Optional; + +@ApplicationScoped +@Named +@Transactional +public class UcFindTaskList { + + @Inject + TaskListRepository taskListRepository; + + @Inject + TaskListMapper taskListMapper; + @Inject + TaskItemMapper taskItemMapper; + + + public TaskListEto findById(Long itemId) { + Optional item = this.taskListRepository.findById(itemId); + return item.map(ety -> this.taskListMapper.toEto(ety)).orElse(null); + } + + public List findAll() { + List item = this.taskListRepository.findAll(); + return this.taskListMapper.toEtos(item); + } + + + + public TaskListCto findTaskListWithItems(Long listId) { + Optional taskList = this.taskListRepository.findById(listId); + if (taskList.isEmpty()) { + return null; + } + TaskListCto cto = new TaskListCto(); + TaskListEntity taskListEntity = taskList.get(); + cto.setList(this.taskListMapper.toEto(taskListEntity)); + cto.setItems(this.taskItemMapper.toEtos(taskListEntity.getItems())); + return cto; + } +} diff --git a/backend/src/main/java/org/example/app/task/logic/UcSaveTaskItem.java b/backend/src/main/java/org/example/app/task/logic/UcSaveTaskItem.java new file mode 100644 index 0000000..ca489df --- /dev/null +++ b/backend/src/main/java/org/example/app/task/logic/UcSaveTaskItem.java @@ -0,0 +1,25 @@ +package org.example.app.task.logic; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import jakarta.transaction.Transactional; +import org.example.app.task.dataaccess.TaskItemEntity; +import org.example.app.task.dataaccess.TaskItemRepository; + +@ApplicationScoped +@Named +@Transactional +public class UcSaveTaskItem { + + @Inject + TaskItemRepository taskItemRepository; + + @Inject + TaskItemMapper taskItemMapper; + + public void save(TaskItemEto taskItemEto) { + TaskItemEntity taskItemEntity = this.taskItemMapper.toEntity(taskItemEto); + this.taskItemRepository.save(taskItemEntity); + } +} diff --git a/backend/src/main/java/org/example/app/task/logic/UcSaveTaskList.java b/backend/src/main/java/org/example/app/task/logic/UcSaveTaskList.java new file mode 100644 index 0000000..766628c --- /dev/null +++ b/backend/src/main/java/org/example/app/task/logic/UcSaveTaskList.java @@ -0,0 +1,25 @@ +package org.example.app.task.logic; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import jakarta.transaction.Transactional; +import org.example.app.task.dataaccess.TaskListEntity; +import org.example.app.task.dataaccess.TaskListRepository; + +@ApplicationScoped +@Named +@Transactional +public class UcSaveTaskList { + + @Inject + TaskListRepository taskListRepository; + + @Inject + TaskListMapper taskListMapper; + + public void save(TaskListEto taskListEto) { + TaskListEntity taskListEntity = this.taskListMapper.toEntity(taskListEto); + this.taskListRepository.save(taskListEntity); + } +} diff --git a/backend/src/main/java/org/example/app/task/service/TaskService.java b/backend/src/main/java/org/example/app/task/service/TaskService.java new file mode 100644 index 0000000..5eca9f0 --- /dev/null +++ b/backend/src/main/java/org/example/app/task/service/TaskService.java @@ -0,0 +1,122 @@ +package org.example.app.task.service; + +import io.quarkus.runtime.Application; +import jakarta.inject.Inject; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.example.app.task.logic.*; + + +import java.util.List; +import java.util.NoSuchElementException; + +@Path("/task") +public class TaskService { + + @Inject + private UcFindTaskItem ucFindTaskItem; + + @Inject + private UcFindTaskList ucFindTaskList; + + @Inject + private UcDeleteTaskItem ucDeleteTaskItem; + + @Inject + private UcDeleteTaskList ucDeleteTaskList; + + @Inject + private UcSaveTaskItem ucSaveTaskItem; + + @Inject + private UcSaveTaskList ucSaveTaskList; + + @GET + @Path("/item/{id}") + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Retrieve task item", description = "JSUT SOME DESCRIPTION") + @APIResponse(responseCode = "200", description = "Task Item!", content = @Content(mediaType + = MediaType.APPLICATION_JSON, schema = @Schema(implementation = TaskItemEto.class))) + @APIResponse(responseCode = "404", description = "NOT FOUND!") + @APIResponse(responseCode = "500", description = "SERVER ERROR!!!!!!!") + public TaskItemEto fetchTaskItem(@Parameter(description = "Task item id as paramSeter!", required = true, example = "2", schema + = @Schema(type = SchemaType.INTEGER)) @PathParam("id") Long id) { + TaskItemEto eto = this.ucFindTaskItem.findById(id); + if(eto==null) { + throw new NoSuchElementException(); + } + return eto; + } + + @GET + @Path("/list/{id}") + public TaskListEto fetchTaskList(@PathParam("id") Long id) { + TaskListEto eto = this.ucFindTaskList.findById(id); + if(eto==null) { + throw new NoSuchElementException(); + } + return eto; + } + + @GET + @Path("/lists") + public List allLists() { + List eto = this.ucFindTaskList.findAll(); + if(eto==null) { + throw new NoSuchElementException(); + } + return eto; + } + + + @DELETE + @Path("/item/{id}") + @Produces(MediaType.APPLICATION_JSON) + public void deleteTaskItem(@PathParam("id") Long id) { + this.ucDeleteTaskItem.deleteById(id); + } + + @DELETE + @Path("/list/{id}") + @Produces(MediaType.APPLICATION_JSON) + public void deleteTaskList(@PathParam("id") Long id) { + this.ucDeleteTaskList.deleteById(id); + } + + + @POST + @Path("/item") + @Produces(MediaType.APPLICATION_JSON) + public void addOrUpdateTaskItem(TaskItemEto taskItemEto) { + this.ucSaveTaskItem.save(taskItemEto); + } + + @POST + @Path("/list") + @Produces(MediaType.APPLICATION_JSON) + public void addOrUpdateTaskList(TaskListEto taskListEto) { + this.ucSaveTaskList.save(taskListEto); + } +/** + @GET + @Path("/list-with-items/{id}") + public TaskListCto fetchTaskListWithItems(@PathParam("id") Long id) { + TaskListCto cto = this.ucFindTaskList.findTaskListWithItems(id); + if(cto==null) { + throw new NoSuchElementException(); + } + return cto; + } + + */ + + + + +} diff --git a/backend/src/main/resources/META-INF/openapi.yaml b/backend/src/main/resources/META-INF/openapi.yaml deleted file mode 100644 index d193f24..0000000 --- a/backend/src/main/resources/META-INF/openapi.yaml +++ /dev/null @@ -1,248 +0,0 @@ ---- -openapi: 3.0.3 -info: - title: Task API - description: A service to manage tasks - contact: - name: Task API Support - url: http://exampleurl.com/contact - email: techsupport@example.com - version: 1.0.0 -servers: -- url: http://localhost:8080 - description: Auto generated value -- url: http://0.0.0.0:8080 - description: Auto generated value -paths: - /task/item: - post: - tags: - - Task Service - summary: Add or update task item - description: Update a task item or add it as a new item if the id is empty - operationId: saveTaskItem - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/TaskItem' - responses: - "200": - description: Task successfully updated - "201": - description: Task successfully created - "400": - description: Validation error - "500": - description: Server unavailable or a server-side error occurred - /task/item/{id}: - get: - tags: - - Task Service - summary: Fetch task item - description: Fetch a task item - operationId: findTaskItem - parameters: - - name: id - in: path - description: The id of the task item to retrieve - required: true - schema: - type: integer - example: 1 - responses: - "200": - description: Task item - content: - application/json: - schema: - $ref: '#/components/schemas/TaskItem' - "404": - description: Task item not found - "500": - description: Server unavailable or a server-side error occurred - delete: - tags: - - Task Service - summary: Delete task item - description: Delete a task item - operationId: deleteTaskItem - parameters: - - name: id - in: path - description: The id of the task item to delete - required: true - schema: - type: integer - example: 1 - responses: - "204": - description: Task list deleted - "500": - description: Server unavailable or a server-side error occurred - /task/list: - post: - tags: - - Task Service - summary: Create or update task list - description: Update a task list or creates a new one if the id is empty. - operationId: saveTask - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/TaskList' - responses: - "200": - description: Task list successfully updated - "201": - description: Task list successfully created - "400": - description: Validation error - "500": - description: Server unavailable or a server-side error occurred - /task/list-with-items/{id}: - get: - tags: - - Task Service - summary: Fetch task list with tasks - description: Fetch a task list including all of its task items - operationId: findTaskListWithItems - parameters: - - name: id - in: path - description: The id of the task list to retrieve - required: true - schema: - type: integer - example: 1 - responses: - "200": - description: Task list with task items - content: - application/json: - schema: - $ref: '#/components/schemas/TaskListWithItems' - "404": - description: Task list not found - "500": - description: Server unavailable or a server-side error occurred - /task/list/{id}: - get: - tags: - - Task Service - summary: Fetch task list - description: Fetch a task list - operationId: findTaskList - parameters: - - name: id - in: path - description: The id of the task list to retrieve - required: true - schema: - type: integer - example: 1 - responses: - "200": - description: Task list - content: - application/json: - schema: - $ref: '#/components/schemas/TaskList' - "404": - description: Task list not found - "500": - description: Server unavailable or a server-side error occurred - delete: - tags: - - Task Service - summary: Delete task list - description: Deletes an entire task list - operationId: deleteTaskList - parameters: - - name: id - in: path - description: The id of the task list to delete - required: true - schema: - type: integer - example: 1 - responses: - "204": - description: Task list deleted - "201": - description: Task list successfully created - "500": - description: Server unavailable or a server-side error occurred -components: - schemas: - LocalDateTime: - format: date-time - type: string - example: 2022-03-10T12:15:50 - TaskItem: - description: Object that represents a task item - required: - - title - - taskListId - type: object - properties: - id: - format: int64 - type: integer - version: - format: int32 - type: integer - title: - description: The task title or description - pattern: \S - type: string - example: Buy eggs - completed: - description: Whether or not the task is completed - type: boolean - example: false - starred: - description: Whether or not the task has been starred - type: boolean - example: false - taskListId: - format: int64 - description: The id of the task list to which this item belongs - minimum: 1 - type: integer - example: 1 - deadline: - allOf: - - $ref: '#/components/schemas/LocalDateTime' - - description: Until when the task must be completed - TaskList: - description: Object that represents a task list - required: - - title - type: object - properties: - id: - format: int64 - type: integer - version: - format: int32 - type: integer - title: - description: Title of the task list - pattern: \S - type: string - example: Shopping list - TaskListWithItems: - description: Object that represents a task list and its task items - required: - - list - - items - type: object - properties: - list: - $ref: '#/components/schemas/TaskList' - items: - type: array - items: - $ref: '#/components/schemas/TaskItem' diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index ac74aad..f2f4ee4 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -21,5 +21,19 @@ quarkus.http.cors.methods=GET, POST, OPTIONS, DELETE quarkus.rest-client.bored-api.url=https://www.boredapi.com/api/ +# OpenAPI +quarkus.smallrye-openapi.info-title=Task API +%dev.quarkus.smallrye-openapi.info-title=Task API (development) +%test.quarkus.smallrye-openapi.info-title=Task API (test) +quarkus.smallrye-openapi.info-version=1.0.0 +quarkus.smallrye-openapi.info-description=A service to manage tasks +quarkus.smallrye-openapi.info-contact-email=techsupport@example.com +quarkus.smallrye-openapi.info-contact-name=Task API Support +quarkus.smallrye-openapi.info-contact-url=http://exampleurl.com/contact +quarkus.smallrye-openapi.operation-id-strategy=method + +# Generate OpenAPI spec at build-time (can be grabbed and published in schema repository by CI process) +quarkus.smallrye-openapi.store-schema-directory=target/openapi + diff --git a/backend/src/test/java/org/example/app/task/dataaccess/TaskItemRepositoryTest.java b/backend/src/test/java/org/example/app/task/dataaccess/TaskItemRepositoryTest.java new file mode 100644 index 0000000..215b47d --- /dev/null +++ b/backend/src/test/java/org/example/app/task/dataaccess/TaskItemRepositoryTest.java @@ -0,0 +1,45 @@ +package org.example.app.task.dataaccess; + +import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@QuarkusTest +public class TaskItemRepositoryTest extends Assertions { + + @Inject + private TaskItemRepository taskItemRepository; + + @Test + public void testFindById() { + + // given + Long itemId = 11L; + + // when + TaskItemEntity item = this.taskItemRepository.findById(itemId).get(); + + // then + assertThat(item.getTitle()).isEqualTo("Milk"); + } + + @Test + public void testSave() { + + // given + TaskItemEntity entity = new TaskItemEntity(); + entity.setTitle("title"); + entity.setVersion(0); + + // when + TaskItemEntity savedItem = this.taskItemRepository.save(entity); + + // then + assertThat(savedItem.getId()).isEqualTo(1000000L); + assertThat(savedItem.getVersion()).isNotNull(); + } + +} \ No newline at end of file diff --git a/backend/src/test/java/org/example/app/task/dataaccess/TaskListRepositoryTest.java b/backend/src/test/java/org/example/app/task/dataaccess/TaskListRepositoryTest.java new file mode 100644 index 0000000..ec9f8e8 --- /dev/null +++ b/backend/src/test/java/org/example/app/task/dataaccess/TaskListRepositoryTest.java @@ -0,0 +1,39 @@ +package org.example.app.task.dataaccess; + +import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@QuarkusTest +public class TaskListRepositoryTest extends Assertions { + + @Inject + private TaskListRepository taskListRepository; + + @Test + public void testLoadAllTasklists() { + // when + List items = this.taskListRepository.findAll(); + + // then + assertThat(items).isNotEmpty().hasSize(4); + } + + @Test + public void testFindCompletedLists() { + // when + List items = this.taskListRepository.findByCompleted(); + + // then + assertThat(items).isNotEmpty(); + } + + + + +} \ No newline at end of file