Skip to content

Reccurring transactions#9

Open
boiboi796 wants to merge 15 commits into
mainfrom
Reccurring-Transactions
Open

Reccurring transactions#9
boiboi796 wants to merge 15 commits into
mainfrom
Reccurring-Transactions

Conversation

@boiboi796
Copy link
Copy Markdown
Collaborator

##What I did
Introduced RecurringTransactionsModule, Controller, Service, and Repository

  • Implemented Create and Update DTOs for recurring transactions
  • Enhanced schema with recurring transactions table and relationships
  • Updated TransactionsRepository for soft delete functionality
  • Added recurrence frequency and status enums to transaction types

Copilot AI review requested due to automatic review settings March 12, 2026 19:11
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds recurring-transactions support to the NestJS + Drizzle expense tracker, alongside introducing JWT-based authentication/authorization and enhancing the DB schema to support recurrence and soft-deleted transactions.

Changes:

  • Added a full RecurringTransactions feature set (module/controller/service/repository + DTOs) and recurrence enums.
  • Introduced JWT auth (strategy + guard), plus role-based access control for admin-only endpoints.
  • Updated DB schema for recurring transactions + transaction soft-delete (and updated repository/service/controller logic accordingly).

Reviewed changes

Copilot reviewed 39 out of 40 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/utils/return-first.ts Adds a small helper to safely return the first row from a query result.
src/users/users.service.ts Implements user creation/listing/lookup and admin checks (bcrypt hashing).
src/users/users.service.spec.ts Adds basic UsersService test scaffold.
src/users/users.repository.ts Adds users persistence methods using Drizzle.
src/users/users.module.ts Registers users providers/controllers (and exports repository).
src/users/users.controller.ts Adds users endpoints with JWT + admin-only role guarding for reads.
src/users/users.controller.spec.ts Adds basic UsersController test scaffold.
src/users/roles.guard.ts Introduces role-based authorization guard.
src/users/roles.decorator.ts Adds @Roles() decorator for role metadata.
src/users/dto/users-dto.ts Adds user DTOs including password validation.
src/transactions/transactions.types.ts Adds recurrence frequency/status enums.
src/transactions/transactions.service.ts Enforces per-user access checks and switches to soft delete.
src/transactions/transactions.service.spec.ts Updates TransactionsService test scaffold with repository mock.
src/transactions/transactions.repository.ts Adds soft-delete behavior + “find by id and user” query.
src/transactions/transactions.controller.ts Secures endpoints with JWT; admin-only list-all; user-scoped CRUD.
src/transactions/transactions.controller.spec.ts Adds TransactionsController test scaffold with service mock.
src/transactions/dto/create-transaction.dto.ts Minor DTO import/validator adjustments.
src/recurring-transactions/recurring-transactions.service.ts Implements recurring transaction scheduling validation and CRUD logic.
src/recurring-transactions/recurring-transactions.repository.ts Adds recurring transactions persistence methods.
src/recurring-transactions/recurring-transactions.module.ts Registers recurring transactions providers/controllers.
src/recurring-transactions/recurring-transactions.controller.ts Adds JWT-protected recurring-transactions CRUD endpoints.
src/recurring-transactions/dto/update-recurring-transaction.dto.ts Adds update DTO via PartialType.
src/recurring-transactions/dto/create-recurring-transaction.dto.ts Adds create DTO with recurrence and schedule validation decorators.
src/db/schema.ts Adds recurring_transactions table, relationships, indexes, and deletedAt for transactions.
src/config/env.ts Adds JWT_SECRET to env config surface.
src/auth/strategies/jwt.strategy/jwt.strategy.ts Adds Passport JWT strategy for request authentication.
src/auth/strategies/jwt.strategy/jwt.strategy.spec.ts Adds JwtStrategy test (currently problematic due to env requirements).
src/auth/interfaces/authenticated-request.interface.ts Adds a typed Express request with authenticated user shape.
src/auth/guards/jwt-auth/jwt-auth.guard.ts Adds JWT auth guard wrapper.
src/auth/guards/jwt-auth/jwt-auth.guard.spec.ts Adds basic JwtAuthGuard test scaffold.
src/auth/dto/users.dto.ts Adds an auth-focused user DTO for login response.
src/auth/dto/login.dto.ts Adds login DTO with validation.
src/auth/auth.service.ts Implements login with bcrypt + JWT issuance.
src/auth/auth.service.spec.ts Adds basic AuthService test scaffold.
src/auth/auth.module.ts Registers auth module providers/controllers.
src/auth/auth.controller.ts Adds login + JWT-protected profile endpoints.
src/auth/auth.controller.spec.ts Adds basic AuthController test scaffold.
src/app.module.ts Wires in AuthModule and RecurringTransactionsModule.
package.json Adds passport/JWT/bcrypt deps and Jest moduleNameMapper for src/* imports.
package-lock.json Locks added dependencies.
Comments suppressed due to low confidence (1)

src/transactions/transactions.repository.ts:12

  • UpdateTransactionInput omits createdOn and lastModifiedOn, but the schema uses createdAt / updatedAt (and now also has transactionDate / deletedAt). As written, the update type will still allow updating system-managed timestamps, and the omit list references non-existent fields. Update the omit list to match the actual schema fields you want to prevent updating (e.g., createdAt, updatedAt, deletedAt, transactionDate, etc.).
type UpdateTransactionInput = Partial<Omit<
          CreateTransactionInput, 
          'userId' | 'id' | 'createdOn' | 'lastModifiedOn'>
        >;

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +55 to 59
async softDelete(id: number) {
return this.dbClient
.delete(transactions)
.update(transactions)
.set({ deletedAt: sql`CURRENT_TIMESTAMP` })
.where(eq(transactions.id, id));
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

softDelete() only filters by transaction id. Prefer enforcing ownership/deleted-state atomically in the update by including userId and deletedAt IS NULL in the where clause (and accept userId in this method), to prevent accidental misuse and reduce TOCTOU risks.

Copilot uses AI. Check for mistakes.
Comment thread src/auth/dto/users.dto.ts
Comment on lines +9 to +13
id: number;
email: string;
username: string;
userType: string;

Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

UserDto exposes username, but other user-facing DTOs in this PR use name. This inconsistent naming will confuse API consumers. Align on a single field name (preferably name) or provide a clear migration plan/documentation if both must exist.

Copilot uses AI. Check for mistakes.
Comment thread src/auth/auth.module.ts Outdated
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { UsersModule } from 'src/users/users.module';
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

This src/... absolute import will be emitted as-is in the compiled JS. With the current start:prod script (node dist/main) and no runtime path-alias loader configured, Node won't resolve src/* specifiers and the app can fail at runtime. Prefer relative imports here, or add a proper TypeScript paths mapping and register a runtime resolver for production builds.

Suggested change
import { UsersModule } from 'src/users/users.module';
import { UsersModule } from '../users/users.module';

Copilot uses AI. Check for mistakes.
Comment on lines +4 to +5
it('should be defined', () => {
expect(new JwtStrategy()).toBeDefined();
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

JwtStrategy throws in its constructor when env.JWT_SECRET is not set, but this unit test instantiates the strategy without configuring the secret first, so it will fail in most test environments. Update the test to set the secret before importing/constructing the strategy (or change the strategy to not throw in the constructor and instead validate configuration during app bootstrap).

Suggested change
it('should be defined', () => {
expect(new JwtStrategy()).toBeDefined();
it('should be defined', () => {
const originalSecret = process.env.JWT_SECRET;
process.env.JWT_SECRET = process.env.JWT_SECRET || 'test-secret';
expect(new JwtStrategy()).toBeDefined();
process.env.JWT_SECRET = originalSecret;

Copilot uses AI. Check for mistakes.
{message: 'Password must be at least 8 characters long and include uppercase letters, lowercase letters, numbers, and symbols.',
}
)
passwordHash: string;
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

This field is named passwordHash, but the service hashes it, which implies the client is sending a plain-text password. This is misleading for API consumers and increases the risk of accidental double-hashing. Consider renaming the DTO field to password (or similar) and mapping internally; if breaking changes are a concern, accept both names temporarily.

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +21
getProfile(@Request() req: ExpressRequest) {
return (req as any).user;
}
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

This endpoint returns (req as any).user even though the PR introduces a typed AuthenticatedRequest interface. Use @Request() req: AuthenticatedRequest (or @Req() with the same type) to avoid any casts and keep the request user shape consistent across controllers.

Copilot uses AI. Check for mistakes.
Comment on lines +51 to +55
const updatePayload: Record<string, unknown> = { ...dto };

if (dto.startAt !== undefined) {
updatePayload.startAt = this.toDate(dto.startAt, 'startAt');
}
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

updatePayload is typed as Record<string, unknown>, but the repository update method expects a typed partial of the insert model; this will fail TypeScript assignment because unknown isn't assignable to the expected field types. Type updatePayload to match Parameters<RecurringTransactionsRepository['updateByIdAndUserId']>[2] (or export a dedicated update input type) before passing it to the repository.

Copilot uses AI. Check for mistakes.
Comment thread src/users/users.module.ts
Comment on lines +1 to +4
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { DbModule } from 'src/db/db.module';
import { UsersController } from './users.controller';
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

This module uses a src/... absolute import for DbModule, while other modules in the repo (e.g., src/transactions/transactions.module.ts) use relative imports for internal modules. Mixing import styles makes refactors and tooling configuration harder; please standardize on one approach across modules (either relative imports everywhere or src/ aliases everywhere).

Copilot uses AI. Check for mistakes.
Comment thread src/db/schema.ts Outdated
.notNull()
.$onUpdate(() => sql`CURRENT_TIMESTAMP`),

amount: decimal('amount', { precision: 15, scale: 3, mode: 'number' }),
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

transactions.amount is nullable in the schema, but CreateTransactionDto requires amount (and the service always inserts it). If amount should always be present, mark the column as .notNull() to keep DB constraints aligned with API validation.

Suggested change
amount: decimal('amount', { precision: 15, scale: 3, mode: 'number' }),
amount: decimal('amount', { precision: 15, scale: 3, mode: 'number' }).notNull(),

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants