Skip to content

feat(tasks): [#8] Implement task CRUD API endpoints#22

Merged
Zafar7645 merged 7 commits intomainfrom
feature/8-task-api
Mar 28, 2026
Merged

feat(tasks): [#8] Implement task CRUD API endpoints#22
Zafar7645 merged 7 commits intomainfrom
feature/8-task-api

Conversation

@Zafar7645
Copy link
Copy Markdown
Owner

@Zafar7645 Zafar7645 commented Mar 14, 2026

Closes #8 and #21

Summary by CodeRabbit

  • New Features
    • Board columns: CRUD endpoints with automatic ordering and project relation
    • Tasks: CRUD endpoints with automatic ordering and column relation
    • DTOs and validation for creating/updating columns and tasks
  • Behavior Changes
    • Route ID parameters now validated and parsed as integers
  • Security
    • Access controls: users can only access/modify their own projects, columns, and tasks
  • Tests
    • Added unit tests for board columns and tasks features

- Define BoardColumn and Task entities strictly mapping to the SQL schema
- Create BoardColumnsModule with full CRUD and project ownership validation
- Create TasksModule with full CRUD, column validation, and auto-ordering
- Protect all new endpoints using JwtAuthGuard
- Implement ParseIntPipe for all ID parameters to match SERIAL database columns
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 14, 2026

Important

Review skipped

This PR was authored by the user configured for CodeRabbit reviews. CodeRabbit does not review PRs authored by this user. It's recommended to use a dedicated user account to post CodeRabbit review feedback.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f46ec2d6-61be-4879-9b84-206645e71b75

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

Adds BoardColumns and Tasks features: new modules, controllers (JWT-protected), services with ownership checks and transactional ordering, DTOs, TypeORM entities, unit tests; updates Project entity (nullable description, boardColumns relation), uses ParseIntPipe in ProjectsController, and registers new modules in AppModule.

Changes

Cohort / File(s) Summary
App wiring
apps/backend/src/app.module.ts
Register TasksModule and BoardColumnsModule; update ProjectsModule import path to alias.
BoardColumns module
apps/backend/src/board-columns/board-columns.module.ts, apps/backend/src/board-columns/board-columns.controller.ts, apps/backend/src/board-columns/board-columns.service.ts
New module with JWT-protected CRUD controller and service; service enforces project ownership, uses transactions and pessimistic locks for ordered inserts, and exposes standard CRUD methods.
BoardColumns DTOs & entity
apps/backend/src/board-columns/dto/create-board-column.dto.ts, apps/backend/src/board-columns/dto/update-board-column.dto.ts, apps/backend/src/board-columns/entities/board-column.entity.ts
Add Create/Update DTOs with validation; add BoardColumn TypeORM entity with relations to Project and Task.
BoardColumns tests
apps/backend/src/board-columns/board-columns.controller.spec.ts, apps/backend/src/board-columns/board-columns.service.spec.ts
Add basic unit tests ensuring controller/service can be instantiated.
Tasks module
apps/backend/src/tasks/tasks.module.ts, apps/backend/src/tasks/tasks.controller.ts, apps/backend/src/tasks/tasks.service.ts
New module with JWT-protected CRUD controller and service; service enforces column/project ownership, transactionally assigns task ordering, and handles moves between columns (locks, transfers).
Tasks DTOs & entity
apps/backend/src/tasks/dto/create-task.dto.ts, apps/backend/src/tasks/dto/update-task.dto.ts, apps/backend/src/tasks/entities/task.entity.ts
Add Create/Update DTOs with validation; add Task TypeORM entity with relation to BoardColumn and cascade delete.
Tasks tests
apps/backend/src/tasks/tasks.controller.spec.ts, apps/backend/src/tasks/tasks.service.spec.ts
Add basic unit tests ensuring controller/service can be instantiated.
Projects updates
apps/backend/src/projects/entities/project.entity.ts, apps/backend/src/projects/projects.controller.ts
Add boardColumns one-to-many relation; change description type to nullable; use ParseIntPipe for route id params in controller.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Controller
    participant JwtAuthGuard
    participant Service
    participant Database

    Client->>Controller: POST /board-columns (Authorization: Bearer ...)
    Controller->>JwtAuthGuard: validate token
    JwtAuthGuard-->>Controller: attach user (userId)
    Controller->>Service: create(createDto, userId)
    Service->>Database: SELECT Project FOR UPDATE (pessimistic_write)
    Database-->>Service: Project row
    Service->>Database: SELECT max(order) FROM board_columns WHERE project_id=...
    Database-->>Service: last order
    Service->>Database: INSERT INTO board_columns (...)
    Database-->>Service: created BoardColumn
    Service-->>Controller: BoardColumn
    Controller-->>Client: 201 Created + JSON
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

feature

Poem

🐰 I hopped through DTOs, locks, and tests tonight,
I nudged tasks, stacked columns, kept orders tight,
Guards snugged each route, transactions held,
Projects linked to boards, all edges welded,
A carrot cheer — the backlog takes flight. 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly describes the main change: implementing task CRUD API endpoints as per issue #8. It accurately reflects the primary work in the changeset.
Linked Issues check ✅ Passed All acceptance criteria from issue #8 are met: TasksModule created, POST/PATCH/DELETE endpoints implemented, endpoints protected with JWT auth, and permission validation enforced through service-level access checks.
Out of Scope Changes check ✅ Passed Changes to ProjectsController (ParseIntPipe updates) and Project entity (boardColumns relationship) are supporting infrastructure for the tasks feature and directly enable the new functionality, not out-of-scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (2)
apps/backend/src/tasks/dto/create-task.dto.ts (1)

16-18: Consider adding @IsPositive() to reject invalid IDs earlier.

While verifyColumnAccess in the service will catch non-existent columns, adding @IsPositive() would fail-fast at validation for columnId <= 0, avoiding an unnecessary database lookup.

♻️ Proposed enhancement
-import { IsNotEmpty, IsOptional, IsString, IsNumber } from 'class-validator';
+import { IsNotEmpty, IsOptional, IsString, IsNumber, IsPositive } from 'class-validator';

 export class CreateTaskDto {
   `@IsString`()
   `@IsNotEmpty`()
   title: string;

   `@IsString`()
   `@IsOptional`()
   description?: string;

   `@IsNumber`()
   `@IsOptional`()
   order?: number;

   `@IsNumber`()
+  `@IsPositive`()
   `@IsNotEmpty`()
   columnId: number;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/backend/src/tasks/dto/create-task.dto.ts` around lines 16 - 18, Add
fast-fail validation to the DTO by decorating the columnId property with
`@IsPositive`() so invalid IDs (<= 0) are rejected before service calls; update
the CreateTaskDto's columnId field (which currently has `@IsNumber`() and
`@IsNotEmpty`()) to also include `@IsPositive`() to prevent unnecessary
verifyColumnAccess DB lookups.
apps/backend/src/tasks/tasks.service.ts (1)

77-83: Stub implementations need completion or removal.

findAll() and findOne() return placeholder strings instead of actual data. If these endpoints are exposed via the controller, they will return unexpected responses.

Should I help implement these methods with proper query logic and access verification, or should they be removed if not required by the current scope?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/backend/src/tasks/tasks.service.ts` around lines 77 - 83, Replace the
placeholder returns in TasksService.findAll and TasksService.findOne with real
data operations: use the service's data access layer (e.g., injected
repository/prisma client used elsewhere in this service) to query tasks, apply
request-scoped filters (ownerId, teamId, permissions) and paging/ordering for
findAll, and validate access/throw NotFound/Forbidden for findOne when the task
doesn't exist or the caller lacks access; if these methods are not needed,
remove the controller routes that call findAll/findOne or delete the methods
entirely to avoid exposing stub responses.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/backend/src/board-columns/board-columns.controller.spec.ts`:
- Around line 9-12: The test registers the concrete BoardColumnsService which
depends on TypeORM repositories and causes module compilation to fail; instead,
in the Test.createTestingModule call provide a mocked service using { provide:
BoardColumnsService, useValue: mockBoardColumnsService } (replace the providers:
[BoardColumnsService] entry), where mockBoardColumnsService is an object with
jest.fn() stubs for the methods the BoardColumnsController tests exercise (e.g.,
the controller's create/find/update/delete handlers); this keeps the test
isolated from repositories and allows the module to compile when instantiating
BoardColumnsController.

In `@apps/backend/src/board-columns/board-columns.controller.ts`:
- Around line 43-46: The findOne controller method currently calls
boardColumnsService.findOne(id) without user context so ownership checks can't
run; update the BoardColumnsController.findOne to accept the request user (e.g.,
add `@Req`() req: Request or `@User`() user param) and pass req.user.userId into the
service call (change to boardColumnsService.findOne(id, userId)); ensure the
service method signature (boardColumnsService.findOne) is updated to accept and
enforce the userId for authorization if not already implemented.

In `@apps/backend/src/board-columns/board-columns.service.spec.ts`:
- Around line 7-12: The test fails because BoardColumnsService depends on two
injected repositories (BoardColumn and Project); update the
Test.createTestingModule setup to provide mocks for those repositories (use the
Nest testing pattern with getRepositoryToken(BoardColumn) and
getRepositoryToken(Project) or equivalent providers) so dependency injection can
resolve them, then retrieve BoardColumnsService as before; ensure the mock
providers are included alongside BoardColumnsService in the module's providers
array to prevent compile-time resolution errors.

In `@apps/backend/src/board-columns/board-columns.service.ts`:
- Around line 56-63: The current order assignment in the create flow (reading
createDto.order and using columnsRepository.findOne to fetch the lastColumn) is
race-prone under concurrent creates; fix by making order allocation atomic —
either wrap the create logic in a DB transaction and acquire a lock on the
project's columns (e.g., select the project's columns with FOR UPDATE before
computing lastColumn.order and inserting) in the service method that handles
creation (the method that reads createDto and calls columnsRepository.findOne),
or add a unique constraint on (project_id, order) and implement a small retry
loop that catches unique-constraint violations and recomputes the next order
before retrying the insert; update the board-columns service create flow (where
createDto and columnsRepository are used) to use one of these approaches.
- Around line 77-79: The findOne method in BoardColumnsService is still a
scaffold that returns a string; replace it with the real lookup that fetches the
BoardColumn by id, enforces ownership using the same access-check path as
update/delete (reuse the existing check/permission logic used in update() and
remove()), and throw/not-found or forbidden errors as appropriate; ensure it
returns the actual entity (or DTO) instead of a string and update the controller
call to pass req.user.userId into findOne so ownership can be validated.

In `@apps/backend/src/board-columns/dto/create-board-column.dto.ts`:
- Around line 8-14: Replace the loose numeric validators on the DTO with
integer-specific validators: change the decorators on the order and projectId
properties in CreateBoardColumnDto from `@IsNumber`() to `@IsInt`(), import `@IsInt`
and `@Min` from class-validator, add `@Min`(0) on order (since it’s optional) and
`@Min`(1) on projectId to enforce positive IDs, and update the imports to remove
`@IsNumber` and include `@IsInt` and `@Min` so runtime validation matches the INT DB
schema.

In `@apps/backend/src/tasks/entities/task.entity.ts`:
- Around line 20-21: The Task entity's description column is declared nullable
but the property is typed as string; change the property type for description to
string | null to match the Column({ nullable: true }) decorator and
strictNullChecks. Locate the description property in the Task entity (the
`@Column`({ nullable: true }) description declaration) and update its TypeScript
type to string | null; ensure any usages handle possible null values or add
appropriate null-checks where used.

In `@apps/backend/src/tasks/tasks.controller.spec.ts`:
- Around line 9-12: The test currently instantiates the real TasksService which
requires repository dependencies; change the Test.createTestingModule setup to
provide a mocked TasksService instead of the real provider: in the module config
replace providers: [TasksService] with a provider object for TasksService using
useValue (or useClass) that supplies mocked method implementations used by
TasksController tests (e.g., mock methods like create, findAll, etc.), then
retrieve the controller from the compiled TestingModule as before so controller
unit tests run without injecting Task/BoardColumn repositories.

In `@apps/backend/src/tasks/tasks.controller.ts`:
- Around line 31-39: The findAll and findOne controller methods currently don't
scope queries to the authenticated user; update findAll and findOne to accept
the request object via `@Request`() (extract userId from req.user) and pass userId
into tasksService.findAll(...) and tasksService.findOne(...), and then update
the corresponding service methods (tasksService.findAll and
tasksService.findOne) to require a userId parameter and enforce
ownership/validation (reuse the same pattern as verifyTaskAccess used by
update/remove to check project/task ownership and throw if not authorized) so
controllers only return tasks for the authenticated user.

In `@apps/backend/src/tasks/tasks.service.spec.ts`:
- Around line 7-12: The test fails because TasksService depends on two injected
repositories (the `@InjectRepository`(Task) and `@InjectRepository`(BoardColumn)
injections used by TasksService) which are not provided in the
Test.createTestingModule providers; add provider mocks for those repositories
using getRepositoryToken(Task) and getRepositoryToken(BoardColumn) and supply
simple mock objects (e.g., objects with jest.fn() stubs for methods used by
TasksService) when building the testing module before calling module.compile();
update the providers array to include { provide: getRepositoryToken(Task),
useValue: taskRepoMock } and { provide: getRepositoryToken(BoardColumn),
useValue: boardColumnRepoMock } so module.get<TasksService>(TasksService) can
resolve successfully.

---

Nitpick comments:
In `@apps/backend/src/tasks/dto/create-task.dto.ts`:
- Around line 16-18: Add fast-fail validation to the DTO by decorating the
columnId property with `@IsPositive`() so invalid IDs (<= 0) are rejected before
service calls; update the CreateTaskDto's columnId field (which currently has
`@IsNumber`() and `@IsNotEmpty`()) to also include `@IsPositive`() to prevent
unnecessary verifyColumnAccess DB lookups.

In `@apps/backend/src/tasks/tasks.service.ts`:
- Around line 77-83: Replace the placeholder returns in TasksService.findAll and
TasksService.findOne with real data operations: use the service's data access
layer (e.g., injected repository/prisma client used elsewhere in this service)
to query tasks, apply request-scoped filters (ownerId, teamId, permissions) and
paging/ordering for findAll, and validate access/throw NotFound/Forbidden for
findOne when the task doesn't exist or the caller lacks access; if these methods
are not needed, remove the controller routes that call findAll/findOne or delete
the methods entirely to avoid exposing stub responses.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8240ef97-d971-4a51-8de9-43405d16c44b

📥 Commits

Reviewing files that changed from the base of the PR and between 53e75a3 and 6f2131b.

📒 Files selected for processing (19)
  • apps/backend/src/app.module.ts
  • apps/backend/src/board-columns/board-columns.controller.spec.ts
  • apps/backend/src/board-columns/board-columns.controller.ts
  • apps/backend/src/board-columns/board-columns.module.ts
  • apps/backend/src/board-columns/board-columns.service.spec.ts
  • apps/backend/src/board-columns/board-columns.service.ts
  • apps/backend/src/board-columns/dto/create-board-column.dto.ts
  • apps/backend/src/board-columns/dto/update-board-column.dto.ts
  • apps/backend/src/board-columns/entities/board-column.entity.ts
  • apps/backend/src/projects/entities/project.entity.ts
  • apps/backend/src/projects/projects.controller.ts
  • apps/backend/src/tasks/dto/create-task.dto.ts
  • apps/backend/src/tasks/dto/update-task.dto.ts
  • apps/backend/src/tasks/entities/task.entity.ts
  • apps/backend/src/tasks/tasks.controller.spec.ts
  • apps/backend/src/tasks/tasks.controller.ts
  • apps/backend/src/tasks/tasks.module.ts
  • apps/backend/src/tasks/tasks.service.spec.ts
  • apps/backend/src/tasks/tasks.service.ts

Comment thread apps/backend/src/board-columns/board-columns.controller.spec.ts
Comment thread apps/backend/src/board-columns/board-columns.controller.ts
Comment thread apps/backend/src/board-columns/board-columns.service.spec.ts
Comment thread apps/backend/src/board-columns/board-columns.service.ts Outdated
Comment thread apps/backend/src/board-columns/board-columns.service.ts Outdated
Comment thread apps/backend/src/board-columns/dto/create-board-column.dto.ts Outdated
Comment thread apps/backend/src/tasks/entities/task.entity.ts Outdated
Comment thread apps/backend/src/tasks/tasks.controller.spec.ts
Comment thread apps/backend/src/tasks/tasks.controller.ts
Comment thread apps/backend/src/tasks/tasks.service.spec.ts
@Zafar7645 Zafar7645 moved this to In Progress in SyncUp Development Plan Mar 27, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
apps/backend/src/tasks/tasks.service.ts (1)

111-113: Use an explicit undefined check for columnId.

Line 111 uses a truthy check. Prefer updateTaskDto.columnId !== undefined so the access-validation branch is explicit and robust.

Suggested fix
-    if (updateTaskDto.columnId && updateTaskDto.columnId !== task.columnId) {
+    if (
+      updateTaskDto.columnId !== undefined &&
+      updateTaskDto.columnId !== task.columnId
+    ) {
       await this.verifyColumnAccess(updateTaskDto.columnId, userId);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/backend/src/tasks/tasks.service.ts` around lines 111 - 113, The current
truthy check on updateTaskDto.columnId should be replaced with an explicit
undefined check to avoid skipping validation for valid falsy values (e.g., 0);
in the TasksService updateTask method, change the condition that calls
verifyColumnAccess to use updateTaskDto.columnId !== undefined &&
updateTaskDto.columnId !== task.columnId so the access validation runs only when
columnId is explicitly provided and different from task.columnId.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/backend/src/board-columns/board-columns.service.ts`:
- Around line 53-62: Move the ownership/access check into the same DB
transaction after acquiring the pessimistic lock to eliminate the TOCTOU window:
inside the async callback passed to columnsRepository.manager.transaction (where
transactionalManager.createQueryBuilder(Project,
'project').setLock('pessimistic_write')... is used), call verifyProjectAccess
(or inline its logic) against the locked project and, importantly, verify the
result of getOne() is not null — throw a clear NotFound or Forbidden error if
the project was deleted or the user lacks access before proceeding with column
creation in create.
- Around line 94-98: The update method allows changing projectId via
columnsRepository.merge without verifying access to the target project;
update(id, UpdateBoardColumnDto, userId) should either ignore projectId or
explicitly validate the user's access to the new project before saving: if
updateDto.projectId is present, call or add a permission check (e.g.,
verifyProjectAccess(projectId, userId) or fetch the target project/membership)
and throw if unauthorized, otherwise remove projectId from the merged DTO so
columnsRepository.save cannot move the column; keep references to update,
verifyColumnAccess, columnsRepository.merge, UpdateBoardColumnDto and ensure
unauthorized attempts are rejected.

In `@apps/backend/src/tasks/tasks.service.ts`:
- Around line 108-117: In update(id, updateTaskDto, userId) (which calls
verifyTaskAccess and possibly verifyColumnAccess, then
tasksRepository.merge/save), detect when updateTaskDto.columnId is different
from task.columnId and updateTaskDto.order is null/undefined, then compute and
assign a destination order before saving: query the target column's max order
(e.g. via tasksRepository.createQueryBuilder or repository methods) and set
updateTaskDto.order = maxOrder + 1 (or 0 if none) so the moved task is appended
and avoids duplicate/unstable ordering; keep existing behavior when an explicit
order is provided.
- Around line 87-101: The findAll method (tasks.service.ts -> findAll) verifies
access but fails to filter by the provided columnId, returning tasks from all
columns for the user; modify the tasksRepository.find call to include the column
id in the where clause (e.g., add column: { id: columnId, project: { userId } }
or set columnId: columnId) so the query restricts results to the requested
column while keeping the existing verifyColumnAccess call.
- Around line 57-67: The authorization check in create (verifyColumnAccess)
happens before the transaction and can be stale; re-check ownership inside the
transaction after acquiring the pessimistic_write lock. Inside the
columnsRepository.manager.transaction callback (the async transactionalManager
block and the createQueryBuilder that locks BoardColumn), fetch the column/owner
(or call a transaction-aware variant of verifyColumnAccess) and validate that
ownerId === userId, throwing the same authorization error if it no longer
matches; keep or remove the pre-transaction check as you prefer, but ensure the
authoritative check occurs inside the transactionalManager scope (referencing
create, verifyColumnAccess, columnsRepository.manager.transaction,
transactionalManager, and the BoardColumn lock).

---

Nitpick comments:
In `@apps/backend/src/tasks/tasks.service.ts`:
- Around line 111-113: The current truthy check on updateTaskDto.columnId should
be replaced with an explicit undefined check to avoid skipping validation for
valid falsy values (e.g., 0); in the TasksService updateTask method, change the
condition that calls verifyColumnAccess to use updateTaskDto.columnId !==
undefined && updateTaskDto.columnId !== task.columnId so the access validation
runs only when columnId is explicitly provided and different from task.columnId.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e533ad83-4dea-4bb1-a60f-8843d0d81f47

📥 Commits

Reviewing files that changed from the base of the PR and between 6f2131b and 727f900.

📒 Files selected for processing (8)
  • apps/backend/src/board-columns/board-columns.controller.ts
  • apps/backend/src/board-columns/board-columns.service.ts
  • apps/backend/src/board-columns/dto/create-board-column.dto.ts
  • apps/backend/src/projects/entities/project.entity.ts
  • apps/backend/src/tasks/dto/create-task.dto.ts
  • apps/backend/src/tasks/entities/task.entity.ts
  • apps/backend/src/tasks/tasks.controller.ts
  • apps/backend/src/tasks/tasks.service.ts
✅ Files skipped from review due to trivial changes (2)
  • apps/backend/src/board-columns/dto/create-board-column.dto.ts
  • apps/backend/src/tasks/entities/task.entity.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/backend/src/projects/entities/project.entity.ts
  • apps/backend/src/tasks/dto/create-task.dto.ts
  • apps/backend/src/tasks/tasks.controller.ts
  • apps/backend/src/board-columns/board-columns.controller.ts

Comment thread apps/backend/src/board-columns/board-columns.service.ts
Comment thread apps/backend/src/board-columns/board-columns.service.ts
Comment thread apps/backend/src/tasks/tasks.service.ts
Comment thread apps/backend/src/tasks/tasks.service.ts
Comment thread apps/backend/src/tasks/tasks.service.ts
Comment thread apps/backend/src/tasks/tasks.service.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/backend/src/tasks/tasks.service.ts`:
- Around line 114-115: Replace the truthy check on updateTaskDto.columnId with
an explicit undefined presence check so numeric or falsy IDs (e.g., 0) are
validated; specifically change the condition used before calling
verifyColumnAccess to check updateTaskDto.columnId !== undefined &&
updateTaskDto.columnId !== task.columnId and then await
this.verifyColumnAccess(updateTaskDto.columnId, userId) as before.
- Around line 111-128: The update function has a race condition when assigning a
destination `order` because you read the max order with tasksRepository.findOne
and later save in a separate flow; fix by performing the read-and-write
atomically inside a DB transaction and obtain a locking read (e.g. SELECT
MAX(order) FOR UPDATE or lock the destination column's task set) so concurrent
updates serialize; implement this inside update (or a helper called by update)
using TypeORM's transaction/queryRunner or manager to call verifyColumnAccess
and then query the max order with a locking query and set updateTaskDto.order
before calling repository.save within the same transaction so order assignment
is deterministic and race-free.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0f66bd5c-f1ef-475f-875c-c0a7253e27e5

📥 Commits

Reviewing files that changed from the base of the PR and between 727f900 and ef5c294.

📒 Files selected for processing (3)
  • apps/backend/src/board-columns/board-columns.service.ts
  • apps/backend/src/board-columns/dto/update-board-column.dto.ts
  • apps/backend/src/tasks/tasks.service.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/backend/src/board-columns/dto/update-board-column.dto.ts
  • apps/backend/src/board-columns/board-columns.service.ts

Comment thread apps/backend/src/tasks/tasks.service.ts
Comment thread apps/backend/src/tasks/tasks.service.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/backend/src/tasks/tasks.service.ts`:
- Around line 111-153: The update flow currently reads the Task via
verifyTaskAccess() before starting the transaction and then merges/saves that
stale task inside the transaction; re-read and lock the source task inside the
transaction to avoid stale writes. Inside the transaction callback (where you
use transactionalManager, targetColumn, taskRepo), use transactionalManager to
fetch the source Task again with a pessimistic write lock (e.g., via
createQueryBuilder(Task).setLock('pessimistic_write').where id = ... or
transactionalManager.findOne with lock) and validate it exists and still belongs
to the user before merging updateTaskDto and calling taskRepo.save; stop using
the pre-fetched outer task variable for the transactional save. Ensure you
handle not-found/forbidden cases after the re-read.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e6ce8370-4743-4d2f-8570-90bf3b38a5d9

📥 Commits

Reviewing files that changed from the base of the PR and between ef5c294 and e588e9d.

📒 Files selected for processing (1)
  • apps/backend/src/tasks/tasks.service.ts

Comment thread apps/backend/src/tasks/tasks.service.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
apps/backend/src/tasks/tasks.service.ts (1)

113-118: ⚠️ Potential issue | 🔴 Critical

The stale snapshot pattern still exists in update().

The move branch now locks the source task, but Line 113 still captures a pre-transaction snapshot that drives the branch at Lines 117-118 and the direct save at Lines 174-175. If another request moves the task or updates a different field in between, this path can overwrite newer state and even revert columnId/order, because both are persisted on Task (apps/backend/src/tasks/entities/task.entity.ts:24-39). Make the transaction's locked reread authoritative for both branches.

Also applies to: 173-175

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/backend/src/tasks/tasks.service.ts` around lines 113 - 118, The update()
method currently uses the pre-transaction task snapshot from
verifyTaskAccess(id, userId) to decide the "move" branch and to later save,
causing stale-write overwrites; change logic so the transaction's locked reread
is authoritative: move the branch decision and all reads of task state into the
transaction (re-call / re-query the task under the same transaction lock—eg. the
SELECT FOR UPDATE read you already perform) and base both the "is column
changed" check and the final tasksRepository.save on that reloaded, locked
entity (not the earlier verifyTaskAccess result); ensure verifyTaskAccess is
only used for permission checks and not as the source of truth for state, and
update usages of tasksRepository.save in update() to persist the
transaction-local task instance.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/backend/src/tasks/tasks.service.ts`:
- Around line 61-65: The query uses leftJoinAndSelect('column.project',
'project') while acquiring a pessimistic write lock on BoardColumn; because
BoardColumn.projectId is non-nullable, replace leftJoinAndSelect with
innerJoinAndSelect to perform an inner join and avoid unnecessary outer join
overhead. Update both occurrences where createQueryBuilder(BoardColumn,
'column') is used with .setLock('pessimistic_write') (the queries joining
'column.project') to call innerJoinAndSelect('column.project', 'project')
instead. Ensure no other changes to locking or parameters are made.

---

Duplicate comments:
In `@apps/backend/src/tasks/tasks.service.ts`:
- Around line 113-118: The update() method currently uses the pre-transaction
task snapshot from verifyTaskAccess(id, userId) to decide the "move" branch and
to later save, causing stale-write overwrites; change logic so the transaction's
locked reread is authoritative: move the branch decision and all reads of task
state into the transaction (re-call / re-query the task under the same
transaction lock—eg. the SELECT FOR UPDATE read you already perform) and base
both the "is column changed" check and the final tasksRepository.save on that
reloaded, locked entity (not the earlier verifyTaskAccess result); ensure
verifyTaskAccess is only used for permission checks and not as the source of
truth for state, and update usages of tasksRepository.save in update() to
persist the transaction-local task instance.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c5717e2e-b238-46f0-af57-1aca44baa184

📥 Commits

Reviewing files that changed from the base of the PR and between e588e9d and 964dd7d.

📒 Files selected for processing (1)
  • apps/backend/src/tasks/tasks.service.ts

Comment thread apps/backend/src/tasks/tasks.service.ts
@Zafar7645 Zafar7645 merged commit 81917ea into main Mar 28, 2026
5 checks passed
@github-project-automation github-project-automation bot moved this from In Progress to Done in SyncUp Development Plan Mar 28, 2026
@Zafar7645 Zafar7645 deleted the feature/8-task-api branch March 28, 2026 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Development

Successfully merging this pull request may close these issues.

feat(tasks): Implement task CRUD API endpoints

1 participant