Skip to content

fix: cap talent search limit at 50 in service layer#1439

Open
vidhiii1711 wants to merge 2 commits into
Sachinchaurasiya360:mainfrom
vidhiii1711:fix/talent-search-limit-cap
Open

fix: cap talent search limit at 50 in service layer#1439
vidhiii1711 wants to merge 2 commits into
Sachinchaurasiya360:mainfrom
vidhiii1711:fix/talent-search-limit-cap

Conversation

@vidhiii1711
Copy link
Copy Markdown
Contributor

@vidhiii1711 vidhiii1711 commented Jun 5, 2026

Pull Request

Description

Added a Math.min(limit, 50) guard directly in the searchTalent service method in recruiter.service.ts so the DB query is always capped at 50 records, independent of the Zod validation middleware.

Related Issue

Fixes #1118

Type of Change

  • Bug Fix
  • Feature
  • Enhancement
  • Documentation

Testing

  1. Run cd server && npm run dev
  2. Call GET /api/recruiter/talent?limit=9999&page=1 with a recruiter JWT token
  3. Verify pagination.limit in response is 50, not 9999
  4. npx tsc --noEmit now passes with 0 errors (pre-existing prisma import error in opensource.routes.ts also fixed)

Screenshots / Video

No UI changes in this PR (delete this line if you are making UI changes)

Checklist

  • Code follows project guidelines
  • No new compile/type errors
  • Tested manually (include steps above)
  • No .env, credentials, or node_modules committed
  • Docs updated (if needed)
  • Screenshot or video attached for every UI change (PR will be rejected if missing)

Summary by CodeRabbit

  • Bug Fixes

    • Restored database access for the user requests listing endpoint.
  • Performance & Stability

    • Enforced a maximum page size of 50 for talent search results to prevent large queries and improve responsiveness.
    • Pagination now reports the enforced per-page limit; total-page calculations may still reflect the original requested size.

@github-actions github-actions Bot added bug Something isn't working gssoc level:beginner Good for first-time contributors gssoc:approved Approved for GSSoC scoring labels Jun 5, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 5, 2026

Hi @vidhiii1711, thanks for contributing to InternHack! 🎉

I have automatically:

  • 👤 Assigned this PR to you.
  • 🏷️ Applied the gssoc:approved label.

Our workflows will now analyze your changes to classify:

  • 📈 PR Difficulty: level:*
  • 🧩 PR Type: type:*
  • 🌟 PR Quality: quality:*

Tip

Ensure your PR description references the issue it resolves (e.g. Closes #123). This allows the bot to inherit any additional labels from that issue!

Happy coding! 🚀

@github-actions github-actions Bot added scope:backend Changes to server-side / API code type:bug Bug fixes quality:exceptional Exceptional implementation quality labels Jun 5, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 5, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a Prisma import to the opensource routes so the /requests/mine handler can query the DB, and clamps filter.limit to a server-side safeLimit = min(filter.limit, 50) in searchTalent, using it for skip/take and returned pagination limit.

Changes

Backend Database and Query Limits

Layer / File(s) Summary
Prisma import for opensource requests
server/src/module/opensource/opensource.routes.ts
Adds a prisma import so the /requests/mine route can query prisma.repoRequest and prisma.opensourceRepo.
Pagination limit cap in talent search
server/src/module/recruiter/recruiter.service.ts
Introduces safeLimit = min(filter.limit, 50) and uses safeLimit for skip/take in the DB query; pagination.limit returned to clients is set to safeLimit while totalPages remains computed from filter.limit.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant RecruiterService as searchTalent
  participant Prisma as prisma.user.findMany
  Client->>RecruiterService: request with filter.limit
  RecruiterService->>RecruiterService: safeLimit = min(filter.limit, 50)
  RecruiterService->>Prisma: findMany(skip, take = safeLimit)
  Prisma-->>RecruiterService: records
  RecruiterService-->>Client: results + pagination(limit: safeLimit, totalPages: computedFromFilter)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • Sachinchaurasiya360

Poem

🐰 I hopped through code with careful paws,
Imported Prisma to open-source laws,
Tamed the pages, capped them at fifty,
Queries now safe and running swift-y,
A tiny fix — from a rabbit’s cause.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly summarizes the main change: adding a cap to the talent search limit at 50 in the service layer.
Linked Issues check ✅ Passed The PR implementation directly addresses issue #1118 by adding Math.min(limit, 50) guard in the service layer to enforce server-side pagination cap.
Out of Scope Changes check ✅ Passed While the PR includes a secondary fix (prisma import in opensource.routes.ts), it is explicitly disclosed and addresses a pre-existing TypeScript error, which is acceptable scope expansion.
Description check ✅ Passed The pull request description follows the required template and includes all essential sections with clear, actionable information.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

Copy link
Copy Markdown
Contributor

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
server/src/module/opensource/opensource.routes.ts (1)

30-62: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Move database queries to service layer per coding guidelines.

The inline handler violates the architectural boundaries defined in the coding guidelines:

  • "Routes file should contain Express router with middleware chain"
  • "Service layer must contain business logic, database queries, and external API calls"

Lines 32-35 duplicate the existing getMyRepoRequests(userId) method already present in opensource.service.ts (see context snippet 3). Additionally, all other routes in this file delegate to controller methods (e.g., lines 25-27, 72-73), but this handler contains inline business logic and direct Prisma calls.

Refactor this handler to delegate to a controller method that orchestrates service layer calls for the database queries and enrichment logic.

♻️ Suggested refactor approach

Move the enrichment logic into a new service method (or enhance the existing getMyRepoRequests), then delegate from the route to a controller method:

In opensource.service.ts:

async getMyRepoRequestsWithRepoId(userId: number) {
  const requests = await prisma.repoRequest.findMany({
    where: { userId },
    orderBy: { createdAt: "desc" },
  });
  
  const approvedUrls = requests
    .filter((r) => r.status === "APPROVED")
    .map((r) => r.url);
  
  const approvedRepos = approvedUrls.length > 0
    ? await prisma.opensourceRepo.findMany({
        where: { url: { in: approvedUrls } },
        select: { id: true, url: true },
      })
    : [];
  
  const repoIdByUrl = new Map(approvedRepos.map((r) => [r.url, r.id]));
  
  return requests.map((r) => ({
    ...r,
    repoId: r.status === "APPROVED" ? (repoIdByUrl.get(r.url) ?? null) : null,
  }));
}

In opensource.controller.ts:

async getMyRepoRequests(req: Request, res: Response, next: NextFunction) {
  try {
    const requests = await this.service.getMyRepoRequestsWithRepoId(req.user!.id);
    res.json({ requests });
  } catch (err) {
    next(err);
  }
}

In this routes file:

-opensourceRouter.get("/requests/mine", authMiddleware, requireRole("STUDENT"), async (req, res, next) => {
-  try {
-    const requests = await prisma.repoRequest.findMany({
-      where: { userId: req.user!.id },
-      orderBy: { createdAt: "desc" },
-    });
-
-    // For approved requests, look up the corresponding InternHack repo ID so
-    // the client can open the repo detail popup directly.
-    const approvedUrls = requests
-      .filter((r) => r.status === "APPROVED")
-      .map((r) => r.url);
-
-    const approvedRepos =
-      approvedUrls.length > 0
-        ? await prisma.opensourceRepo.findMany({
-            where: { url: { in: approvedUrls } },
-            select: { id: true, url: true },
-          })
-        : [];
-
-    const repoIdByUrl = new Map(approvedRepos.map((r) => [r.url, r.id]));
-
-    const enriched = requests.map((r) => ({
-      ...r,
-      repoId: r.status === "APPROVED" ? (repoIdByUrl.get(r.url) ?? null) : null,
-    }));
-
-    res.json({ requests: enriched });
-  } catch (err) {
-    next(err);
-  }
-});
+opensourceRouter.get("/requests/mine", authMiddleware, requireRole("STUDENT"), (req, res, next) =>
+  controller.getMyRepoRequests(req, res, next)
+);

Once refactored, the prisma import on line 1 can be removed from this routes file.

As per coding guidelines: "Routes file should contain Express router with middleware chain" and "Service layer must contain business logic, database queries, and external API calls".

🤖 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/module/opensource/opensource.routes.ts` around lines 30 - 62, The
route handler for GET "/requests/mine" in opensource.routes.ts contains business
logic and direct Prisma queries; move that logic into the service layer by
adding or updating a method like getMyRepoRequestsWithRepoId(userId) in
opensource.service.ts that performs the repoRequest.findMany, approvedUrls
filtering, opensourceRepo.findMany lookup, map creation, and returns the
enriched requests; then add a controller method getMyRepoRequests in
opensource.controller.ts that calls
service.getMyRepoRequestsWithRepoId(req.user!.id) and sends res.json({ requests
}), and finally change the route to delegate to that controller method (removing
prisma usage and the inline logic from opensource.routes.ts).
🤖 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/src/module/recruiter/recruiter.service.ts`:
- Around line 656-663: The pagination computes DB reads with safeLimit but still
uses raw filter.limit for metadata; update the metadata construction (where
totalPages/pageSize/current page info is built around the results from
prisma.user.findMany and total) to use safeLimit instead of filter.limit (and
ensure any calculations that derive skip or totalPages use safeLimit as the page
size), referencing the existing variables safeLimit, skip, filter.page,
students, and total so page metadata matches the actual queried page size.

---

Outside diff comments:
In `@server/src/module/opensource/opensource.routes.ts`:
- Around line 30-62: The route handler for GET "/requests/mine" in
opensource.routes.ts contains business logic and direct Prisma queries; move
that logic into the service layer by adding or updating a method like
getMyRepoRequestsWithRepoId(userId) in opensource.service.ts that performs the
repoRequest.findMany, approvedUrls filtering, opensourceRepo.findMany lookup,
map creation, and returns the enriched requests; then add a controller method
getMyRepoRequests in opensource.controller.ts that calls
service.getMyRepoRequestsWithRepoId(req.user!.id) and sends res.json({ requests
}), and finally change the route to delegate to that controller method (removing
prisma usage and the inline logic from opensource.routes.ts).
🪄 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: 47c68dec-841e-4858-ae07-5db6b2bc6d9d

📥 Commits

Reviewing files that changed from the base of the PR and between 0c176ae and b53a162.

📒 Files selected for processing (2)
  • server/src/module/opensource/opensource.routes.ts
  • server/src/module/recruiter/recruiter.service.ts

Comment thread server/src/module/recruiter/recruiter.service.ts Outdated
@vidhiii1711 vidhiii1711 force-pushed the fix/talent-search-limit-cap branch from e727e1d to 60f10eb Compare June 5, 2026 16:50
@github-actions github-actions Bot added level:critical High-impact or critical changes and removed level:beginner Good for first-time contributors labels Jun 5, 2026
Copy link
Copy Markdown
Contributor

@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)
server/src/module/recruiter/recruiter.service.ts (1)

737-737: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use safeLimit for totalPages calculation.

Line 737 still calculates totalPages using filter.limit, but the actual page size is safeLimit (line 664). This creates a pagination inconsistency: when a client requests limit=100 (capped to 50), totalPages is computed as Math.ceil(total / 100) instead of Math.ceil(total / 50), resulting in fewer reported pages than actually needed to retrieve all records.

For example, with 150 total records and limit=100, the response would show totalPages: 2 (incorrect) instead of totalPages: 3 (correct). This breaks client-side pagination logic.

🔧 Proposed fix
       pagination: {
         page: filter.page,
         limit: safeLimit,
         total,
-        totalPages: Math.ceil(total / filter.limit),
+        totalPages: Math.ceil(total / safeLimit),
       },
🤖 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/module/recruiter/recruiter.service.ts` at line 737, The totalPages
calculation is using filter.limit but should use the actual page size safeLimit
(declared earlier), so update the assignment for totalPages to use
Math.ceil(total / safeLimit) (and guard against zero by using Math.ceil(total /
(safeLimit || 1))) to ensure pagination reflects the applied cap; change the
expression that sets totalPages (currently referencing filter.limit) to
reference safeLimit instead.
🧹 Nitpick comments (1)
server/src/module/recruiter/recruiter.service.ts (1)

355-363: ⚖️ Poor tradeoff

Consider moving the duplicate status check inside the transaction.

The early-return check at Line 356 prevents unnecessary database writes and emails when the status is unchanged, which is good. However, because this check occurs outside the transaction (Line 361), there's a narrow race window where the status could theoretically change between the check and the transaction execution.

The _updateApplicationStatus helper at Line 821 does include its own status comparison before creating history, which mitigates the risk. Still, for consistency and to fully prevent race conditions, consider moving the duplicate check inside the transaction.

♻️ Optional refactor to move check inside transaction
-    // Fix for `#1111`: Prevent duplicate status updates and emails
-    if (application.status === status) {
-      const { job: _job, student: _student, ...current } = application;
-      return current;
-    }
-
     const updated = await prisma.$transaction(async (tx) => {
+      const current = await tx.application.findUnique({
+        where: { id: applicationId },
+        select: { status: true },
+      });
+      if (current?.status === status) {
+        return await tx.application.findUnique({ where: { id: applicationId } });
+      }
       return this._updateApplicationStatus(tx, applicationId, status, recruiterId);
     });

Note: You would also need to move the email logic inside or after the transaction to respect the short-circuit.

🤖 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/module/recruiter/recruiter.service.ts` around lines 355 - 363, The
duplicate-status early return (comparing application.status to status) should be
performed inside the database transaction to avoid the race window between the
check and prisma.$transaction; move the comparison into the transaction block
that calls this._updateApplicationStatus (or delegate to that helper) and ensure
any email-sending logic that relies on the short-circuit is moved to after the
transaction completes (or executed within the same tx-controlled flow) so no
emails are sent when the status stayed the same; reference applicationId,
recruiterId, prisma.$transaction and the _updateApplicationStatus helper when
making this change.
🤖 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/src/module/recruiter/recruiter.service.ts`:
- Around line 477-485: The current catch in recruiter.service.ts using
JSON.parse(JSON.stringify(evaluationScores)) doesn't validate client input;
replace this with a proper schema validation at the request boundary (or inside
the service) for evaluationScores: define a Zod (or Joi) schema that describes
the expected record shape (keys -> { score: number, comment?: string }),
validate evaluationScores (e.g., safeParse) and on failure throw or return the
existing 422 error with validation details; remove the
JSON.parse(JSON.stringify(...)) deep-clone and use the validated value as
parsedScores so downstream code uses a type-safe, validated object.

---

Duplicate comments:
In `@server/src/module/recruiter/recruiter.service.ts`:
- Line 737: The totalPages calculation is using filter.limit but should use the
actual page size safeLimit (declared earlier), so update the assignment for
totalPages to use Math.ceil(total / safeLimit) (and guard against zero by using
Math.ceil(total / (safeLimit || 1))) to ensure pagination reflects the applied
cap; change the expression that sets totalPages (currently referencing
filter.limit) to reference safeLimit instead.

---

Nitpick comments:
In `@server/src/module/recruiter/recruiter.service.ts`:
- Around line 355-363: The duplicate-status early return (comparing
application.status to status) should be performed inside the database
transaction to avoid the race window between the check and prisma.$transaction;
move the comparison into the transaction block that calls
this._updateApplicationStatus (or delegate to that helper) and ensure any
email-sending logic that relies on the short-circuit is moved to after the
transaction completes (or executed within the same tx-controlled flow) so no
emails are sent when the status stayed the same; reference applicationId,
recruiterId, prisma.$transaction and the _updateApplicationStatus helper when
making this change.
🪄 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: 71e87d59-01d8-4ebb-994e-2c6389e943b3

📥 Commits

Reviewing files that changed from the base of the PR and between e727e1d and 60f10eb.

📒 Files selected for processing (2)
  • server/src/module/opensource/opensource.routes.ts
  • server/src/module/recruiter/recruiter.service.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/src/module/opensource/opensource.routes.ts

Comment on lines +477 to +485
// Fix for #1116: Gracefully catch malformed JSON data during evaluation
let parsedScores;
try {
parsedScores = JSON.parse(JSON.stringify(evaluationScores));
} catch (error) {
const err = new Error("Invalid JSON format in evaluation data");
(err as any).status = 422;
throw err;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

JSON parsing doesn't validate malformed input as intended.

The comment states this is "Fix for #1116: Gracefully catch malformed JSON data during evaluation," but JSON.parse(JSON.stringify(evaluationScores)) only performs deep cloning. If evaluationScores is already a JavaScript object (which it is after Express middleware parses the request body), this operation won't fail unless there are circular references—not the same as catching malformed JSON from the client.

To properly validate the structure and types of evaluationScores, use schema validation (e.g., Zod) in the controller or route validation layer before calling this service method.

📋 Alternative: Use Zod for proper validation

In the controller or validation layer, define and apply a schema:

const evaluationScoresSchema = z.record(
  z.string(),
  z.object({
    score: z.number(),
    comment: z.string().optional(),
  })
);

Then validate before calling the service:

const parsed = evaluationScoresSchema.safeParse(evaluationScores);
if (!parsed.success) {
  return res.status(400).json({ message: "Invalid evaluation scores", errors: parsed.error });
}

This properly catches malformed input at the boundary rather than attempting to handle it in the service layer.

🤖 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/module/recruiter/recruiter.service.ts` around lines 477 - 485, The
current catch in recruiter.service.ts using
JSON.parse(JSON.stringify(evaluationScores)) doesn't validate client input;
replace this with a proper schema validation at the request boundary (or inside
the service) for evaluationScores: define a Zod (or Joi) schema that describes
the expected record shape (keys -> { score: number, comment?: string }),
validate evaluationScores (e.g., safeParse) and on failure throw or return the
existing 422 error with validation details; remove the
JSON.parse(JSON.stringify(...)) deep-clone and use the validated value as
parsedScores so downstream code uses a type-safe, validated object.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working gssoc:approved Approved for GSSoC scoring gssoc level:critical High-impact or critical changes quality:exceptional Exceptional implementation quality scope:backend Changes to server-side / API code type:bug Bug fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[recruiter] Pagination limit on talent search not server-enforced — can request unlimited records

1 participant