diff --git a/server/src/controller/project.controller.ts b/server/src/controller/project.controller.ts index be84de2..c03af9d 100644 --- a/server/src/controller/project.controller.ts +++ b/server/src/controller/project.controller.ts @@ -3,6 +3,7 @@ import { ProjectDto } from "../dto/poject.dto"; import { ProjectService } from "../service/project.service"; const router = Router(); +export const ProjectRoutes = router; export const apiNamespace = "/projects"; @@ -44,10 +45,7 @@ router.put(`${apiNamespace}/:id`, async (req, res) => { const { name } = req.body; const projectId = parseInt(id); - const userProjects = await ProjectService.listAllByUserId(req.userId); - const projectToBeUpdated = userProjects.find( - (project) => project.id === projectId - ); + const projectToBeUpdated = await ProjectService.findProjectFromUserById(req.userId, projectId); if (projectToBeUpdated) { ProjectService.update(projectToBeUpdated, { @@ -66,14 +64,11 @@ router.put(`${apiNamespace}/:id`, async (req, res) => { } }); -router.delete(`${apiNamespace}/:id`, async (req, res, next) => { +router.delete(`${apiNamespace}/:id`, async (req, res) => { const { id } = req.params; const projectId = parseInt(id); - const userProjects = await ProjectService.listAllByUserId(req.userId); - const projecToBeDeleted = userProjects.find( - (project) => project.id === projectId - ); + const projecToBeDeleted = await ProjectService.findProjectFromUserById(req.userId, projectId); if (projecToBeDeleted) { const success = await ProjectService.destroy(projecToBeDeleted); @@ -91,5 +86,3 @@ router.delete(`${apiNamespace}/:id`, async (req, res, next) => { }); } }); - -export const ProjectRoutes = router; diff --git a/server/src/controller/task.controller.ts b/server/src/controller/task.controller.ts new file mode 100644 index 0000000..19957c9 --- /dev/null +++ b/server/src/controller/task.controller.ts @@ -0,0 +1,171 @@ +import { Router } from "express"; +import { ProjectDto } from "../dto/poject.dto"; +import { ProjectService } from "../service/project.service"; +import { TaskService } from "../service/task.service"; + +const router = Router(); +export const TaskRoutes = router; + +export const apiNamespace = "/projects/:projectId"; + +router.get(`${apiNamespace}/tasks`, async (req, res) => { + let { projectId } = req.params; + const parsedProjctId = parseInt(projectId); + + const project = await ProjectService.findProjectFromUserById( + req.userId, + parsedProjctId + ); + + if (!project) { + res.status(404).json({ + error: "Project not found", + }); + return; + } + + const tasks: TaskDto[] = ( + await TaskService.findByProjectId(parsedProjctId) + ).map((task) => ({ + id: task.id, + description: task.description, + createdAt: task.createdAt, + finishedAt: task.finishedAt, + })); + + res.json({ + data: tasks, + }); +}); + +router.post(`${apiNamespace}/tasks`, async (req, res) => { + let { projectId } = req.params; + const parsedProjctId = parseInt(projectId); + + const project = await ProjectService.findProjectFromUserById( + req.userId, + parsedProjctId + ); + + if (!project) { + res.status(404).json(); + return; + } + + const task = await TaskService.create(project, { + description: req.body.description, + }); + + const taskDto: TaskDto = { + id: task.id, + createdAt: task.createdAt, + description: task.description, + finishedAt: task.finishedAt, + }; + + res.json({ + data: taskDto, + }); +}); + +router.put(`${apiNamespace}/tasks/:id`, async (req, res) => { + let { projectId, id } = req.params; + const { description }: UpdateTaskDto = req.body; + + const parsedProjctId = parseInt(projectId); + + const project = await ProjectService.findProjectFromUserById( + req.userId, + parsedProjctId + ); + + if (!project) { + res.status(404).json(); + return; + } + + const task = await TaskService.findByProjectIdAndTaskId( + parsedProjctId, + parseInt(id) + ); + + await TaskService.update(task, { description }); + + const taskDto: TaskDto = { + id: task.id, + createdAt: task.createdAt, + description: task.description, + finishedAt: task.finishedAt, + }; + + res.json({ + data: taskDto, + }); +}); + +router.delete(`${apiNamespace}/tasks/:id`, async (req, res) => { + let { projectId, id } = req.params; + const parsedProjctId = parseInt(projectId); + + const project = await ProjectService.findProjectFromUserById( + req.userId, + parsedProjctId + ); + + if (!project) { + res.status(404).json(); + return; + } + + const task = await TaskService.findByProjectIdAndTaskId( + parsedProjctId, + parseInt(id) + ); + + if (!task) { + res.status(404).json(); + return; + } + + const success = await TaskService.destroy(task); + + if (success) { + res.status(204).json(); + } else { + res.status(504).json(); + } +}); + +router.get(`${apiNamespace}/tasks/:id/finish`, async (req, res) => { + const { projectId, id } = req.params; + const parsedProjctId = parseInt(projectId); + + const project = await ProjectService.findProjectFromUserById( + req.userId, + parsedProjctId + ); + + + const task = await TaskService.findByProjectIdAndTaskId( + parsedProjctId, + parseInt(id) + ); + + if (!project || !task) { + res.status(404).json(); + return; + } + + const result = await TaskService.finish(task); + + const resultDto: TaskDto = { + id: result.id, + createdAt: result.createdAt, + description: result.description, + finishedAt: result.finishedAt, + }; + + res.json({ + data: resultDto, + }); +}); diff --git a/server/src/controller/user.controller.ts b/server/src/controller/user.controller.ts index 6512a4f..50de8a8 100644 --- a/server/src/controller/user.controller.ts +++ b/server/src/controller/user.controller.ts @@ -4,6 +4,7 @@ import { AuthService } from '../service/auth.service'; import { UserService } from '../service/user.service'; const router = Router(); +export const UserRoutes = router; export const apiNamespace = '/users' @@ -64,5 +65,3 @@ router.delete(`${apiNamespace}/sign_out`, async (req, res) => { }) } }) - -export const UserRoutes = router; \ No newline at end of file diff --git a/server/src/dto/newTaskDto.ts b/server/src/dto/newTask.dto.ts similarity index 100% rename from server/src/dto/newTaskDto.ts rename to server/src/dto/newTask.dto.ts diff --git a/server/src/dto/task.dto.ts b/server/src/dto/task.dto.ts new file mode 100644 index 0000000..20406b6 --- /dev/null +++ b/server/src/dto/task.dto.ts @@ -0,0 +1,6 @@ +type TaskDto = { + id: number; + description: string; + createdAt: Date; + finishedAt?: Date; +} \ No newline at end of file diff --git a/server/src/dto/updateTask.dto.ts b/server/src/dto/updateTask.dto.ts new file mode 100644 index 0000000..e01e556 --- /dev/null +++ b/server/src/dto/updateTask.dto.ts @@ -0,0 +1,3 @@ +type UpdateTaskDto = { + description?: string; +} \ No newline at end of file diff --git a/server/src/entity/task.entity.ts b/server/src/entity/task.entity.ts index 283d304..7a3994a 100644 --- a/server/src/entity/task.entity.ts +++ b/server/src/entity/task.entity.ts @@ -24,7 +24,10 @@ export class Task { @Column({ type: "timestamptz", nullable: true }) finishedAt?: Date; - @ManyToOne((_type) => Project, (project) => project.tasks) + @ManyToOne((_type) => Project, (project) => project.tasks,{ + onUpdate: "CASCADE", + onDelete: "CASCADE", + }) @JoinColumn() @IsNotEmpty() project: Project; diff --git a/server/src/index.ts b/server/src/index.ts index 9709e3d..6f7c882 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -1,8 +1,9 @@ -import "reflect-metadata" import * as dotenv from 'dotenv'; import * as express from 'express'; -import { AppDataSource } from "./infra/dataSource"; +import "reflect-metadata"; import { ProjectRoutes, UserRoutes } from "./controller"; +import { TaskRoutes } from "./controller/task.controller"; +import { AppDataSource } from "./infra/dataSource"; import { RedisConnection } from "./infra/redis"; import { sessionMiddleware } from "./middleware/session.middleware"; @@ -15,6 +16,7 @@ app.use(sessionMiddleware) app.use(UserRoutes) app.use(ProjectRoutes) +app.use(TaskRoutes) const startApp = async () => { console.log('[redis]: connecting') diff --git a/server/src/service/project.service.ts b/server/src/service/project.service.ts index 6e655fb..c97eb9a 100644 --- a/server/src/service/project.service.ts +++ b/server/src/service/project.service.ts @@ -48,9 +48,21 @@ async function update( return !!updateResult; } +async function findProjectFromUserById( + userId: User["id"], + projectId: Project["id"] +): Promise { + const query = projectRepository.createQueryBuilder(); + query.where('"userId" = :userId', { userId }); + query.andWhere('"id" = :projectId', { projectId }); + + return query.getOne(); +} + export const ProjectService = { create, listAllByUserId, destroy, update, + findProjectFromUserById }; diff --git a/server/src/service/task.service.ts b/server/src/service/task.service.ts index 6b0427a..27cf9ea 100644 --- a/server/src/service/task.service.ts +++ b/server/src/service/task.service.ts @@ -1,4 +1,4 @@ -import { NewTaskDto } from "../dto/newTaskDto"; +import { NewTaskDto } from "../dto/newTask.dto"; import { Project } from "../entity/project.entity"; import { Task } from "../entity/task.entity"; import { taskRepository } from "../repository/task.repository"; @@ -18,20 +18,40 @@ async function destroy(task: Task) { throw new Error("You can't destroy a finished task"); } - return taskRepository.delete(task); + const result = await taskRepository.delete({ + id: task.id, + }); + return !!result.affected; } -async function create(newTaskDto: NewTaskDto, project: Project) { +async function create(project: Project, newTaskDto: NewTaskDto) { const task = new Task(); task.description = newTaskDto.description; task.project = project; + task.createdAt = new Date(); return taskRepository.save(task); } +async function findByProjectId(projectId: Project["id"]): Promise { + return await taskRepository.findBy({ project: { id: projectId } }); +} + +async function findByProjectIdAndTaskId (projectId: Project['id'], taskId: Task["id"]): Promise { + return await taskRepository.findOneBy({ id: taskId, project: { id: projectId } }); +} + +async function update(task: Task, updateTaskDto: UpdateTaskDto): Promise { + taskRepository.merge(task, updateTaskDto); + return taskRepository.save(task); +} + export const TaskService = { finish, destroy, create, + update, + findByProjectId, + findByProjectIdAndTaskId, };