Skip to content

refactor(backend): consolidate delivery controllers, remove unsafe casts, unify query contract & extract SDK interfaces (#219, #220, #221, #222) - #285

Merged
Tybravo merged 2 commits into
SwiftChainn:mainfrom
johnkoye19:refactor/backend-consolidation-219-222
Sep 30, 2026
Merged

Tybravo merged 2 commits into
SwiftChainn:mainfrom
johnkoye19:refactor/backend-consolidation-219-222

Conversation

@johnkoye19

Copy link
Copy Markdown

Refactor: Consolidate Delivery Controllers, Remove Unsafe Casts, Unify Query Contract & Extract Provider Interfaces

Overview

This PR implements four related backend refactoring issues in one branch, moving the SwiftChain backend toward the target layered architecture: single canonical controller per resource, typed service boundaries (no unsafe casts), one shared query contract for all list endpoints, and external SDK clients hidden behind interfaces injected via DI.

Related Issues

Closes #219
Closes #220
Closes #221
Closes #222

Changes

#219 — Collapse triple delivery controllers

  • [ADD] getDeliveryETA handler in the canonical src/controllers/delivery.controller.ts.
  • [MODIFY] src/controllers/deliveryController.ts reduced to a re-export shim for backward compatibility.
  • [MODIFY] Deleted src/controllers/deliveryCrudController.ts (third duplicate implementation).
  • [MODIFY] src/di/tokens.ts + src/di/container.ts: removed tokens/registrations pointing at deleted controller.
  • [MODIFY] docs/architecture.md updated to reflect the single-controller layout.

#220 — Remove as unknown as / as any casts

  • [ADD] UserDTO + toUserDTO typed projection in src/services/authService.ts (replaces as unknown as user shaping).
  • [MODIFY] src/services/deliveryService.ts: typed AppError instead of cast-laden error handling.
  • [MODIFY] src/controllers/notificationController.ts: zod parse instead of as any body access.
  • [MODIFY] src/repositories/BaseRepository.ts, src/repositories/DeliveryRepository.ts, src/services/userService.ts: casts removed with properly typed queries/projections (also dropped unused IDelivery import).

#221 — Query middleware rollout to all list endpoints

  • [MODIFY] src/routes/delivery.routes.ts: buildQueryOptions on GET / and GET /archived (sortable createdAt; filterable status, driver; searchable trackingNumber, customer.name, customer.phone).
  • [MODIFY] src/routes/userRoutes.ts + src/controllers/userController.ts + src/services/userService.ts: GET /users/deleted now paginates/sorts/filters via the middleware (keeps legacy top-level pagination key for API compatibility).
  • [MODIFY] src/routes/disputeRoutes.ts + src/controllers/disputeController.ts + src/services/disputeService.ts: admin list endpoint migrated; ObjectId-format validation preserved.
  • [MODIFY] src/routes/fleetRoutes.ts + src/controllers/fleetController.ts: fleet list migrated; legacy pagination.pages key replaced by the shared meta contract.
  • [MODIFY] src/routes/notificationRoutes.ts + src/controllers/notificationController.ts + src/services/notificationService.ts + src/repositories/NotificationRepository.ts + src/repositories/types.ts: history list migrated (zod query schema replaced by the middleware).
  • [MODIFY] src/routes/webhookRoutes.ts + src/controllers/webhookController.ts + src/services/webhookService.ts: merchant webhook list migrated from an unpaginated array to a paged, filterable result.
  • [MODIFY] src/routes/eventLogRoutes.ts + src/controllers/eventLogController.ts + src/services/eventLogService.ts: /unprocessed now paged (limit clamped to 100).
  • [ADD] docs/query-contract.md: parameters table, meta JSON shape, per-endpoint config matrix.
  • [ADD] tests/integration/queryContract.test.ts: 9 supertest-based integration tests against MongoMemoryServer covering all 7 endpoints — page/limit, whitelisted filters, search, sort, 400 on bad sort/page/operator, identical meta keys across endpoints, fleets pages key removed, webhook/notification scoping.
  • [MODIFY] Existing suites updated for the new response shape: tests/delivery.test.ts, tests/dispute.test.ts, tests/disputeRoutes.test.ts, tests/notificationService.test.ts.
  • [MODIFY] src/middlewares/queryMiddleware.ts: added flattenQueryFilter helper for services that need to inspect whitelisted filter keys.

#222 — Extract external SDK clients behind interfaces

  • [ADD] src/services/providers/routingProvider.ts: IRoutingProvider (+ Coordinates, RouteInfo, ETARequest, ETAResponse).
  • [ADD] src/services/providers/sorobanRpcClient.ts: ISorobanRpcClient mirroring the rpc.Server surface (getAccount, getHealth, getTransaction, getEvents, getLatestLedger, getNetwork, prepareTransaction, sendTransaction) with derived types.
  • [ADD] src/services/providers/imagesStorage.ts: IImagesStorage reusing the existing storage abstraction.
  • [ADD] src/services/providers/adapters.ts: GoogleMapsRoutingProvider (axios + Haversine fallback + FALLBACK_SPEEDS_KMH), SdkSorobanRpcClient (delegates 1:1 to rpc.Server), defaultSorobanRpcClient.
  • [MODIFY] src/services/routingService.ts rewritten as a facade over the injected provider; consumers (deliveryService, poolingService) take IRoutingProvider via constructor.
  • [MODIFY] stellarService, soroban.service, transactionService, indexerService, escrowHandlers now depend on ISorobanRpcClient (setEscrowRpcClient() test seam in escrowHandlers); stellarService.send() returns a typed SendTransactionResult union — the synthetic bad-seq response is typed RawSendTransactionResponse without as unknown as.
  • [MODIFY] src/di/tokens.ts + src/di/container.ts: routingProvider, sorobanRpcClient, imagesStorage tokens registered.
  • [ADD] tests/providers.test.ts: 8 tests (Google Maps path/fallback/anti-meridian/error-wrap, SDK adapter delegation, routing facade, PoolingService with fake provider).

Enabling bug fixes (required for #221's integration tests)

Two pre-existing upstream crashes made every app-booting test suite fail before any assertion could run:

  • [MODIFY] src/middlewares/rateLimiter.ts: TEST_UNLIMITED_MAX was referenced but never defined (upstream file uses it at lines 14/31) — added const TEST_UNLIMITED_MAX = 1_000_000;.
  • [MODIFY] src/routes/delivery.routes.ts: validateRequest(...) was called but never imported — changed to validate(...), matching the existing import.

Verification Results

All commands run with the project's pnpm 9 toolchain (node_modules/.bin/... directly):

Command Result
node_modules/.bin/tsc --noEmit 79 → 76 errors (baseline 79; diff vs baseline shows 0 new errors, 3 removed by the two bug fixes above; remaining errors are pre-existing upstream, dominated by duplicate env/AWS keys in src/config/env.ts)
node_modules/.bin/eslint src --ext .ts 0 errors in touched files (1 remaining pre-existing upstream error in untouched src/config/stellar.ts; 49 warnings pre-existing)
node_modules/.bin/eslint tests --ext .ts 0 errors, 30 warnings (pre-existing)
CI=true MONGO_URI=... JWT_SECRET=... node_modules/.bin/jest 16 failing suites vs 30 at baseline — 14 suites fixed, 0 new failures. 926/1008 tests pass. Remaining 16 failures are pre-existing upstream test/model drift in suites that could not boot before (details below)
node_modules/.bin/tsc (build) Same pre-existing errors as --noEmit; no new build breakage

New/updated suites: tests/providers.test.ts 8/8 PASS, tests/integration/queryContract.test.ts 9/9 PASS, tests/di.container.test.ts 36 PASS, tests/delivery.test.ts PASS (was FAIL), tests/notificationService.test.ts PASS (was FAIL), tests/health.test.ts, tests/monitorRoutes.test.ts, tests/user.schema.hooks.test.ts, tests/transaction.escrowLock.test.ts, tests/auth.test.ts, tests/admin.test.ts, tests/security.test.ts etc. all PASS now (were baseline FAIL due to the two bug fixes).

The 16 still-failing suites all fail at baseline too (verified with comm against the baseline FAIL list). They are pre-existing upstream drift, now merely visible because the suites can boot: e.g. dispute.test.ts/disputeRoutes.test.ts use non-admin tokens against the admin-only GET /disputes (upstream gated it admin already), fleet.test.ts POSTs {name} only while the Fleet model requires treasuryAddress/businessMetadata, swagger.test.ts expects docs for routes mounted upstream without annotations (/deliveries/fee-estimate, /pooling/*).

Acceptance Criteria

#219 — Single canonical delivery controller

  • One canonical delivery.controller.ts used by routes and DI
  • Duplicate controllers deleted / reduced to a compatibility shim
  • DI tokens and container cleaned up
  • Architecture docs updated
  • No new tsc/eslint errors introduced

#220 — No unsafe casts

  • as unknown as removed from auth, delivery, notification, repository, and user layers
  • Typed DTO projections (UserDTO/toUserDTO) replace cast-based shaping
  • Zod parsing replaces as any in notification controller
  • tsc shows zero new errors vs baseline

#221 — Shared query contract on every list endpoint

  • All 7 list endpoints accept page, limit, sort, whitelisted filters, search
  • Identical buildPaginationMeta meta structure returned by every endpoint
  • 400 on unknown sort field, invalid page/limit, non-whitelisted filter operators
  • docs/query-contract.md documents parameters, meta shape, per-endpoint config
  • Integration tests cover every endpoint end to end (9/9 passing)
  • Legacy response keys preserved where consumers depend on them (/users/deleted pagination)

#222 — SDK clients behind interfaces

  • IRoutingProvider, ISorobanRpcClient, IImagesStorage defined in src/services/providers/
  • Concrete adapters (GoogleMapsRoutingProvider, SdkSorobanRpcClient) isolated in adapters.ts
  • Services consume interfaces, injected via awilix (routingProvider, sorobanRpcClient, imagesStorage tokens)
  • Test seams provided (setEscrowRpcClient, constructor injection) — no SDK mocking gymnastics in suites
  • as unknown as eliminated from stellarService (typed RawSendTransactionResponse + result union)

General

…sts, unify query contract & extract provider interfaces (SwiftChainn#219, SwiftChainn#220, SwiftChainn#221, SwiftChainn#222)

SwiftChainn#219 — collapse the triple delivery controllers into one canonical
src/controllers/delivery.controller.ts (plus a re-export shim and DI cleanup).

SwiftChainn#220 — remove `as unknown as` / `as any` casts across auth, delivery,
notification, repository and user layers by introducing typed projections
(UserDTO), zod parsing and proper AppError typing.

SwiftChainn#221 — roll the shared query middleware out to every list endpoint
(deliveries, users/deleted, disputes, fleets, notifications, webhooks,
event log) with a uniform buildPaginationMeta contract, documented in
docs/query-contract.md, and add tests/integration/queryContract.test.ts
covering all seven endpoints end to end.

SwiftChainn#222 — extract external SDK clients behind interfaces (IRoutingProvider,
ISorobanRpcClient, IImagesStorage) with swappable adapters, injected
through awilix DI, so services no longer import Google Maps or the
Stellar SDK directly.

Also fixes two pre-existing crashes that blocked app-boot test suites
(required for SwiftChainn#221's integration tests): rateLimiter TEST_UNLIMITED_MAX
undefined and delivery.routes validateRequest not imported.

Verification: tsc --noEmit errors 79 -> 76 (no new errors; 3 removed by
the bug fixes), eslint 0 errors in touched files, jest 16 failing suites
vs 30 at baseline (14 suites fixed, 0 new failures; remaining failures
are pre-existing upstream drift), tsc build unchanged.

Closes SwiftChainn#219
Closes SwiftChainn#220
Closes SwiftChainn#221
Closes SwiftChainn#222

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@drips-wave

drips-wave Bot commented Sep 30, 2026

Copy link
Copy Markdown

@johnkoye19 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

@johnkoye19

Copy link
Copy Markdown
Author

Verification Evidence (local, full toolchain)

All commands run from the repo root against the project's pnpm 9 toolchain (binaries invoked directly, e.g. node_modules/.bin/tsc).

TypeScript — no new errors

$ node_modules/.bin/tsc --noEmit
# 76 errors, all pre-existing upstream (baseline: 79)
# diff vs upstream baseline: 0 new, 3 removed
#   - src/middlewares/rateLimiter.ts(14/31): TEST_UNLIMITED_MAX undefined  -> fixed
#   - src/routes/delivery.routes.ts(22): validateRequest not imported      -> fixed
# remaining errors: pre-existing upstream (duplicate env/AWS keys in src/config/env.ts, etc.)

ESLint — 0 errors in touched files

$ node_modules/.bin/eslint src --ext .ts
# 50 problems: 1 error (pre-existing upstream, untouched src/config/stellar.ts), 49 warnings (pre-existing)
$ node_modules/.bin/eslint tests --ext .ts
# 30 problems: 0 errors, 30 warnings (pre-existing)

Tests — zero new failures, 14 suites fixed

$ CI=true MONGO_URI="mongodb://localhost:27017/swiftchain_test" JWT_SECRET="test-secret-key-16chars" node_modules/.bin/jest
Test Suites: 16 failed, 50 passed, 66 total   (baseline: 30 failed, 36 passed)
Tests:       82 failed, 926 passed, 1008 total
  • Fixed vs baseline (previously FAIL, now PASS): admin, auth, delivery, driverRating, driverVehicleDetails, escrow, health, integration/dashboard.integration, monitorRoutes, responseWrapper, security, transaction.escrowLock, uploadRoutes, user.schema.hooks — these suites could not even boot before the two enabling bug fixes.
  • New suites: tests/providers.test.ts 8/8 PASS · tests/integration/queryContract.test.ts 9/9 PASS · tests/di.container.test.ts 36 PASS.
  • Still failing (16): every one is on the upstream baseline FAIL list (verified with comm); they are pre-existing test/model drift that only became visible once the suites could boot — e.g. dispute*.test.ts uses non-admin tokens against the admin-only GET /disputes (gated admin at upstream), fleet.test.ts POSTs {name} only while the Fleet model requires treasuryAddress/businessMetadata, swagger.test.ts expects annotations for routes mounted upstream without docs (/deliveries/fee-estimate, /pooling/*).

Build

$ node_modules/.bin/tsc   # same pre-existing errors as --noEmit; no new build breakage

Note on CI: the workflow run for this PR is held in action_required pending a maintainer's approval (standard GitHub behavior for first-time contributors to this repo). Once approved, the run at https://github.com/SwiftChainn/SwiftChain_Backend/actions/runs/36697280746 will execute against commit eccc3c0.

@Tybravo
Tybravo merged commit 58b01a0 into SwiftChainn:main Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants