Skip to content

Commit

Permalink
feat(backend/shared): add action buttons to tasks bb-362 (#451)
Browse files Browse the repository at this point in the history
* feat(backend/shared): add endpoint to update task bb-362

* feat(backend/shared): add prehandler to check access to task bb-362

* feat(backend/shared): add endpoint to get past users tasks bb-362

* fix(backend): update imports/export for api pre handler type bb-362
  • Loading branch information
marharita08 authored Sep 16, 2024
1 parent 54ba144 commit 15c3ef0
Show file tree
Hide file tree
Showing 20 changed files with 224 additions and 4 deletions.
1 change: 1 addition & 0 deletions apps/backend/src/libs/modules/controller/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export { BaseController } from "./base-controller.module.js";
export {
type APIHandlerOptions,
type APIHandlerResponse,
type APIPreHandler,
} from "./libs/types/types.js";
4 changes: 3 additions & 1 deletion apps/backend/src/modules/tasks/libs/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ const NO_DAYS_THIS_WEEK = 0;

const NO_USER_TASK_DAYS = 0;

export { FULL_WEEK, NO_DAYS_THIS_WEEK, NO_USER_TASK_DAYS };
const STATUS_FIELD = "status";

export { FULL_WEEK, NO_DAYS_THIS_WEEK, NO_USER_TASK_DAYS, STATUS_FIELD };
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ErrorMessage } from "~/libs/enums/enums.js";
import { type APIPreHandler } from "~/libs/modules/controller/controller.js";
import { HTTPCode } from "~/libs/modules/http/http.js";
import { type UserDto } from "~/modules/users/users.js";

import { type TaskService } from "../../task.service.js";
import { TaskError } from "../exceptions/exceptions.js";
import { type TaskUpdateParametersDto } from "../types/types.js";

const checkAccessToTask =
(taskService: TaskService): APIPreHandler =>
async (request) => {
const { params, user } = request;

const taskId = (params as TaskUpdateParametersDto).id;

const task = await taskService.find(taskId);

if ((user as UserDto).id !== task?.userId) {
throw new TaskError({
message: ErrorMessage.FORBIDDEN,
status: HTTPCode.FORBIDDEN,
});
}
};

export { checkAccessToTask };
1 change: 1 addition & 0 deletions apps/backend/src/modules/tasks/libs/hooks/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { checkAccessToTask } from "./check-access-to-task.hook.js";
6 changes: 5 additions & 1 deletion apps/backend/src/modules/tasks/libs/types/types.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
export { type UsersTaskCreateRequestDto } from "./users-task-create-request-dto.type.js";
export { type TaskDto } from "shared";
export {
type TaskDto,
type TaskUpdateParametersDto,
type TaskUpdateRequestDto,
} from "shared";
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { taskUpdateValidationSchema } from "shared";
93 changes: 93 additions & 0 deletions apps/backend/src/modules/tasks/task.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ import { type Logger } from "~/libs/modules/logger/logger.js";
import { type UserDto } from "~/modules/users/users.js";

import { TasksApiPath } from "./libs/enums/enums.js";
import { checkAccessToTask } from "./libs/hooks/hooks.js";
import {
type TaskUpdateParametersDto,
type TaskUpdateRequestDto,
} from "./libs/types/types.js";
import { taskUpdateValidationSchema } from "./libs/validation-schemas/validation-schemas.js";
import { type TaskService } from "./task.service.js";

/*** @swagger
Expand Down Expand Up @@ -65,6 +71,33 @@ class TaskController extends BaseController {
method: "GET",
path: TasksApiPath.CURRENT,
});

this.addRoute({
handler: (options) =>
this.findPastByUserId(
options as APIHandlerOptions<{
user: UserDto;
}>,
),
method: "GET",
path: TasksApiPath.PAST,
});

this.addRoute({
handler: (options) =>
this.update(
options as APIHandlerOptions<{
body: TaskUpdateRequestDto;
params: TaskUpdateParametersDto;
}>,
),
method: "PATCH",
path: TasksApiPath.$ID,
preHandlers: [checkAccessToTask(taskService)],
validation: {
body: taskUpdateValidationSchema,
},
});
}

/**
Expand Down Expand Up @@ -97,6 +130,66 @@ class TaskController extends BaseController {
status: HTTPCode.OK,
};
}

/**
* @swagger
* /tasks/past:
* get:
* description: Returns an array of past users tasks
* security:
* - bearerAuth: []
* responses:
* 200:
* description: Successful operation
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Task"
*/

private async findPastByUserId(
options: APIHandlerOptions<{
user: UserDto;
}>,
): Promise<APIHandlerResponse> {
const { user } = options;

return {
payload: await this.taskService.findPastByUserId(user.id),
status: HTTPCode.OK,
};
}

/**
* @swagger
* /tasks/current:
* get:
* description: updates status of task by id
* security:
* - bearerAuth: []
* responses:
* 200:
* description: Successful operation
* content:
* application/json:
* schema:
* type: object
* $ref: "#/components/schemas/Task"
*/

private async update(
options: APIHandlerOptions<{
body: TaskUpdateRequestDto;
params: TaskUpdateParametersDto;
}>,
): Promise<APIHandlerResponse> {
return {
payload: await this.taskService.update(options.params.id, options.body),
status: HTTPCode.OK,
};
}
}

export { TaskController };
24 changes: 24 additions & 0 deletions apps/backend/src/modules/tasks/task.repository.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { RelationName } from "~/libs/enums/enums.js";
import { type Repository } from "~/libs/types/types.js";

import { STATUS_FIELD } from "./libs/constants/constants.js";
import { TaskStatus } from "./libs/enums/enums.js";
import { TaskEntity } from "./task.entity.js";
import { type TaskModel } from "./task.model.js";
Expand Down Expand Up @@ -114,6 +115,29 @@ class TaskRepository implements Repository {
});
}

public async findPastByUserId(userId: number): Promise<TaskEntity[]> {
const tasks = await this.taskModel
.query()
.withGraphFetched(`[${RelationName.CATEGORY}]`)
.whereIn(STATUS_FIELD, [TaskStatus.COMPLETED, TaskStatus.SKIPPED])
.andWhere({ userId });

return tasks.map((task) => {
return TaskEntity.initialize({
category: task.category.name,
categoryId: task.categoryId,
createdAt: task.createdAt,
description: task.description,
dueDate: task.dueDate,
id: task.id,
label: task.label,
status: task.status,
updatedAt: task.updatedAt,
userId: task.userId,
});
});
}

public async update(
id: number,
payload: Partial<TaskModel>,
Expand Down
6 changes: 6 additions & 0 deletions apps/backend/src/modules/tasks/task.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ class TaskService implements Service {
return tasks.map((task) => task.toObject());
}

public async findPastByUserId(userId: number): Promise<TaskDto[]> {
const tasks = await this.taskRepository.findPastByUserId(userId);

return tasks.map((task) => task.toObject());
}

public async update(
id: number,
payload: Partial<TaskModel>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ErrorMessage } from "~/libs/enums/enums.js";
import { type APIPreHandler } from "~/libs/modules/controller/libs/types/types.js";
import { type APIPreHandler } from "~/libs/modules/controller/controller.js";
import { HTTPCode } from "~/libs/modules/http/http.js";
import { UserError } from "~/modules/users/libs/exceptions/exceptions.js";
import {
Expand Down
3 changes: 3 additions & 0 deletions packages/shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ export {
type TaskDto,
TasksApiPath,
TaskStatus,
type TaskUpdateParametersDto,
type TaskUpdateRequestDto,
taskUpdateValidationSchema,
} from "./modules/tasks/tasks.js";
export {
type EmailDto,
Expand Down
1 change: 1 addition & 0 deletions packages/shared/src/modules/tasks/libs/enums/enums.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { TaskStatus } from "./task-status.enum.js";
export { TaskValidationMessage } from "./task-validation-message.enum.js";
export { TasksApiPath } from "./tasks-api-path.enum.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const TaskValidationMessage = {
INVALID_TYPE:
"Status must be one of the following: Completed, Current, or Skipped",
REQUIRED: "Status field is required",
} as const;

export { TaskValidationMessage };
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const TasksApiPath = {
$ID: "/:id",
CURRENT: "/current",
PAST: "/past",
} as const;

export { TasksApiPath };
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type TaskUpdateParametersDto = {
id: number;
};

export { type TaskUpdateParametersDto };
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { type ValueOf } from "../../../../libs/types/types.js";
import { type TaskStatus } from "../enums/enums.js";

type TaskUpdateRequestDto = {
status: ValueOf<typeof TaskStatus>;
};

export { type TaskUpdateRequestDto };
2 changes: 2 additions & 0 deletions packages/shared/src/modules/tasks/libs/types/types.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export { type TaskDto } from "./task-dto.type.js";
export { type TaskUpdateParametersDto } from "./task-update-parameters-dto.type.js";
export { type TaskUpdateRequestDto } from "./task-update-request-dto.type.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { z } from "zod";

import { TaskStatus, TaskValidationMessage } from "../enums/enums.js";

type TaskUpdateRequestValidationDto = {
status: z.ZodEnum<
[
typeof TaskStatus.COMPLETED,
typeof TaskStatus.CURRENT,
typeof TaskStatus.SKIPPED,
]
>;
};

const taskUpdate = z
.object<TaskUpdateRequestValidationDto>({
status: z.enum(
[TaskStatus.COMPLETED, TaskStatus.CURRENT, TaskStatus.SKIPPED],
{
invalid_type_error: TaskValidationMessage.INVALID_TYPE,
required_error: TaskValidationMessage.REQUIRED,
},
),
})
.required();

export { taskUpdate };
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { taskUpdate } from "./task-update.validation-schema.js";
7 changes: 6 additions & 1 deletion packages/shared/src/modules/tasks/tasks.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
export { TasksApiPath, TaskStatus } from "./libs/enums/enums.js";
export { type TaskDto } from "./libs/types/types.js";
export {
type TaskDto,
type TaskUpdateParametersDto,
type TaskUpdateRequestDto,
} from "./libs/types/types.js";
export { taskUpdate as taskUpdateValidationSchema } from "./libs/validation-schemas/validation-schemas.js";

0 comments on commit 15c3ef0

Please sign in to comment.