Skip to content

Server/feat/class api#31

Merged
thureinhtet99 merged 5 commits into
developmentfrom
server/feat/class-api
Jul 12, 2026
Merged

Server/feat/class api#31
thureinhtet99 merged 5 commits into
developmentfrom
server/feat/class-api

Conversation

@thureinhtet99

@thureinhtet99 thureinhtet99 commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Added class archiving, with separate options for restoring visibility or permanently deleting classes.
    • Class details can now be viewed for a selected academic year.
    • Class creation responses include enrolled students and teaching assignments.
    • Added clearer teacher, student, enrollment, academic year, and teaching assignment response information.
  • Bug Fixes

    • Improved handling of related attendance, exams, and timetables when records are removed.
    • Archived classes are excluded from standard listings and updates.
  • Documentation

    • Updated API examples and response documentation for clearer, more accurate usage.

…allocation; improve response formatting and examples
- Added `deletedAt` field to Class model and related types in Prisma schema.
- Updated Class service to support soft deletion of classes.
- Modified Classes controller to include archive endpoint for soft delete.
- Adjusted queries to filter out soft-deleted classes.
- Updated migration script to add `deletedAt` column and adjust foreign keys.
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR revises class relationships and foreign-key actions, adds soft-delete and permanent-delete class flows, updates class response and request DTOs, introduces shared entity DTOs, and refreshes Swagger examples and documentation.

Changes

Class lifecycle and API contracts

Layer / File(s) Summary
Domain relationships and persistence rules
flow.txt, server/prisma/schema.prisma, server/prisma/migrations/...
The relationship overview is reorganized around enrollments and teaching allocations; classes gain deletedAt, and related foreign keys receive explicit referential actions.
Class response and creation contracts
server/src/modules/classes/dto/class-response-dto.ts, server/src/modules/classes/dto/create-class-response.dto.ts, server/src/modules/classes/dto/create-class.dto.ts, server/src/modules/classes/dto/class.dto.ts
Class responses reuse shared nested DTOs, class creation accepts teacher/subject allocation summaries, and creation returns mapped student and allocation references.
Related API DTO contracts and documentation
server/src/modules/academic-years/..., server/src/modules/attendances/..., server/src/modules/enrollments/..., server/src/modules/exams/..., server/src/modules/results/..., server/src/modules/students/..., server/src/modules/subjects/..., server/src/modules/teachers/..., server/src/modules/teaching-allocations/..., server/src/modules/classes/dto/update-class.dto.ts
Shared academic-year, enrollment, student, and teacher DTOs are added; teaching allocations use optional nested summaries; Swagger response metadata and examples are updated.
Class lifecycle service and controller flow
server/src/modules/classes/classes.controller.ts, server/src/modules/classes/classes.service.ts, server/src/common/formatters/class.formatter.ts
Class queries exclude archived records, optionally filter by academic year, response mappings use normalized identifiers, and archive/permanent-delete endpoints replace the former removal flow.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ClassesController
  participant ClassesService
  participant Database
  Client->>ClassesController: Request class archive or permanent deletion
  ClassesController->>ClassesService: Invoke archive(id) or delete(id)
  ClassesService->>Database: Update deletedAt or remove related records and class
  Database-->>ClassesService: Return persistence result
  ClassesService-->>ClassesController: Return operation message
  ClassesController-->>Client: Return API response
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is too generic and path-like to clearly convey the main change in the pull request. Use a specific, concise title that names the primary change, such as soft-delete and class API updates.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch server/feat/class-api

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🧹 Nitpick comments (3)
server/src/modules/classes/dto/create-class-response.dto.ts (2)

31-39: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Validation decorators on a response DTO are unnecessary.

@IsArray() and @IsOptional() are request-validation decorators from class-validator. Since CreateClassResponse is a response projection (the service constructs it, not the client), these decorators add no runtime benefit and can mislead readers into thinking this DTO validates incoming payloads. Consider removing them and the class-validator import.

♻️ Proposed refactor
-import { ApiProperty, ApiPropertyOptional, PickType } from '`@nestjs/swagger`';
-import { IsArray, IsOptional } from 'class-validator';
+import { ApiProperty, ApiPropertyOptional, PickType } from '`@nestjs/swagger`';
 import { StudentDto } from '../../students/dto/student.dto';
 import { SubjectDto } from '../../subjects/dto/subject.dto';
 import { TeacherDto } from '../../teachers/dto/teacher.dto';

@@
   `@ApiPropertyOptional`({ type: [StudentSummaryDto] })
-  `@IsArray`()
-  `@IsOptional`()
   students?: StudentSummaryDto[];

   `@ApiPropertyOptional`({ type: [TeachingAllocationRefDto] })
-  `@IsArray`()
-  `@IsOptional`()
   teachingAllocations?: TeachingAllocationRefDto[];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/modules/classes/dto/create-class-response.dto.ts` around lines 31
- 39, Remove the class-validator decorators from the students and
teachingAllocations properties in CreateClassResponseDto, and remove the
now-unused class-validator import. Keep the ApiPropertyOptional decorators and
property types unchanged.

14-15: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove commented-out dead code.

The commented-out id field in TeachingAllocationRefDto adds noise. If the field is not needed, remove it entirely; if it is needed, uncomment and implement it properly.

🧹 Proposed cleanup
 class TeachingAllocationRefDto {
-  // `@ApiProperty`({ type: String })
-
-  // id!: string;
-
   `@ApiProperty`({ type: TeacherSummaryDto })
   teacher!: TeacherSummaryDto;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/modules/classes/dto/create-class-response.dto.ts` around lines 14
- 15, Remove the commented-out id property and its ApiProperty annotation from
TeachingAllocationRefDto unless the DTO requires this field; if required,
restore both as active, properly typed declarations.
server/src/modules/teaching-allocations/dto/teaching-allocation.dto.ts (1)

4-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate summary DTOs across files violate DRY.

SubjectSummaryDto, ClassSummaryDto, and TeacherSummaryDto are defined here with id and name properties, while create-class-response.dto.ts defines StudentSummaryDto, TeacherSummaryDto, and SubjectSummaryDto using PickType. These represent the same domain entities but with different shapes (one has name, the other only has id). Consider extracting shared summary DTOs to a common location to prevent divergence.

♻️ Proposed approach

Extract summary DTOs to a shared file, e.g. server/src/common/dto/summary-dtos.ts:

import { ApiProperty } from '`@nestjs/swagger`';

export class SubjectSummaryDto {
  `@ApiProperty`({ type: String, example: 'subjectid123' })
  id!: string;

  `@ApiProperty`({ type: String, example: 'English' })
  name!: string;
}

export class ClassSummaryDto {
  `@ApiProperty`({ type: String, example: 'classid123' })
  id!: string;

  `@ApiProperty`({ type: String, example: 'Grade 10' })
  name!: string;
}

export class TeacherSummaryDto {
  `@ApiProperty`({ type: String, example: 'teacherid123' })
  id!: string;

  `@ApiProperty`({ type: String, example: 'Mrs Alice' })
  name!: string;
}

Then import and reuse in both teaching-allocation.dto.ts and create-class-response.dto.ts, using PickType where only id is needed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/modules/teaching-allocations/dto/teaching-allocation.dto.ts`
around lines 4 - 24, Extract the shared SubjectSummaryDto, ClassSummaryDto, and
TeacherSummaryDto definitions into a common DTO module, then update
teaching-allocation.dto.ts and create-class-response.dto.ts to import and reuse
them. Preserve the existing full id/name shapes and continue using PickType in
create-class-response.dto.ts where only id is exposed, removing the duplicate
local declarations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/prisma/migrations/20260712084700_init/migration.sql`:
- Around line 14-20: Update the foreign-key definitions for
time_tables.teachingAllocationId, attendances.enrollmentId, and
exams.teachingAllocationId in the migration so their ON DELETE behavior remains
cascading, matching schema.prisma and the preceding cascade migration; leave the
ON UPDATE behavior unchanged.

In `@server/prisma/schema.prisma`:
- Line 179: Update the class model’s name uniqueness constraint near deletedAt
so archived records no longer block active classes with the same name. Replace
the global @@unique([name]) constraint with the project’s supported active-row
partial unique index approach, preserving uniqueness among rows where deletedAt
is null and the existing create/update behavior.

In `@server/src/modules/classes/classes.controller.ts`:
- Around line 74-96: Update the classesService.delete flow used by the
controller’s delete method to verify the class is already archived before
permanently deleting it. Enforce a deletedAt or equivalent archived-state
precondition, and reject active classes without removing the class or related
data; preserve the existing permanent-delete behavior for archived records.

In `@server/src/modules/classes/classes.service.ts`:
- Around line 113-116: Update the class creation flow around the returned
CreateClassResponse to validate academicYearId before writing and persist a
class-year association for that year. Ensure the created class is discoverable
by findAll() and reject unknown academic-year IDs instead of returning the
unpersisted association.
- Around line 247-251: Update findOne() around the effectiveAcademicYearId
lookup to throw NotFoundException when an explicitly supplied academicYearId
does not resolve through prisma.academicYear.findUnique. Preserve the existing
null behavior when the parameter is not supplied, and continue using the
resolved academic year for valid IDs.

In `@server/src/modules/classes/dto/create-class.dto.ts`:
- Around line 31-34: Align the DTO and service behavior for academicYearId:
either add `@IsOptional`() to academicYearId in the create-class DTO to preserve
active-year auto-resolution, or remove the nullish fallback from the classes
service and require the validated field directly. Keep validation and service
semantics consistent.

In `@server/src/modules/exams/dto/create-exam.dto.ts`:
- Around line 49-52: Update ExamsService.create to persist the required
CreateExamDto.academicYearId value by restoring the academicYearId mapping in
the created exam data; keep the DTO field and its validation unchanged.

In `@server/src/modules/results/dto/create-result.dto.ts`:
- Around line 29-37: Restore the create flow by uncommenting and implementing
ResultsService.create, ensuring it accepts and persists the DTO’s required
academicYearId and enrollmentId fields so the create-result endpoint functions
at runtime; otherwise remove the endpoint and its exposed route until
implementation is available.

In `@server/src/modules/students/dto/student-response.dto.ts`:
- Around line 17-20: Update the Swagger metadata for the
StudentResponseDto.class property to use ClassDto instead of EnrollmentDto,
matching its declared ClassDto | null type; remove the unused EnrollmentDto
definition if no other references require it.

In `@server/src/modules/students/dto/student.dto.ts`:
- Around line 23-24: Update the `@ApiProperty` decorator for the gender field in
the student DTO to document UserGender as a string rather than a boolean,
preferably using enum: UserGender so the OpenAPI schema exposes the valid enum
values. Keep the field type and existing API example aligned with UserGender.

In `@server/src/modules/teachers/dto/teacher.dto.ts`:
- Around line 23-24: Update the ApiProperty metadata for the gender field in the
teacher DTO to declare a string or UserGender enum schema instead of Boolean,
while preserving the existing UserGender typing and example value.

---

Nitpick comments:
In `@server/src/modules/classes/dto/create-class-response.dto.ts`:
- Around line 31-39: Remove the class-validator decorators from the students and
teachingAllocations properties in CreateClassResponseDto, and remove the
now-unused class-validator import. Keep the ApiPropertyOptional decorators and
property types unchanged.
- Around line 14-15: Remove the commented-out id property and its ApiProperty
annotation from TeachingAllocationRefDto unless the DTO requires this field; if
required, restore both as active, properly typed declarations.

In `@server/src/modules/teaching-allocations/dto/teaching-allocation.dto.ts`:
- Around line 4-24: Extract the shared SubjectSummaryDto, ClassSummaryDto, and
TeacherSummaryDto definitions into a common DTO module, then update
teaching-allocation.dto.ts and create-class-response.dto.ts to import and reuse
them. Preserve the existing full id/name shapes and continue using PickType in
create-class-response.dto.ts where only id is exposed, removing the duplicate
local declarations.
🪄 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: 2b329bc8-ea04-488c-aad6-9dca0d345cb0

📥 Commits

Reviewing files that changed from the base of the PR and between b03dea9 and 04c3961.

