Skip to content

feat: implement donations API with campaign title support - #47

Merged
pragmaticAweds merged 4 commits into
Fundable-Protocol:devfrom
Skinny001:feat/donations-api
Jun 29, 2026
Merged

pragmaticAweds merged 4 commits into
Fundable-Protocol:devfrom
Skinny001:feat/donations-api

Conversation

@Skinny001

@Skinny001 Skinny001 commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

This pull request introduces a comprehensive implementation of the donation API, including route definitions, controller logic, validation, and data access layers. It also adds robust authentication and authorization middleware, as well as thorough test coverage for validation logic.

Key changes include:

Donation API Implementation

  • Added the DonationEntity model with full schema, including new fields, database indexes, and enum support for status and network. (src/components/v1/Donation/donation.entity.ts)
  • Implemented the DonationResponseDto, PaginatedResponse, and DonationStatsDto types to standardize API responses. (src/components/v1/Donation/donation.dto.ts)
  • Created the donation controller with handlers for creating, listing, and retrieving donations, as well as campaign/user-specific queries and donation statistics. (src/components/v1/Donation/donation.controller.ts)
  • Defined and registered donation routes, including authentication, authorization, and request validation middleware for each endpoint. (src/components/v1/Donation/donation.routes.ts)

Authentication & Authorization

  • Enhanced JWT authentication middleware to support additional claims (role, userType) and added a new requireAdminApi middleware for admin-only endpoints. (src/appMiddlewares/jwtAuth.api.ts)

Validation & Testing

  • Added comprehensive tests for donation input and query validation, covering required fields, data types, address formats, and query parameter parsing. (src/__tests__/donation.validation.test.ts)- Add DonationEntity, service, controller, routes, validation, DTO
  • Add campaign title column to CampaignEntity and donation.campaign_title
  • Support filtering (date range, amount, status, confirmed, campaign, donor)
  • Support sorting (created_at, amount, status, confirmed_at, campaign_ref, campaign_title, donor_address)
  • Support search across donorAddress, donorName, campaignRef, campaignTitle
  • Add pagination with stats aggregation endpoint
  • Add admin guard middleware for GET /api/v1/donations/
  • Register spec-compliant routes under /campaigns/:id/donations and /users/:userId/donations
  • Add 2 migrations for donations table and title columns
  • 33 tests covering service, validation, edge cases

Summary

Describe what changed and why.

Area

  • [ #] Backend API (src/)
  • Indexer common infrastructure (indexer/common/)
  • Streams indexer (indexer/streams/)
  • Distributions indexer (indexer/distributions/)
  • Tooling, docs, CI, or Docker

Scope

  • This PR addresses one scoped issue or task
  • Unrelated formatting, generated files, and follow-up work were left out
  • Backend and indexer package boundaries were respected

Verification

  • [ #] bun run type-check
  • [ #] bun run test
  • [# ] bun run lint
  • bun run indexer:type-check if indexer files changed
  • bun run indexer:test if indexer files changed
  • bun run indexer:lint if indexer files changed

Indexer Safety

  • Event processing changes are idempotent or do not affect event processing
  • Cursor changes advance only after successful processing
  • Event names and payload shapes were confirmed from contracts, if relevant
  • Backfill and replay behavior was considered, if relevant

Notes

Closes #9

Summary by CodeRabbit

  • New Features

    • Added end-to-end donation management: create donations, view donation details, list donations by campaign/user, view “my donations”, and retrieve donation statistics.
    • Added donation filtering/sorting/pagination and confirmed-status handling.
    • Added campaign title support, reflected in donation-related data.
  • Bug Fixes

    • Improved donation and campaign input validation (addresses, amounts, IDs, and date ranges) and stricter query parameter parsing.
    • Strengthened access control for admin-only donation listings and self-only access to personal donation records.
  • Tests

    • Added unit test coverage for donation services and validation schemas.

- Add DonationEntity, service, controller, routes, validation, DTO
- Add campaign title column to CampaignEntity and donation.campaign_title
- Support filtering (date range, amount, status, confirmed, campaign, donor)
- Support sorting (created_at, amount, status, confirmed_at, campaign_ref, campaign_title, donor_address)
- Support search across donorAddress, donorName, campaignRef, campaignTitle
- Add pagination with stats aggregation endpoint
- Add admin guard middleware for GET /api/v1/donations/
- Register spec-compliant routes under /campaigns/:id/donations and /users/:userId/donations
- Add 2 migrations for donations table and title columns
- 33 tests covering service, validation, edge cases
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Skinny001, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 37 minutes and 27 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8994f581-9972-4a01-b8cb-db690859af1a

📥 Commits

Reviewing files that changed from the base of the PR and between ebfcc04 and 1359a7a.

📒 Files selected for processing (3)
  • src/__tests__/campaign.service.test.ts
  • src/__tests__/donation.service.test.ts
  • src/components/v1/campaign/campaign.service.ts
📝 Walkthrough

Walkthrough

Adds donation API validation, persistence, service, auth, and routing for create/list/filter/stats flows, plus campaign title support in the campaign module and unit tests for donation and campaign behaviors.

Changes

Donation API and campaign title changes

Layer / File(s) Summary
Donation contracts
src/components/v1/Donation/donation.dto.ts, src/components/v1/Donation/donation.validation.ts, src/types/enums.ts, src/__tests__/donation.validation.test.ts
Donation response, pagination, stats, status, and query schemas define the donation API shapes and validation rules, with tests covering creation, listing, and params parsing.
Donation persistence
src/components/v1/Donation/donation.entity.ts, src/migrations/CreateDonationsTable1760000000002.js, src/config/persistence/data-source.ts
DonationEntity maps the donations table, the migration creates the table and indexes, and the data source registers the entity.
Donation service
src/components/v1/Donation/donation.service.ts, src/__tests__/donation.service.test.ts
DonationService creates donations, lists and filters results, fetches by id, aggregates stats, and is covered by repository-backed unit tests.
Auth and controllers
src/appMiddlewares/jwtAuth.api.ts, src/components/v1/Donation/donation.controller.ts
JWT claims now carry role and userType, admin access is checked from those claims, and the donation controller handles create/list/get/stat responses plus authenticated my-donations requests.
Route wiring
src/components/v1/Donation/donation.routes.ts, src/components/v1/routes.api.v1.ts
The new donation router and V1 router registration expose create, list, stats, per-campaign, per-user, my-donations, and by-id endpoints with auth and validation middleware.
Campaign title support
src/components/v1/campaign/campaign.validation.ts, src/components/v1/campaign/campaign.entity.ts, src/components/v1/campaign/campaign.service.ts, src/components/v1/campaign/campaign.controller.ts, src/components/v1/campaign/campaign.routes.ts, src/migrations/AddCampaignTitleColumns1760000000003.js, src/__tests__/campaign.service.test.ts
Campaign create validation, entity mapping, service, controller, migration, and tests add an optional title field and persist it through createCampaign.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant donation.routes
  participant donation.controller
  participant DonationService
  participant donationRepository
  Client->>donation.routes: HTTP request
  donation.routes->>donation.controller: validated handler call
  donation.controller->>DonationService: create/list/get/stats
  DonationService->>donationRepository: save / findOne / createQueryBuilder
  donationRepository-->>DonationService: entities / raw aggregates
  DonationService-->>donation.controller: DTOs / paginated data / stats
  donation.controller-->>Client: sendSuccess / sendError
Loading
sequenceDiagram
  participant campaign.controller
  participant CampaignService
  participant cairoClient
  participant campaignRepository
  campaign.controller->>CampaignService: createCampaign(parsed.title)
  CampaignService->>campaignRepository: save reserved campaign row
  CampaignService->>cairoClient: createCampaign(...)
  cairoClient-->>CampaignService: chain campaignId and transactionHash
  CampaignService->>campaignRepository: update reserved row
  CampaignService-->>campaign.controller: campaign response with title
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Fundable-Protocol/Backend#11: Adds the initial donation DTO/entity/validation scaffolding that this PR extends into service, routing, and tests.
  • Fundable-Protocol/Backend#17: Modifies the same donation DTO area that this PR replaces with donation-specific response, pagination, and stats types.
  • Fundable-Protocol/Backend#18: Touches the same campaign create flow and shared middleware path that now carries the optional title field.

Poem

A bunny hopped through API glade,
Where donation trails and titles played.
With stats and sorts and guards so neat,
The carrots of code smell mighty sweet.
🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The API covers the requested endpoints, pagination, filtering, sorting, stats, and privacy behavior, but search does not include campaign descriptions from #9. Add campaign description search to the donation query layer and update tests/docs to reflect the full issue scope.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding a donations API with campaign title support.
Description check ✅ Passed The description includes the required summary, area, scope, verification, and notes context, even though some checklist formatting is messy.
Out of Scope Changes check ✅ Passed The extra campaign title, auth, migration, and test changes are directly related to the donations API scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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.

@drips-wave

drips-wave Bot commented Jun 27, 2026

Copy link
Copy Markdown

@Skinny001 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 12

🤖 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 `@src/__tests__/donation.service.test.ts`:
- Around line 8-10: The query-builder mock in donation.service tests is only
parsing “=” clauses via extractColName, so `!=`, `>=`, `<=`, and `ILIKE` are
being simulated incorrectly and can produce false positives. Update the mocked
`where`/filter handling in `donation.service.test.ts` to evaluate each clause as
a real predicate over the in-memory rows instead of inferring a column name, and
make sure the tests around `confirmed`, date range, amount range, and search
paths use the same predicate-based behavior.

In `@src/components/v1/campaign/campaign.service.ts`:
- Around line 102-110: The campaignCount increment in CampaignService is
currently read-modify-write and can lose updates under concurrent requests.
Update the logic in the campaign.service.ts flow to perform the increment
atomically in the database, using the userRepository update path instead of
first calling findOne and computing nextCount in memory. Keep the fix localized
around the userRepository.update call so it applies COALESCE(campaign_count, 0)
+ 1 in one SQL statement.
- Around line 37-86: The campaign creation flow in campaign.service.ts is racy
because `findOne()` is only a precheck and `cairoClient.createCampaign()` can
run before `campaign_ref` is durably reserved. Change the `createCampaign` path
to reserve the ref/idempotency key in the database first, then call
`cairoClient.createCampaign`, and keep the final save guarded by the unique
`campaign_ref` constraint. In the duplicate path, translate the unique-violation
handling in `CampaignService` to the duplicate 409 instead of letting it bubble
as a generic error, and preserve the existing error codes for the other early
validation branches.

In `@src/components/v1/campaign/campaign.validation.ts`:
- Around line 34-38: The campaign title validation currently allows
whitespace-only input because `campaign.validation.ts` uses `title` with
`z.string().trim().max(255).optional()`, which can normalize `"   "` into an
empty string. Update the `title` schema in the validation object to reject empty
strings after trimming, or convert trimmed empty strings to `undefined`, so the
field is represented consistently and does not persist as `""` in addition to
missing/null.

In `@src/components/v1/Donation/donation.controller.ts`:
- Around line 28-35: The createDonation handler is trusting client-provided
donor ownership, so update the createDonation flow in donation.controller to use
IRequest and derive donorId from req.auth.userId instead of forwarding donorId
from req.body. Keep the request body for donation fields only, and if a
wallet/address is still accepted, verify it against the authenticated user
before using it. Make sure the createDonation call receives the authenticated
donor identity consistently so /users/:userId/donations and /users/me/donations
can’t be polluted by spoofed IDs.

In `@src/components/v1/Donation/donation.entity.ts`:
- Around line 44-45: The DonationEntity.blockNumber field is typed as
number|null even though the bigint column hydrates as a string at runtime, so
update the end-to-end typing to match the actual value or add a transformer.
Adjust DonationEntity.blockNumber and DonationResponseDto.blockNumber together,
and if you choose normalization, apply it in the entity mapping/transformer so
callers receive a consistent value from the Donation flow.

In `@src/components/v1/Donation/donation.routes.ts`:
- Line 33: The /stats route in the donation router is currently the only global
read endpoint that bypasses auth, so protect it consistently with the other
admin-only donation routes. Update the routing setup in donation.routes.ts
around router.get('/stats', getDonationStats) to apply the same
requireJwtAuthApi and requireAdminApi guards used for GET /donations, or move
public access to a separately reviewed campaign-scoped stats handler.

In `@src/components/v1/Donation/donation.service.ts`:
- Around line 269-294: The DonationService.formatResponse method is exposing
donor identity fields even when isAnonymous is true, so public donation
endpoints are still deanonymizing anonymous donations. Update the response
shaping in DonationService/formatResponse so donorAddress, donorName, and
message are redacted or omitted for public callers when the donation is
anonymous, and only included for the donor or admin views. Consider splitting
the DTO mapping into public and admin variants, or gating the sensitive fields
on the caller’s role/context while keeping the rest of DonationResponseDto
unchanged.

In `@src/components/v1/Donation/donation.validation.ts`:
- Around line 51-63: The Donation validation schema currently only checks for
decimal strings, but amount, usdAmount, and gasFee must also respect the
database’s decimal(65,30) limits. Update the zod rules in donation.validation.ts
for these fields to enforce max precision and scale (and any required
sign/format constraints) so values accepted by the request schema cannot exceed
what the persistence layer can store. Keep the change localized to the donation
schema definitions for amount, usdAmount, and gasFee.

In `@src/components/v1/routes.api.v1.ts`:
- Around line 33-38: The user donations endpoints are missing an
ownership/access check, so any authenticated user can query another user’s
donation history. Update the route wiring in routes.api.v1.ts for both the
users/:userId/donations and donations/users/:userId paths to enforce that
req.params.userId matches req.auth.userId unless the caller has admin
privileges, ideally via a shared authorization middleware before
getUserDonations. Keep policyMiddleware and requireJwtAuthApi, but add the same
self-or-admin guard to both route definitions so the restriction is consistent.

In `@src/migrations/CreateDonationsTable1760000000002.js`:
- Around line 19-43: The donations migration currently adds only a normal index
for transaction_hash, which does not prevent duplicate on-chain donations from
being inserted on retries. Update CreateDonationsTable1760000000002 so
transaction_hash is enforced as unique in the donations table, and adjust the
index/constraint definition accordingly using the existing migration/queryRunner
setup to preserve idempotency for donation creation and aggregate stats.
- Around line 39-43: The donation table migration is missing indexes for the new
primary filter columns, so update CreateDonationsTable1760000000002 to add
indexes for donor_id and donation_token alongside the existing create index
calls in the migration’s up path. Make sure the corresponding down path in the
same migration removes those indexes as well, and keep the changes aligned with
the existing queryRunner.query patterns used for donations_campaign_id_idx and
donations_donor_address_idx.
🪄 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: f1b160b7-adb6-478c-9a17-728e8c824a9f

📥 Commits

Reviewing files that changed from the base of the PR and between bcfa08d and 816f22b.

📒 Files selected for processing (19)
  • src/__tests__/donation.service.test.ts
  • src/__tests__/donation.validation.test.ts
  • src/appMiddlewares/jwtAuth.api.ts
  • src/components/v1/Donation/donation.controller.ts
  • src/components/v1/Donation/donation.dto.ts
  • src/components/v1/Donation/donation.entity.ts
  • src/components/v1/Donation/donation.routes.ts
  • src/components/v1/Donation/donation.service.ts
  • src/components/v1/Donation/donation.validation.ts
  • src/components/v1/campaign/campaign.controller.ts
  • src/components/v1/campaign/campaign.entity.ts
  • src/components/v1/campaign/campaign.routes.ts
  • src/components/v1/campaign/campaign.service.ts
  • src/components/v1/campaign/campaign.validation.ts
  • src/components/v1/routes.api.v1.ts
  • src/config/persistence/data-source.ts
  • src/migrations/AddCampaignTitleColumns1760000000003.js
  • src/migrations/CreateDonationsTable1760000000002.js
  • src/types/enums.ts

Comment thread src/__tests__/donation.service.test.ts Outdated
Comment thread src/components/v1/campaign/campaign.service.ts Outdated
Comment thread src/components/v1/campaign/campaign.service.ts Outdated
Comment thread src/components/v1/campaign/campaign.validation.ts Outdated
Comment thread src/components/v1/Donation/donation.controller.ts Outdated
Comment thread src/components/v1/Donation/donation.service.ts Outdated
Comment thread src/components/v1/Donation/donation.validation.ts Outdated
Comment on lines +33 to +38
router.get(
'/users/:userId/donations',
requireJwtAuthApi,
policyMiddleware(listUserDonationsQuerySchema, 'query'),
getUserDonations
);

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

This route leaks any user’s donation history to any logged-in user.

requireJwtAuthApi only authenticates; it does not ensure req.params.userId === req.auth.userId. As written, one user can call /users/{otherUserId}/donations and read that account’s donations. Restrict this to self/admin access, and keep the same rule on the duplicate /donations/users/:userId route.

🤖 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 `@src/components/v1/routes.api.v1.ts` around lines 33 - 38, The user donations
endpoints are missing an ownership/access check, so any authenticated user can
query another user’s donation history. Update the route wiring in
routes.api.v1.ts for both the users/:userId/donations and
donations/users/:userId paths to enforce that req.params.userId matches
req.auth.userId unless the caller has admin privileges, ideally via a shared
authorization middleware before getUserDonations. Keep policyMiddleware and
requireJwtAuthApi, but add the same self-or-admin guard to both route
definitions so the restriction is consistent.

Comment thread src/migrations/CreateDonationsTable1760000000002.js Outdated
Comment thread src/migrations/CreateDonationsTable1760000000002.js Outdated
@pragmaticAweds

Copy link
Copy Markdown
Contributor

Hi @Skinny001

Thank you for your awesome contribution, however after analyzing your implementation, there are some minor fixes to be done. Kindly fix them to merge your PR asap.

Also do not forget to use fundable.finance to offramp.

@Skinny001

Copy link
Copy Markdown
Contributor Author

Hi @Skinny001

Thank you for your awesome contribution, however after analyzing your implementation, there are some minor fixes to be done. Kindly fix them to merge your PR asap.

Also do not forget to use fundable.finance to offramp.

@pragmaticAweds working on it already

- donation.service.test.ts: mock QB parses !=, >=, <=, ILIKE predicates
- campaign.service.ts: atomic COALESCE campaignCount increment
- campaign.service.ts: reserve campaign_ref in DB before blockchain call
- campaign.validation.ts: reject whitespace-only title
- donation.controller.ts: derive donorId from req.auth.userId
- donation.entity.ts/dto.ts/validation.ts: blockNumber typed as string | null
- donation.routes.ts: protect /stats with auth + admin guards
- donation.service.ts: redact anonymous donor fields in formatResponse
- donation.validation.ts: constrain amount/usdAmount/gasFee to decimal(65,30)
- routes.api.v1.ts / jwtAuth.api.ts: add requireSelfOrAdmin middleware
- CreateDonationsTable migration: unique transaction_hash, add donor_id
  and donation_token indexes
- campaign.service.test.ts: update mock to simulate unique constraint
  violations and atomic campaignCount increment
@Skinny001

Copy link
Copy Markdown
Contributor Author

Hi @Skinny001

Thank you for your awesome contribution, however after analyzing your implementation, there are some minor fixes to be done. Kindly fix them to merge your PR asap.

Also do not forget to use fundable.finance to offramp.

@pragmaticAweds pls check now

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/__tests__/donation.service.test.ts (1)

10-16: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep the query mock aligned with production amount and search clauses.

CAST(donation.amount AS numeric) >= ... does not parse here, and the OR-based search clause only checks the first matched column. That leaves amount filtering and non-address search paths under-modeled. This is the remaining part of the earlier query-builder mock fidelity concern.

Suggested direction
 type PredicateOp = '=' | '!=' | '>=' | '<=' | 'ILIKE';
 
 const parseClause = (
     clause: string
-): { col: string; op: PredicateOp } | null => {
-    const match = clause.match(/\.(\w+)\s*(!?=|>=|<=|ILIKE)/);
-    if (!match) return null;
-    return { col: match[1], op: match[2] as PredicateOp };
+): { cols: string[]; op: PredicateOp } | null => {
+    const opMatch = clause.match(/\s(>=|<=|!=|=|ILIKE)\s*:/i);
+    const cols = [...clause.matchAll(/donation\.(\w+)/g)].map((m) => m[1]);
+    if (!opMatch || cols.length === 0) return null;
+    return { cols, op: opMatch[1].toUpperCase() as PredicateOp };
 };

Then let ILIKE match any parsed column, while scalar operators use the parsed amount/status/date column.

Also applies to: 25-40, 72-87

🤖 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 `@src/__tests__/donation.service.test.ts` around lines 10 - 16, The query mock
in the donation service tests is too narrow: parseClause only recognizes simple
dotted comparisons and the current matcher under-models production’s
CAST(donation.amount AS numeric) and OR-based search behavior. Update the mock
logic around parseClause and the related query matching helpers in
donation.service.test so amount filtering is recognized via the amount
column/scalar operators, while ILIKE can match any parsed searchable column
instead of only the first address field. Keep the mock aligned with the
production query-builder clauses for amount, status, date, and search paths.
🤖 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 `@src/__tests__/campaign.service.test.ts`:
- Around line 75-89: The mock in campaign.service.test.ts is too permissive
because execute() increments every record with a numeric campaignCount whenever
qbWhereClause contains id =, which can hide incorrect user targeting. Tighten
the mock in the where/execute path so it uses the params passed into where(...)
to identify the intended user id and only increments that matching record in
repo.data, ensuring the campaign service is validated against the exact id being
updated.

In `@src/components/v1/campaign/campaign.service.ts`:
- Around line 88-100: The campaign reservation flow in
CampaignService.createCampaign is not resumable when cairoClient.createCampaign
or the subsequent campaignRepository update fails, leaving a partially reserved
row that blocks retries with DUPLICATE_CAMPAIGN_REF. Update the create/reserve
path to persist and detect an explicit pending/failed state for the
localCampaignId so retries can resume or reconcile the existing reservation
instead of returning 409 immediately; make the resume logic live alongside the
existing campaign_ref reservation and update step in createCampaign.

---

Duplicate comments:
In `@src/__tests__/donation.service.test.ts`:
- Around line 10-16: The query mock in the donation service tests is too narrow:
parseClause only recognizes simple dotted comparisons and the current matcher
under-models production’s CAST(donation.amount AS numeric) and OR-based search
behavior. Update the mock logic around parseClause and the related query
matching helpers in donation.service.test so amount filtering is recognized via
the amount column/scalar operators, while ILIKE can match any parsed searchable
column instead of only the first address field. Keep the mock aligned with the
production query-builder clauses for amount, status, date, and search paths.
🪄 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: 36db88c5-867b-42cd-b00d-b1193cced2f7

📥 Commits

Reviewing files that changed from the base of the PR and between 816f22b and ebfcc04.

📒 Files selected for processing (13)
  • src/__tests__/campaign.service.test.ts
  • src/__tests__/donation.service.test.ts
  • src/appMiddlewares/jwtAuth.api.ts
  • src/components/v1/Donation/donation.controller.ts
  • src/components/v1/Donation/donation.dto.ts
  • src/components/v1/Donation/donation.entity.ts
  • src/components/v1/Donation/donation.routes.ts
  • src/components/v1/Donation/donation.service.ts
  • src/components/v1/Donation/donation.validation.ts
  • src/components/v1/campaign/campaign.service.ts
  • src/components/v1/campaign/campaign.validation.ts
  • src/components/v1/routes.api.v1.ts
  • src/migrations/CreateDonationsTable1760000000002.js
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/migrations/CreateDonationsTable1760000000002.js
  • src/components/v1/Donation/donation.routes.ts
  • src/components/v1/Donation/donation.entity.ts
  • src/components/v1/campaign/campaign.validation.ts
  • src/components/v1/routes.api.v1.ts
  • src/components/v1/Donation/donation.dto.ts
  • src/components/v1/Donation/donation.service.ts
  • src/components/v1/Donation/donation.controller.ts
  • src/components/v1/Donation/donation.validation.ts

Comment thread src/__tests__/campaign.service.test.ts Outdated
Comment thread src/components/v1/campaign/campaign.service.ts
- campaign.service.test.ts: mock execute targets specific user via where params
- campaign.service.ts: resume failed reservations instead of 409
- donation.service.test.ts: parseClause handles CAST() and OR-group ILIKE
@Skinny001

Copy link
Copy Markdown
Contributor Author

Hi @Skinny001

Thank you for your awesome contribution, however after analyzing your implementation, there are some minor fixes to be done. Kindly fix them to merge your PR asap.

Also do not forget to use fundable.finance to offramp.

@pragmaticAweds Done

@pragmaticAweds
pragmaticAweds merged commit 47625db into Fundable-Protocol:dev Jun 29, 2026
1 check 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.

Create API Endpoint to Retrieve All Donations

2 participants