Skip to content

AI Prompt: Implement Stubs, Audit Services, and Retry Queue Handlers

Purpose: Replace all stub/placeholder implementations identified in the TODO.md "In-Code TODOs, Stubs, and Placeholders" scan (items 4–11) with working code, including unit tests and acceptance tests
Estimated Effort: 8–12 hours
Prerequisites: Working codebase with passing build, lint, and existing tests
Reference: TODO.md section "In-Code TODOs, Stubs, and Placeholders" (items 4–11)
Output: Fully functional audit services in shipping-service/gridflock-service/print-service, a real fulfillment service in shipping-service, and working retry handlers for print job creation and cancellation in both order-service and shipping-service
Status:COMPLETED (Phase 1 completed 2026-02-28, Phase 2 completed 2026-02-28, Phase 3 completed 2026-02-28, Phase 4 completed 2026-02-28, Phase 5 completed 2026-02-28)


🎯 Mission

Implement all stub and placeholder code identified in the TODO.md scan (items 4–11). Each item falls into one of two categories:

Category A — Audit Service Stubs (items 4, 6, 7):
The AuditService and AUDIT_ACTIONS in shipping-service, gridflock-service, and print-service are no-op stubs. Replace them with real audit logging that matches the reference implementation in order-service.

Category B — Retry Queue Placeholders (items 5, 8, 9, 10, 11):
The FulfillmentService stub in shipping-service, and the PRINT_JOB_CREATION and CANCELLATION retry handlers in both order-service and shipping-service are placeholders. Implement them with real business logic.

After implementation, update TODO.md to mark each item as resolved with the implementation date.


📐 Architecture

Audit Service Pattern

order-service (reference)                shipping-service / gridflock-service / print-service
┌────────────────────────────────┐       ┌────────────────────────────────┐
│  AuditService                  │       │  AuditService                  │
│  ├── log(input)                │  ──>  │  ├── log(input)                │
│  ├── logAuth(action, req, opt) │ copy  │  ├── logAuth(action, req, opt) │
│  ├── logPermissionDenied(...)  │       │  └── getClientIp(req)          │
│  ├── logUserManagement(...)    │       │                                │
│  └── getClientIp(req)          │       │  AuditRepository               │
│                                │       │  └── create(input)             │
│  AuditRepository               │       │                                │
│  └── create(input)             │       │  AUDIT_ACTIONS (full constant) │
│                                │       └────────────────────────────────┘
│  AUDIT_ACTIONS (full constant) │
└────────────────────────────────┘

Retry Queue Handler Pattern

RetryQueueProcessor.processJob(job)
├── FULFILLMENT        → FulfillmentService.createFulfillment()       ✅ Already works
├── NOTIFICATION       → NotificationsService.sendEmail()             ✅ Already works
├── PRINT_JOB_CREATION → PrintJobsService.createPrintJobsForLineItem() ❌ Placeholder (items 9, 11)
├── CANCELLATION       → CancellationService.cancelOrder()            ❌ Placeholder (items 8, 10)
└── SHIPMENT           → (shipping-service only)                      ✅ Already works

📋 Implementation

Phase 1: Audit Services for shipping-service, gridflock-service, and print-service (3–4 hours)

Priority: P1 | Impact: High | Dependencies: None Resolves: TODO.md items 4, 6, 7

These three services have minimal stub AuditService classes that do nothing. Replace them with real implementations modeled on the order-service's AuditService.

Reference implementation

The order-service's audit module at apps/order-service/src/audit/ contains:

  • audit.service.ts — the main service with log(), logAuth(), logPermissionDenied(), logUserManagement(), and getClientIp() methods
  • audit.repository.ts — Prisma repository that writes to the AuditLog table
  • audit.module.ts — NestJS module
  • dto/audit-event.dto.ts — DTO for audit log input
  • index.ts — barrel exports
  • AUDIT_ACTIONS constant — full set of action constants

1. Determine each service's audit scope

Not all services need all audit methods. Scope them appropriately:

Service log() logAuth() logPermissionDenied() logUserManagement() Reasoning
shipping-service Handles auth (session-based), no user management
gridflock-service Handles auth (internal API key), no user management
print-service Handles auth (internal API key), no user management

2. For each of the three services, create:

a) audit.repository.ts — Copy from apps/order-service/src/audit/audit.repository.ts. This writes to the AuditLog Prisma table which is shared across all services (they all connect to the same database).

b) audit.service.ts — Create a service with: - log(input: CreateAuditLogInput) — best-effort logging to AuditLog table with Sentry fallback - logAuth(action, request, options) — authentication event logging - getClientIp(request) — private method to extract client IP from request headers - Optional OtelLoggerService integration (if @forma3d/observability is available as a dependency in the service)

c) audit.module.ts — Import PrismaModule (or however the service provides Prisma), provide AuditRepository and AuditService, export AuditService.

d) dto/audit-event.dto.ts — Copy from apps/order-service/src/audit/dto/audit-event.dto.ts.

e) index.ts — Export AuditService, AUDIT_ACTIONS, and the DTO.

f) Update AUDIT_ACTIONS — Replace the minimal stub constant with the full set of actions from the order-service reference. Each service only needs the actions relevant to it, but include the full set for consistency.

3. Update module imports

In each service's root module (e.g., apps/shipping-service/src/app/app.module.ts), import the new AuditModule so the real AuditService is available for dependency injection.

4. Verify existing callers

Search each service for existing references to AuditService or AUDIT_ACTIONS. Ensure they now use the real implementation without breaking any existing code. The stub's logAuth(action, req, metadata) signature may differ slightly from the real implementation's logAuth(action, req, options) — update callers if needed.

5. Unit tests for each service's AuditService

Create __tests__/audit.service.spec.ts and __tests__/audit.repository.spec.ts in each service's audit directory. Follow the test patterns from the order-service:

audit.service.spec.ts should test: - log() calls the repository and succeeds - log() catches repository errors and reports to Sentry (best-effort) - logAuth() correctly extracts user info, tenant ID, and IP - logAuth() uses DEFAULT_TENANT_ID when no user is available - getClientIp() extracts from x-forwarded-for, x-real-ip, and falls back to socket address

audit.repository.spec.ts should test: - create() calls prisma.auditLog.create() with the correct data - Correct field mapping (tenantId, actorUserId, action, etc.)


Phase 2: Shipping-Service FulfillmentService (1–2 hours)

Priority: P1 | Impact: High | Dependencies: None Resolves: TODO.md item 5

The FulfillmentService in apps/shipping-service/src/fulfillment/fulfillment.service.ts is a stub. The shipping-service's fulfillment needs are different from the order-service's — the shipping-service does not create Shopify fulfillments directly. Instead, the shipping-service's role in fulfillment is to:

  1. Track when a shipment has been created (via Sendcloud)
  2. Emit events that the order-service listens to for creating Shopify fulfillments

1. Evaluate what createFulfillment should do in the shipping-service

The shipping-service's RetryQueueProcessor calls FulfillmentService.createFulfillment({ orderId }) for FULFILLMENT retry jobs. Look at how FULFILLMENT retry jobs are enqueued in the shipping-service to understand what failed operation needs retrying.

If the shipping-service enqueues FULFILLMENT retry jobs when Sendcloud label creation fails, then createFulfillment should: - Fetch the order details - Retry the Sendcloud label creation - Emit a fulfillment.completed or fulfillment.failed event

If no code in the shipping-service currently enqueues FULFILLMENT retry jobs, then the stub may be there for future use. In this case: - Implement a minimal but real version that logs the attempt and emits the appropriate event - Add a Logger.warn for the case where no retry logic is needed yet - Do NOT leave the method body empty — at minimum log and return

2. Update the module

Ensure FulfillmentModule imports any services needed by the real implementation (e.g., SendcloudService, EventEmitter2).

3. Unit tests

Create __tests__/fulfillment.service.spec.ts with tests for: - createFulfillment() with a valid orderId - createFulfillment() with a non-existent order - Error handling and event emission - Integration with the retry queue (if applicable)


Phase 3: Print Job Creation Retry Handler (2–3 hours)

Priority: P0 | Impact: Critical | Dependencies: None Resolves: TODO.md items 9 (order-service) and 11 (shipping-service)

The processPrintJobRetry() method in both order-service and shipping-service currently throws a ConflictError('Print job retry not yet implemented'). Replace this with a real implementation.

1. Order-service implementation

In apps/order-service/src/retry-queue/retry-queue.processor.ts, replace processPrintJobRetry():

private async processPrintJobRetry(job: RetryQueue): Promise<void> {
  const payload = job.payload as { lineItemId: string; orderId: string };

  // Fetch the line item to get context needed for print job creation
  const lineItems = await this.ordersService.getLineItems(payload.orderId);
  const lineItem = lineItems.find(li => li.id === payload.lineItemId);

  if (!lineItem) {
    throw new NotFoundException(
      `Line item ${payload.lineItemId} not found for order ${payload.orderId}`
    );
  }

  await this.printJobsService.createPrintJobsForLineItem(lineItem);
}

Key requirements: - Inject PrintJobsService (or the appropriate interface IPrintJobsService) into the RetryQueueProcessor constructor - Inject IOrdersService (via ORDERS_SERVICE token) to fetch line items - Update the RetryQueueModule to import the modules that provide these services - Remove the ConflictError import if no longer used elsewhere in the file

Important: Check what createPrintJobsForLineItem expects as input. The method signature in apps/order-service/src/print-jobs/print-jobs.service.ts uses a PrintJobLineItemContext type. The retry payload may need to store additional fields (shopifyProductId, shopifyVariantId, productSku, productName, quantity) or these need to be fetched from the line item at retry time.

2. Shipping-service implementation

The shipping-service may not have its own PrintJobsService. Check if it does: - If yes: implement the same pattern as order-service - If no: the shipping-service should not be processing PRINT_JOB_CREATION retry jobs. In this case, replace the ConflictError with a clear log message explaining this job type is not applicable to the shipping-service, and mark the job as completed (not failed):

private async processPrintJobRetry(job: RetryQueue): Promise<void> {
  this.logger.warn(
    `PRINT_JOB_CREATION retry job ${job.id} received by shipping-service — ` +
    `this job type should be processed by the order-service. Marking as completed.`
  );
}

3. Update unit tests

In apps/order-service/src/retry-queue/__tests__/retry-queue.processor.spec.ts:

Replace the test 'should handle print job creation retry (throws not implemented)' with:

  • 'should retry print job creation successfully' — mock the line item lookup and createPrintJobsForLineItem, verify they are called with correct arguments
  • 'should handle failure when line item not found' — verify NotFoundException is thrown and caught by the outer error handler
  • 'should handle failure when print job creation fails' — verify handleFailure is called

In apps/shipping-service/src/retry-queue/__tests__/retry-queue.processor.spec.ts:

Update the equivalent test to match the shipping-service's implementation (either real retry or the "not applicable" log).


Phase 4: Cancellation Retry Handler (2–3 hours)

Priority: P0 | Impact: Critical | Dependencies: None Resolves: TODO.md items 8 (order-service) and 10 (shipping-service)

The CANCELLATION case in both services' RetryQueueProcessor.processJob() currently only logs a warning. Implement real cancellation retry logic.

1. Order-service implementation

In apps/order-service/src/retry-queue/retry-queue.processor.ts, the CANCELLATION case should call the existing CancellationService:

case RetryJobType.CANCELLATION:
  await this.processCancellationRetry(job);
  break;

Add a new private method:

private async processCancellationRetry(job: RetryQueue): Promise<void> {
  const payload = job.payload as { orderId: string; reason?: string };
  await this.cancellationService.cancelOrder(payload.orderId, payload.reason);
}

Key requirements: - Inject CancellationService into the RetryQueueProcessor constructor - Update RetryQueueModule to import CancellationModule - Handle the case where the order is already cancelled (idempotent — CancellationService.cancelOrder() already handles this by checking order status)

Cancellation payload structure: Check how CANCELLATION retry jobs are enqueued in the codebase (search for RetryJobType.CANCELLATION in enqueue calls) to confirm the payload structure. It likely contains { orderId: string; reason?: string }, but verify.

2. Shipping-service implementation

The shipping-service may need to cancel shipments when an order is cancelled. Check if the shipping-service has a CancellationService or equivalent:

  • If yes: inject it and call the appropriate cancellation method
  • If no: check what cancellation means in the shipping-service context. It may need to:
  • Cancel a Sendcloud shipment/label
  • Update the local shipment status
  • Emit a cancellation event

If the shipping-service genuinely has no cancellation logic yet, implement a minimal but functional handler:

private async processCancellationRetry(job: RetryQueue): Promise<void> {
  const payload = job.payload as { orderId: string; reason?: string };
  this.logger.log(`Processing cancellation retry for order ${payload.orderId}`);
  // If Sendcloud shipment exists, attempt to cancel it
  // Otherwise, log and complete
}

3. Update unit tests

In apps/order-service/src/retry-queue/__tests__/retry-queue.processor.spec.ts:

Replace the test 'should handle cancellation job type with warning' with:

  • 'should retry cancellation successfully' — mock CancellationService.cancelOrder, verify it's called with the correct orderId and reason
  • 'should handle cancellation when order is already cancelled' — verify idempotent behavior
  • 'should handle cancellation failure' — verify handleFailure is called when cancelOrder throws

In apps/shipping-service/src/retry-queue/__tests__/retry-queue.processor.spec.ts:

Update the equivalent test to match the shipping-service's implementation.


Phase 5: Update TODO.md (15 min)

Priority: P0 | Impact: Required | Dependencies: Phases 1–4

After all implementations are complete, update TODO.md section "In-Code TODOs, Stubs, and Placeholders" to reflect that items 4–11 have been resolved.

For each resolved item, update the table row to include the implementation status. Add a new column or modify the Description column to append: — ✅ Implemented (YYYY-MM-DD)

Example:

| 4 | `apps/shipping-service/src/audit/index.ts` | 17 | `AuditService.logAuth()` — ✅ Implemented (2026-03-XX) |

✅ Validation Checklist

Audit Services (items 4, 6, 7)

  • apps/shipping-service/src/audit/audit.service.ts exists with real log() and logAuth() methods
  • apps/shipping-service/src/audit/audit.repository.ts exists and writes to AuditLog table
  • apps/gridflock-service/src/audit/audit.service.ts exists with real log() and logAuth() methods
  • apps/gridflock-service/src/audit/audit.repository.ts exists and writes to AuditLog table
  • apps/print-service/src/audit/audit.service.ts exists with real log() and logAuth() methods
  • apps/print-service/src/audit/audit.repository.ts exists and writes to AuditLog table
  • AUDIT_ACTIONS in all three services has the full set of action constants (matching order-service)
  • All three services have AuditModule properly imported in their root module
  • No stub comments remain (// Stub, // does nothing, etc.)
  • Unit tests pass for all three AuditService implementations
  • Unit tests pass for all three AuditRepository implementations

Fulfillment Service (item 5)

  • apps/shipping-service/src/fulfillment/fulfillment.service.ts has a real createFulfillment() implementation (not an empty body)
  • No stub comments remain
  • Unit tests exist and pass for the fulfillment service
  • FulfillmentModule imports all required dependencies
  • apps/order-service/src/retry-queue/retry-queue.processor.ts processPrintJobRetry() calls PrintJobsService.createPrintJobsForLineItem() (or equivalent)
  • No ConflictError('Print job retry not yet implemented') remains in order-service
  • apps/shipping-service/src/retry-queue/retry-queue.processor.ts processPrintJobRetry() has appropriate handling (real retry or explicit "not applicable" log)
  • No ConflictError('Print job retry not yet implemented') remains in shipping-service
  • Unit tests updated: no test expects the ConflictError for "not yet implemented"
  • New unit tests verify the actual retry behavior

Cancellation Retry (items 8, 10)

  • apps/order-service/src/retry-queue/retry-queue.processor.ts CANCELLATION case calls CancellationService.cancelOrder()
  • No "Cancellation retry not yet implemented" warning remains in order-service
  • apps/shipping-service/src/retry-queue/retry-queue.processor.ts CANCELLATION case has appropriate handling
  • No "Cancellation retry not yet implemented" warning remains in shipping-service
  • Unit tests updated: no test expects only a warning log for cancellation
  • New unit tests verify the actual cancellation retry behavior

TODO.md Update

  • Items 4–11 in TODO.md are marked as ✅ Implemented (YYYY-MM-DD)
  • No stale placeholder descriptions remain

Build & Lint

  • pnpm nx run-many -t build --all passes
  • pnpm nx run-many -t lint --all passes
  • pnpm nx run-many -t test --all --exclude=acceptance-tests passes
  • No any, ts-ignore, or eslint-disable introduced
  • No console.log in production code

Acceptance Tests

  • Existing acceptance tests still pass: pnpm nx run acceptance-tests:e2e
  • If retry queue endpoints exist in the API, verify they work with the new implementations
  • Consider adding acceptance test scenarios for:
  • Print job creation retry: enqueue a PRINT_JOB_CREATION retry job, wait for processing, verify print jobs are created
  • Cancellation retry: enqueue a CANCELLATION retry job, wait for processing, verify order is cancelled
  • (These may be integration tests rather than full acceptance tests — use judgment based on the existing test infrastructure)

🚫 Constraints and Rules

MUST DO

  • Follow the order-service's AuditService as the reference implementation for all three audit services
  • Use best-effort logging (catch errors, report to Sentry, never throw from audit)
  • Write to the shared AuditLog Prisma table
  • Inject real services via NestJS DI — no direct instantiation
  • Use forwardRef() where circular dependencies exist (follow existing patterns in the retry queue processors)
  • Keep the retry queue processor's error handling pattern intact: individual job failures are caught and handled via retryQueueService.handleFailure()
  • Update existing unit tests that expect stub behavior (e.g., tests expecting ConflictError('Print job retry not yet implemented'))
  • Verify cancellation payload structure by searching for RetryJobType.CANCELLATION enqueue calls
  • Update TODO.md after all implementations

MUST NOT

  • Break any existing functionality or tests
  • Add any, ts-ignore, or eslint-disable
  • Add console.log in production code paths
  • Change the retry queue's cron schedule or concurrency model
  • Modify the AuditLog Prisma schema (use the existing table as-is)
  • Remove the RetryJobType.CANCELLATION or RetryJobType.PRINT_JOB_CREATION enum values from Prisma
  • Leave any stub comments (// Stub, // placeholder, // not yet implemented) in the implemented code
  • Skip unit tests — every new or modified method must have test coverage

SHOULD DO (Nice to Have)

  • Add OpenTelemetry integration (OtelLoggerService) to audit services if the @forma3d/observability library is available in those services
  • Add structured Sentry tags to all error captures for better filtering in the Sentry dashboard
  • Consider whether the shipping-service's retry queue should handle PRINT_JOB_CREATION at all — if not, document why in a code comment
  • Add event log entries for successful retry operations (e.g., 'PRINT_JOB_CREATION_RETRIED', 'CANCELLATION_RETRIED')

📚 Key References

Reference Implementations (order-service): - Audit service: apps/order-service/src/audit/audit.service.ts - Audit repository: apps/order-service/src/audit/audit.repository.ts - Audit module: apps/order-service/src/audit/audit.module.ts - Audit DTO: apps/order-service/src/audit/dto/audit-event.dto.ts - Audit tests: apps/order-service/src/audit/__tests__/ - Fulfillment service: apps/order-service/src/fulfillment/fulfillment.service.ts - Cancellation service: apps/order-service/src/cancellation/cancellation.service.ts - Print jobs service: apps/order-service/src/print-jobs/print-jobs.service.ts - Retry queue processor: apps/order-service/src/retry-queue/retry-queue.processor.ts - Retry queue tests: apps/order-service/src/retry-queue/__tests__/retry-queue.processor.spec.ts

Stubs to Replace: - Shipping audit: apps/shipping-service/src/audit/index.ts - Shipping fulfillment: apps/shipping-service/src/fulfillment/fulfillment.service.ts - Shipping retry processor: apps/shipping-service/src/retry-queue/retry-queue.processor.ts - GridFlock audit: apps/gridflock-service/src/audit/index.ts - Print-service audit: apps/print-service/src/audit/index.ts

Domain Contracts: - libs/domain-contracts/ — shared interfaces (IOrdersService, IPrintJobsService, etc.) - libs/domain/ — shared error types (ConflictError, PrintJobStateError, etc.)

Prisma Schema: - prisma/schema.prismaAuditLog table definition, RetryJobType enum

TODO Tracking: - TODO.md — section "In-Code TODOs, Stubs, and Placeholders", items 4–11


END OF PROMPT


This prompt replaces all stub and placeholder implementations identified in the TODO.md code scan (items 4–11). It covers three categories: (1) real audit services for shipping-service, gridflock-service, and print-service modeled on the order-service reference; (2) a real fulfillment service in shipping-service; (3) working retry handlers for print job creation and cancellation in both order-service and shipping-service. Each implementation includes unit tests and updates to existing tests that expected stub behavior. After completion, TODO.md is updated to mark all items as resolved.