⛔ Files ignored due to path filters (4)
  • server/prisma/generated/prisma/internal/class.ts is excluded by !**/generated/**
  • server/prisma/generated/prisma/internal/prismaNamespace.ts is excluded by !**/generated/**
  • server/prisma/generated/prisma/internal/prismaNamespaceBrowser.ts is excluded by !**/generated/**
  • server/prisma/generated/prisma/models/Class.ts is excluded by !**/generated/**
📒 Files selected for processing (34)
  • flow.txt
  • server/prisma/migrations/20260712073901_init/migration.sql
  • server/prisma/migrations/20260712084700_init/migration.sql
  • server/prisma/schema.prisma
  • server/src/common/formatters/class.formatter.ts
  • server/src/modules/academic-years/academic-years.controller.ts
  • server/src/modules/academic-years/dto/academic-year.dto.ts
  • server/src/modules/academic-years/dto/create-academic-year.dto.ts
  • server/src/modules/attendances/dto/attendance-response.dto.ts
  • server/src/modules/attendances/dto/create-attendance.dto.ts
  • server/src/modules/attendances/dto/query-attendance-dto.ts
  • server/src/modules/classes/classes.controller.ts
  • server/src/modules/classes/classes.service.ts
  • server/src/modules/classes/dto/class-response-dto.ts
  • server/src/modules/classes/dto/class.dto.ts
  • server/src/modules/classes/dto/create-class-response.dto.ts
  • server/src/modules/classes/dto/create-class.dto.ts
  • server/src/modules/classes/dto/update-class.dto.ts
  • server/src/modules/enrollments/dto/create-enrollment.dto.ts
  • server/src/modules/enrollments/dto/enrollment-response.dto.ts
  • server/src/modules/enrollments/dto/enrollment.dto.ts
  • server/src/modules/enrollments/dto/query-enrollment.dto.ts
  • server/src/modules/exams/dto/create-exam.dto.ts
  • server/src/modules/results/dto/create-result.dto.ts
  • server/src/modules/results/dto/query-result-dto.ts
  • server/src/modules/results/dto/result-response.dto.ts
  • server/src/modules/students/dto/student-response.dto.ts
  • server/src/modules/students/dto/student.dto.ts
  • server/src/modules/subjects/dto/subject.dto.ts
  • server/src/modules/teachers/dto/teacher.dto.ts
  • server/src/modules/teaching-allocations/dto/create-teaching-allocation.dto.ts
  • server/src/modules/teaching-allocations/dto/query-teaching-allocation.dto.ts
  • server/src/modules/teaching-allocations/dto/teaching-allocation-response.dto.ts
  • server/src/modules/teaching-allocations/dto/teaching-allocation.dto.ts

Comment on lines +14 to +20
ALTER TABLE "time_tables" ADD CONSTRAINT "time_tables_teachingAllocationId_fkey" FOREIGN KEY ("teachingAllocationId") REFERENCES "teaching_assignments"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "attendances" ADD CONSTRAINT "attendances_enrollmentId_fkey" FOREIGN KEY ("enrollmentId") REFERENCES "enrollments"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "exams" ADD CONSTRAINT "exams_teachingAllocationId_fkey" FOREIGN KEY ("teachingAllocationId") REFERENCES "teaching_assignments"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Keep the final foreign keys cascading.

This later migration overrides the preceding cascade migration with RESTRICT, contrary to schema.prisma. A class with attendance, exams, or timetable entries cannot be permanently deleted: deleting its enrollment/allocation fails on the dependent row and rolls back the transaction.

Proposed fix
- ON DELETE RESTRICT ON UPDATE CASCADE;
+ ON DELETE CASCADE ON UPDATE CASCADE;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ALTER TABLE "time_tables" ADD CONSTRAINT "time_tables_teachingAllocationId_fkey" FOREIGN KEY ("teachingAllocationId") REFERENCES "teaching_assignments"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "attendances" ADD CONSTRAINT "attendances_enrollmentId_fkey" FOREIGN KEY ("enrollmentId") REFERENCES "enrollments"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "exams" ADD CONSTRAINT "exams_teachingAllocationId_fkey" FOREIGN KEY ("teachingAllocationId") REFERENCES "teaching_assignments"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "time_tables" ADD CONSTRAINT "time_tables_teachingAllocationId_fkey" FOREIGN KEY ("teachingAllocationId") REFERENCES "teaching_assignments"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "attendances" ADD CONSTRAINT "attendances_enrollmentId_fkey" FOREIGN KEY ("enrollmentId") REFERENCES "enrollments"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "exams" ADD CONSTRAINT "exams_teachingAllocationId_fkey" FOREIGN KEY ("teachingAllocationId") REFERENCES "teaching_assignments"("id") ON DELETE CASCADE ON UPDATE CASCADE;
🧰 Tools
🪛 Squawk (2.59.0)

[warning] 14-14: By default new constraints require a table scan and block writes to the table while that scan occurs. Use NOT VALID with a later VALIDATE CONSTRAINT call.

(constraint-missing-not-valid)


[warning] 14-14: Adding a foreign key constraint requires a table scan and a SHARE ROW EXCLUSIVE lock on both tables, which blocks writes to each table. Add NOT VALID to the constraint in one transaction and then VALIDATE the constraint in a separate transaction.

(adding-foreign-key-constraint)


[warning] 17-17: By default new constraints require a table scan and block writes to the table while that scan occurs. Use NOT VALID with a later VALIDATE CONSTRAINT call.

(constraint-missing-not-valid)


[warning] 17-17: Adding a foreign key constraint requires a table scan and a SHARE ROW EXCLUSIVE lock on both tables, which blocks writes to each table. Add NOT VALID to the constraint in one transaction and then VALIDATE the constraint in a separate transaction.

(adding-foreign-key-constraint)


[warning] 20-20: By default new constraints require a table scan and block writes to the table while that scan occurs. Use NOT VALID with a later VALIDATE CONSTRAINT call.

(constraint-missing-not-valid)


[warning] 20-20: Adding a foreign key constraint requires a table scan and a SHARE ROW EXCLUSIVE lock on both tables, which blocks writes to each table. Add NOT VALID to the constraint in one transaction and then VALIDATE the constraint in a separate transaction.

(adding-foreign-key-constraint)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/prisma/migrations/20260712084700_init/migration.sql` around lines 14 -
20, Update the foreign-key definitions for time_tables.teachingAllocationId,
attendances.enrollmentId, and exams.teachingAllocationId in the migration so
their ON DELETE behavior remains cascading, matching schema.prisma and the
preceding cascade migration; leave the ON UPDATE behavior unchanged.

enrollments Enrollment[]
teachingAllocations TeachingAllocation[]
announcements Announcement[]
deletedAt DateTime?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make class-name uniqueness compatible with soft deletion.

@@unique([name]) still includes archived rows, but create() and update() only search active rows. After archiving “Grade 1A,” creating or renaming to that name reaches Prisma’s unique constraint and fails instead of treating the archived record as inactive. Replace the global constraint with an active-row partial unique index (or explicitly restore the archived class).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/prisma/schema.prisma` at line 179, Update the class model’s name
uniqueness constraint near deletedAt so archived records no longer block active
classes with the same name. Replace the global @@unique([name]) constraint with
the project’s supported active-row partial unique index approach, preserving
uniqueness among rows where deletedAt is null and the existing create/update
behavior.

Comment on lines +74 to +96
@ApiOperation({
summary: 'Archive a class (soft delete)',
description: 'This performs a soft delete, preserving all related data.',
})
@ApiOkResponse({
schema: { example: { message: 'Class archived successfully' } },
})
@Delete(':id/archive')
async archive(@Param('id') id: string): Promise<{ message: string }> {
return this.classesService.archive(id);
}

@ApiOperation({
summary: 'Permanently delete a class (destructive)',
description:
'Warning: This action is irreversible and will delete the class and all its associated data, including enrollments, attendance, exams, and timetables.',
})
@ApiOkResponse({
schema: { example: { message: 'Class deleted successfully' } },
schema: { example: { message: 'Class permanently deleted successfully' } },
})
@Delete(':id')
async remove(@Param('id') id: string): Promise<{ message: string }> {
return this.classesService.remove(id);
async delete(@Param('id') id: string): Promise<{ message: string }> {
return this.classesService.delete(id);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check archive and delete method implementations in ClassesService.
ast-grep run --pattern $'async archive($_) {
  $$$
}' --lang typescript server/src/modules/classes/classes.service.ts

ast-grep run --pattern $'async delete($_) {
  $$$
}' --lang typescript server/src/modules/classes/classes.service.ts

Repository: thureinhtet99/student-os

Length of output: 799


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the service file structure first.
ast-grep outline server/src/modules/classes/classes.service.ts --view expanded || true

# Find deletedAt / archive / delete usages in the classes module.
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
  '\bdeletedAt\b|\barchive\(|\bdelete\(' server/src/modules/classes -S || true

# Show the relevant section of the service file with line numbers.
wc -l server/src/modules/classes/classes.service.ts
sed -n '1,260p' server/src/modules/classes/classes.service.ts | cat -n

Repository: thureinhtet99/student-os

Length of output: 11640


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the archive/delete implementation with line numbers.
sed -n '360,430p' server/src/modules/classes/classes.service.ts | cat -n

# Check for any class-related delete/archive helpers or state checks elsewhere.
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
  'deletedAt: null|NotFoundException|archive\(id: string\)|delete\(id: string\)' server/src/modules/classes -S || true

Repository: thureinhtet99/student-os

Length of output: 3672


Guard permanent delete against active classes. archive already rejects archived records, but delete still hard-deletes any existing class ID, including active classes and their related data. Add a deletedAt precondition or require an explicit archived state before deleting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/modules/classes/classes.controller.ts` around lines 74 - 96,
Update the classesService.delete flow used by the controller’s delete method to
verify the class is already archived before permanently deleting it. Enforce a
deletedAt or equivalent archived-state precondition, and reject active classes
without removing the class or related data; preserve the existing
permanent-delete behavior for archived records.

Comment on lines +113 to +116
const response: CreateClassResponse = {
name: classItem.name,
academicYearId,
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Persist and validate the returned academic-year association.

A valid create request containing only name and academicYearId writes no row tied to that year, yet returns it here. Such a class is immediately excluded by findAll() because that query requires an enrollment or teaching allocation for the year; an unknown academic-year ID can also succeed. Persist a class-year association (or require an association row) and verify the academic year before writing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/modules/classes/classes.service.ts` around lines 113 - 116, Update
the class creation flow around the returned CreateClassResponse to validate
academicYearId before writing and persist a class-year association for that
year. Ensure the created class is discoverable by findAll() and reject unknown
academic-year IDs instead of returning the unpersisted association.

Comment on lines +247 to +251
const academicYear = effectiveAcademicYearId
? await this.prisma.academicYear.findUnique({
where: { id: effectiveAcademicYearId },
})
: null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject an unknown explicit academic year.

findAll() validates a supplied academicYearId, but findOne() converts an unknown ID into academicYear: undefined and returns 200 with empty nested data. Apply the same NotFoundException check when the caller explicitly supplies this query parameter.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/modules/classes/classes.service.ts` around lines 247 - 251, Update
findOne() around the effectiveAcademicYearId lookup to throw NotFoundException
when an explicitly supplied academicYearId does not resolve through
prisma.academicYear.findUnique. Preserve the existing null behavior when the
parameter is not supplied, and continue using the resolved academic year for
valid IDs.

Comment on lines +49 to 52
@ApiProperty({ example: 'academicyearid123' })
@IsString()
@IsNotEmpty()
academicYearId!: string;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

academicYearId is required by the DTO but commented out in the service.

CreateExamDto.academicYearId has @IsNotEmpty(), so clients must provide it. However, ExamsService.create has // academicYearId: createExamDto.academicYearId commented out, meaning the value is silently discarded. Either remove the field from the DTO or uncomment the service line to persist it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/modules/exams/dto/create-exam.dto.ts` around lines 49 - 52, Update
ExamsService.create to persist the required CreateExamDto.academicYearId value
by restoring the academicYearId mapping in the created exam data; keep the DTO
field and its validation unchanged.

Comment on lines +29 to 37
@ApiProperty({ example: 'academicyearid123' })
@IsString()
@IsNotEmpty()
academicYearId!: string;

@ApiProperty({ example: 'ckx123enrollmentid' })
@ApiProperty({ example: 'enrollmentid123' })
@IsString()
@IsNotEmpty()
enrollmentId!: string;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

ResultsService.create is fully commented out — the endpoint is non-functional.

The DTO requires academicYearId and enrollmentId with @IsNotEmpty(), but the service's create method is entirely commented out. Any request to create a result will fail at runtime. Uncomment and implement the service method, or remove the endpoint until it's ready.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/modules/results/dto/create-result.dto.ts` around lines 29 - 37,
Restore the create flow by uncommenting and implementing ResultsService.create,
ensuring it accepts and persists the DTO’s required academicYearId and
enrollmentId fields so the create-result endpoint functions at runtime;
otherwise remove the endpoint and its exposed route until implementation is
available.

Comment on lines 17 to 20
class EnrollmentDto {
@ApiProperty({ example: 'ckx123enrollmentid' })
@ApiProperty({ example: 'enrollmentid123' })
id!: string;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Use ClassDto for the documented class property.

This newly defined EnrollmentDto is used by @ApiProperty({ type: EnrollmentDto }) at Lines [43-44], while the property is declared as ClassDto | null. Swagger clients will therefore receive an incomplete/incorrect schema for StudentResponseDto.class. Use ClassDto there, or rename the property/type if it is actually intended to represent an enrollment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/modules/students/dto/student-response.dto.ts` around lines 17 -
20, Update the Swagger metadata for the StudentResponseDto.class property to use
ClassDto instead of EnrollmentDto, matching its declared ClassDto | null type;
remove the unused EnrollmentDto definition if no other references require it.

Comment on lines +23 to +24
@ApiProperty({ type: Boolean, example: 'MALE' })
gender!: UserGender;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fix incorrect Swagger type for gender field.

@ApiProperty({ type: Boolean, ... }) is applied to a UserGender enum field. This will generate misleading OpenAPI documentation showing gender as a boolean. The type should be String (or use enum: UserGender) to match the actual UserGender type.

🐛 Proposed fix
-  `@ApiProperty`({ type: Boolean, example: 'MALE' })
+  `@ApiProperty`({ type: String, example: 'MALE', enum: UserGender })
   gender!: UserGender;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@ApiProperty({ type: Boolean, example: 'MALE' })
gender!: UserGender;
`@ApiProperty`({ type: String, example: 'MALE', enum: UserGender })
gender!: UserGender;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/modules/students/dto/student.dto.ts` around lines 23 - 24, Update
the `@ApiProperty` decorator for the gender field in the student DTO to document
UserGender as a string rather than a boolean, preferably using enum: UserGender
so the OpenAPI schema exposes the valid enum values. Keep the field type and
existing API example aligned with UserGender.

Comment on lines +23 to +24
@ApiProperty({ type: Boolean, example: 'MALE' })
gender!: UserGender;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

gender Swagger type is Boolean but the field is a UserGender string enum.

@ApiProperty({ type: Boolean, example: 'MALE' }) will generate an incorrect OpenAPI schema showing a boolean input for a gender field. The type should be String (or use the enum directly).

🔧 Proposed fix
-  `@ApiProperty`({ type: Boolean, example: 'MALE' })
+  `@ApiProperty`({ type: String, example: 'MALE', enum: UserGender })
   gender!: UserGender;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@ApiProperty({ type: Boolean, example: 'MALE' })
gender!: UserGender;
`@ApiProperty`({ type: String, example: 'MALE', enum: UserGender })
gender!: UserGender;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/modules/teachers/dto/teacher.dto.ts` around lines 23 - 24, Update
the ApiProperty metadata for the gender field in the teacher DTO to declare a
string or UserGender enum schema instead of Boolean, while preserving the
existing UserGender typing and example value.

@thureinhtet99
thureinhtet99 merged commit 189cb9f into development Jul 12, 2026
5 of 6 checks passed
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.

1 participant