8000 Eng 433 scheduler for patient job by thomasyopes · Pull Request #3997 · metriport/metriport · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Eng 433 scheduler for patient job #3997

New issue 8000

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 69 commits into from
Jun 19, 2025

Conversation

thomasyopes
Copy link
Contributor
@thomasyopes thomasyopes commented Jun 11, 2025

Issues:

Dependencies

Description

  • adds scheduled at column for jobs
  • adds run job command for starting jobs
  • adds new jobs table utilities for: fail, cancel, update runtime data

Testing

  • Local
    • canvas diff runs immediately when created with no scheduled at
    • canvas diff runs when scheduled and we ping /internal/patient/job/start
    • update runtimeData endpoints works
    • cancel/fail job endpoints work
    • getLatestJob still works via EHR endpoint
    • get jobs endpoint works and filters correctly
  • Staging
    • canvas diff runs immediately when created with no scheduled at
    • update runtimeData endpoints works
    • cance/fail endpoints work
    • getLatestJob still works via EHR endpoint
    • get jobs endpoint works and filters correctly
  • Sandbox
    • N/A
  • Production
    • canvas diff runs immediately when created with no scheduled at
    • update runtimeData endpoints works
    • cance/fail endpoints work
    • getLatestJob still works via EHR endpoint
    • get jobs endpoint works and filters correctly

Release Plan

  • ⚠️ This contains a DB migration
  • Upstream dependencies are met/released
  • Merge this

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added support for scheduling, running, failing, and cancelling patient jobs, including new endpoints for managing job lifecycle and runtime data.
    • Introduced job filtering and retrieval by various parameters such as status, scheduled dates, and identifiers.
    • Implemented infrastructure for patient job processing using AWS Lambda and SQS, with robust error handling and monitoring.
    • Added runtime data management for patient jobs, allowing runtime data to be fetched and updated.
    • Introduced job execution via API triggers and asynchronous queue processing with concurrency controls.
    • Added creation and execution of EHR resource diff bundle jobs with support for multiple EHR sources.
  • Enhancements

    • Expanded job status options to include "cancelled" and improved status transition validation.
    • Improved job scheduling capabilities and automatic execution for immediate jobs.
    • Enhanced API validation and error reporting for job operations.
    • Updated logging and error handling for job API interactions.
    • Improved date validation supporting ISO date and date-time formats.
  • Infrastructure

    • Provisioned new AWS resources for job scheduling and processing, including Lambda functions, SQS queues, and related configuration.
    • Integrated patient job queue permissions and environment variables into API service deployment.
  • Bug Fixes

    • Improved validation for date and status parameters in job-related endpoints.
  • Chores

    • Refactored and reorganized code to support new job features and infrastructure, including type and schema updates.
    • Reorganized imports and inline type declarations for better modularity and maintainability.

Thomas Yopes added 5 commits June 10, 2025 17:08
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MacBook-Pro.local>
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MacBook-Pro.local>
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MacBook-Pro.local>
Ref: ENG-281

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MacBook-Pro.local>
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MacBook-Pro.local>
Copy link
linear bot commented Jun 11, 2025

Copy link
coderabbitai bot commented Jun 11, 2025

Walkthrough

This update introduces a comprehensive patient job scheduling and execution framework. It adds new fields to the patient job model, enables job scheduling, cancellation, and failure, and provides infrastructure for running jobs via SQS and Lambda. The API surface is expanded with endpoints for job management, and supporting types, handlers, and infrastructure stacks are implemented.

Changes

File(s) / Path(s) Change Summary
packages/api/src/command/job/patient/create.ts, get.ts, scheduler/start-jobs.ts, status/cancel.ts, status/complete.ts, status/fail.ts, status/initialize.ts, update/set-entry-status.ts, update/update-runtime-data.ts, update/update-total.ts Refactored and expanded patient job command handlers: new fields, status transitions (cancel/fail), runtime data update, job scheduling, and parameter type updates.
packages/api/src/command/job/shared.ts Removed shared job parameter/response types (now inlined or moved).
packages/api/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/run-job.ts, start-job.ts, get-job-payload.ts, utils/job.ts Added/updated job execution logic, run URL utilities, and refactored job creation to remove concurrency checks.
packages/api/src/models/patient-job.ts, packages/shared/src/domain/job/patient-job.ts Extended patient job model/type with runUrl, scheduledAt, cancelledAt, failedAt, runtimeData; added raw column mapping.
packages/api/src/routes/internal/medical/patient-job.ts, ehr/job.ts, ehr/index.ts, ehr/patient.ts Added/updated API endpoints for job management: scheduling, status transitions, runtime data, and job execution.
packages/api/src/sequelize/migrations/2025-06-17_00_alter-patient-job-add-scheduled-at.ts Migration: added new columns to the patient job table and a status index.
packages/api/src/shared/date.ts, packages/shared/src/common/date.ts Added ISO date-time validation and utility for date parsing/validation.
packages/core/src/command/job/patient/api/complete-job.ts, initialize-job.ts, set-entry-status.ts, update-job-total.ts Refactored logging/response validation and updated endpoint paths.
packages/core/src/command/job/patient/api/get-jobs.ts, run-job.ts, update-job-runtime-data.ts, shared.ts Added API calls for job listing, execution, runtime data update, and shared parameter types.
packages/core/src/command/job/patient/command/run-job/run-job-cloud.ts, run-job-direct.ts, run-job-factory.ts, run-job.ts Implemented job runner handlers (direct/cloud), factory, and request types for job execution.
packages/core/src/external/ehr/api/job/shared.ts Removed obsolete shared job parameter type.
packages/core/src/external/ehr/job/create-resource-diff-bundles/steps/compute/ehr-compute-resource-diff-bundles-cloud.ts, steps/compute/ehr-compute-resource-diff-bundles.ts, steps/refresh/ehr-refresh-ehr-bundles-cloud.ts, steps/refresh/ehr-refresh-ehr-bundles.ts Updated import paths for shared utilities/types.
packages/core/src/external/ehr/job/create-resource-diff-bundles/shared.ts Added types/utilities for resource diff bundle jobs.
packages/core/src/util/config.ts Added method to retrieve patient job queue URL from environment.
packages/infra/config/env-config.ts, example.ts Added jobs config section for scheduler schedule and URL.
packages/infra/lib/api-stack.ts, api-stack/api-service.ts, jobs/jobs-stack.ts, jobs/types.ts Provisioned new nested jobs stack: Lambda, SQS queue, scheduler, asset wiring, and environment/IAM integration.
packages/infra/lib/surescripts/surescripts-stack.ts Minor import reordering.
packages/lambdas/src/job/patient/run-job.ts Added Lambda handler for running patient jobs from SQS.
packages/shared/src/common/response.ts Added utility functions for Axios response logging and validation.
packages/shared/src/domain/job/job-status.ts Added "cancelled" status and updated status transition validation.
packages/shared/src/domain/job/types.ts Added schema for job run request body validation.

Sequence Diagram(s)

Patient Job Scheduling and Execution Flow

sequenceDiagram
    participant Client
    participant API
    participant DB as PatientJob DB
    participant SQS as SQS Queue
    participant Lambda as RunJob Lambda

    Client->>API: POST /internal/patient/job (create job with runUrl, scheduledAt)
    API->>DB: Insert patient job (with runUrl, scheduledAt)
    alt scheduledAt is not set (immediate run)
        API->>SQS: Enqueue job (id, cxId, runUrl)
    end
    SQS->>Lambda: Trigger event (job message)
    Lambda->>API: POST runUrl (with jobId, cxId)
    API->>DB: Update job status (processing/completed/failed)
Loading

Job Status Transition (Cancel/Fail)

sequenceDiagram
    participant Client
    participant API
    participant DB as PatientJob DB

    Client->>API: POST /internal/patient/job/:jobId/cancel or /fail
    API->>DB: Update job status (cancelled or failed), set cancelledAt/failedAt
    API-->>Client: 200 OK (updated job)
Loading

Scheduled Job Runner

sequenceDiagram
    participant Scheduler
    participant API
    participant DB as PatientJob DB
    participant SQS as SQS Queue

    Scheduler->>API: POST /internal/patient/job/scheduler/start
    API->>DB: Query jobs (status=waiting, scheduledBefore=now)
    loop For each job with runUrl
        API->>SQS: Enqueue job (id, cxId, runUrl)
    end
Loading

Possibly related PRs

  • metriport/metriport#3714: Introduced the original createPatientJob function with concurrency checks. The current changes modify this function by removing concurrency checks and adding synchronous job execution via runUrl.
  • metriport/metriport#3682: Also introduced the original createPatientJob with concurrency logic, which is now refactored in this update to support immediate execution and new parameters.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error code ERR_SSL_WRONG_VERSION_NUMBER
npm error errno ERR_SSL_WRONG_VERSION_NUMBER
npm error request to https://10.0.0.28:4873/punycode/-/punycode-2.3.1.tgz failed, reason: C0EC63764D7F0000:error:0A00010B:SSL routines:ssl3_get_record:wrong version number:../deps/openssl/openssl/ssl/record/ssl3_record.c:354:
npm error
npm error A complete log of this run can be found in: /.npm/_logs/2025-06-19T17_47_46_457Z-debug-0.log


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab8c0c1 and 9d7c144.

📒 Files selected for processing (2)
  • packages/api/src/command/job/patient/scheduler/start-jobs.ts (1 hunks)
  • packages/infra/config/env-config.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/infra/config/env-config.ts
  • packages/api/src/command/job/patient/scheduler/start-jobs.ts
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: Analyze (javascript)
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Thomas Yopes and others added 2 commits June 10, 2025 17:26
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MacBook-Pro.local>
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Thomas Yopes added 13 commits June 11, 2025 08:29
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
… eng-433-scheduler-for-patient-job

Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Ref: ENG-435

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
@thomasyopes thomasyopes marked this pull request as ready for review June 12, 2025 22:53
Copy link
@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: 23

🔭 Outside diff range comments (3)
packages/core/src/command/job/patient/api/set-entry-status.ts (1)

31-38: 🛠️ Refactor suggestion

Reduce repetition – extract a tiny helper

completeJob, initializeJob, updateJobTotal, and setJobEntryStatus all duplicate:

  • axios.create({ baseURL })
  • retry wrapper
  • logAxiosResponse

A shared utility such as postInternalJobEndpoint({ path, params }) would cut ~30 lines per file and ensure consistent error handling.

packages/api/src/command/job/patient/update/update-total.ts (1)

22-29: ⚠️ Potential issue

Missing guard against updating a running job – spec/doc mismatch

The JSDoc advertises:

“throws BadRequestError if the job total is already set and the job is running.”

The implementation only validates total < 1; it never checks whether
a) job.total is already set, or
b) the job is currently running.

That means an in-flight job can have its counters silently reset, breaking every consumer that relies on the invariants (successful + failed ≤ total).

Suggested minimal fix:

@@
-const jobModel = await getPatientJobModelOrFail({ jobId, cxId });
+const jobModel = await getPatientJobModelOrFail({ jobId, cxId });
+const job       = jobModel.dataValues;
+
+// Prevent mutating totals for jobs that are already in progress or completed
+if (job.total !== null && ["running", "queued"].includes(job.status)) {
+  throw new BadRequestError(
+    `Cannot update total for a job in status ${job.status}`
+  );
+}

Optionally refuse updates when job.total is already > 0 regardless of status if that better matches business rules.

packages/shared/src/domain/job/job-status.ts (1)

18-20: 🛠️ Refactor suggestion

isJobDone omits cancelled

In many systems a cancelled job is considered terminal.
If callers rely on isJobDone, they may endlessly poll a cancelled job.

-export function isJobDone(status: JobStatus): boolean {
-  return status === "completed" || status === "failed";
+export function isJobDone(status: JobStatus): boolean {
+  return status === "completed" || status === "failed" || status === "cancelled";
 }
♻️ Duplicate comments (2)
packages/core/src/command/job/patient/api/initialize-job.ts (1)

17-24: Same query-param construction caveat as completeJob

Replicate the Axios params refactor here to stay consistent and avoid divergent URL building logic.

packages/core/src/command/job/patient/api/update-job-total.ts (1)

21-29: Consistent param handling & logging concerns (see prior comments)

Same points as in completeJob/initializeJob:

  1. Replace manual query construction with Axios params.
  2. Re-evaluate logging of full response.data for PHI.
🧹 Nitpick comments (25)
packages/core/src/command/job/patient/api/complete-job.ts (1)

17-24: Avoid manual query-string assembly – leverage Axios params for correctness & readability

Manually concatenating ?${queryParams.toString()} works but is easy to break (e.g., double ?, forgotten encoding, trailing &). Axios already handles this via its params option:

-const queryParams = new URLSearchParams({ cxId });
-const completeJobUrl = `/internal/patient/job/${jobId}/complete?${queryParams.toString()}`;
-...
-const response = await executeWithNetworkRetries(async () => {
-  return api.post(completeJobUrl);
-});
+const completeJobUrl = `/internal/patient/job/${jobId}/complete`;
+...
+const response = await executeWithNetworkRetries(async () => {
+  return api.post(completeJobUrl, undefined, { params: { cxId } });
+});

Benefits: centralised encoding, reduced string-bashing, clearer intent.

packages/api/src/command/job/patient/status/initialize.ts (1)

24-34: Minor JSDoc mismatch – parameter name

The code uses cxId, but the docstring line still says “customer ID”. Consider clarifying (cxId - The customer (CX) ID). Small but keeps docs accurate.

packages/api/src/routes/internal/medical/patient-jobs/index.ts (1)

6-8: Router export is fine – consider using a named export for consistency

Most route modules in the code-base appear to use export default but a few expose a named constant. Mixing the two styles can bite during refactors (e.g., when converting to ESM or re-exporting barrels).
If there is no established convention yet, consider switching to

-const router = Router();
-...
-export default router;
+export const router = Router();
+...

and importing it with import { router as patientJobsRouter } from "./ehr" to keep everything explicit.
No functional changes required – this is purely a consistency nit.

packages/api/src/shared/config.ts (1)

122-125: Wrapper now just forwards to core – consider de-duplicating sooner

getDBCreds is now a pure pass-through to CoreConfig.getDBCreds().
If nothing inside api calls the core implementation directly, we are keeping two public APIs that must stay in sync.

 /** @deprecated Use core's version of Config instead */
 static getDBCreds(): string {
-  return CoreConfig.getDBCreds();
+  // TODO: remove once callers migrate to CoreConfig
+  return CoreConfig.getDBCreds();
 }

Follow-up:

  1. Search for Config.getDBCreds( usages and migrate them to CoreConfig.
  2. Delete this wrapper in a subsequent clean-up PR to avoid bit-rot.
packages/infra/lib/shared/secrets.ts (1)

16-18: Nit: keep helper naming consistent with existing API

buildSecrets takes a generic scope: Construct; the new buildSecret takes nestedStack: Construct.
For symmetry (and to avoid a mental context-switch for callers) prefer the same parameter name & doc comment:

-export function buildSecret(nestedStack: Construct, name: string): secret.ISecret {
-  return secret.Secret.fromSecretNameV2(nestedStack, name, name);
+/**
+ * Build a single secret reference (sugar over buildSecrets for one entry).
+ */
+export function buildSecret(scope: Construct, name: string): secret.ISecret {
+  return secret.Secret.fromSecretNameV2(scope, name, name);
 }

No functional change, purely readability.

packages/core/src/command/job/patient/api/shared.ts (1)

1-5: Export JobId to maximise reuse

JobId is defined but not exported. Several downstream files may need the exact literal type instead of re-deriving PatientJob["id"].

-type JobId = PatientJob["id"];
+export type JobId = PatientJob["id"];

This avoids duplicating the alias elsewhere and keeps typings DRY.

packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job-direct.ts (1)

4-12: Minor style nit – destructure the argument for readability

Destructuring avoids the request. prefix noise and matches the functional-style guideline in the TS coding guide.

-  async runJob(request: RunJobRequest): Promise<void> {
-    await runJob({
-      jobId: request.id,
-      cxId: request.cxId,
-      jobType: request.jobType,
-    });
+  async runJob({ id: jobId, cxId, jobType }: RunJobRequest): Promise<void> {
+    await runJob({ jobId, cxId, jobType });
   }
packages/api/src/command/job/patient/scheduler/start-jobs.ts (1)

14-25: Fire-and-forget pattern may terminate execution prematurely

startJobs returns as soon as the handler promise is created, not when the jobs are actually fetched/run. In a Lambda or short-lived process this risks the runtime finishing before work completes (even though you attach .catch). Consider awaiting (or explicitly documenting that early return is intentional).

-export async function startJobs({...}: StartJobsParams): Promise<void> {
+export async function startJobs({...}: StartJobsParams): Promise<void> {
   const handler = buildGetJobsHandler();
-  handler
-    .getJobs({ runDate, cxId, patientId, jobType, status })
-    .catch(processAsyncError("startPatientJobs"));
+  await handler
+    .getJobs({ runDate, cxId, patientId, jobType, status })
+    .catch(processAsyncError("startPatientJobs"));
 }

If “fire-and-forget” is desired (e.g., for an API 202 Accepted semantics), mark it with a comment and drop the unnecessary async keyword to avoid confusion.

packages/core/src/util/config.ts (1)

35-38: DB credentials accessor returns opaque string – clarify expected format

getDBCreds() surfaces a raw DB_CREDS string. Call-sites likely need individual fields (host, user, pwd). Consider returning a parsed object (or at least document the expected JSON / connection-string format) to avoid ad-hoc parsing in multiple places.

packages/shared/src/domain/job/patient-job.ts (1)

24-24: Consider using a generic for runtimeData for safer downstream usage

runtimeData: unknown forces consumers to cast, masking type errors. A generic type parameter keeps flexibility while preserving type-safety:

-export type PatientJob = {
+export type PatientJob<TRuntimeData = unknown> = {
   ...
-  runtimeData: unknown;
+  runtimeData: TRuntimeData | undefined;
 }

This change is non-breaking if defaulted to unknown, but gives callers the option to specify a shape.

packages/api/src/command/job/patient/update/update-runtime-data.ts (2)

5-14: Docstring is misleading & parameter names are out of sync

  • The description still says “Updates a patient job's total.” – should be “runtime data.”
  • @param runtimeData does not exist in the signature; the actual key is data.

Keeping docs accurate prevents downstream confusion.

- * Updates a patient job's total.
+ * Updates a patient job's runtime data.
...
- * @param runtimeData - The runtime data to update.
+ * @param data - The runtime data payload that will replace the existing value.

20-25: Return plain object instead of Sequelize instance for cleaner typing

jobModel.update() returns a Sequelize instance; returning dataValues leaks an internal detail and strips any virtuals/getters. Prefer updatedJob.get({ plain: true }) (or toJSON() on newer Sequelize) to keep a clean POJO and consistent serialization.

-const updatedJob = await jobModel.update(fieldsToUpdate);
-return updatedJob.dataValues;
+const [updatedJob] = await jobModel.update(fieldsToUpdate, { returning: true });
+return updatedJob.get({ plain: true });
packages/infra/lib/api-stack/api-service.ts (1)

458-459: Grant invoke permission also for runPatientJobLambda

The task can invoke getPatientJobsLambda but not runPatientJobLambda. If direct invocation is ever required (e.g., retries, back-fill scripts) the call will fail with AccessDenied.

+jobAssets.runPatientJobLambda.grantInvoke(fargateService.taskDefinition.taskRole);
packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job.ts (1)

3-5: Consider freezing RunJobRequest to avoid accidental mutation

Since the same object may propagate through multiple layers, wrapping the request in Readonly<...> (or freezing) enforces immutability per guidelines.

-export type RunJobRequest = Pick<PatientJob, "id" | "cxId" | "jobType"> &
-  Partial<Pick<PatientJob, "paramsCx" | "paramsOps" | "data">>;
+export type RunJobRequest = Readonly<
+  Pick<PatientJob, "id" | "cxId" | "jobType"> &
+    Partial<Pick<PatientJob, "paramsCx" | "paramsOps" | "data">>
+>;
packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job-cloud.ts (1)

9-12: Optional DI enhancement for testability

Injecting SQSClient via the constructor is great. To make unit tests deterministic, also expose the JSON stringify step so tests can assert the exact payload without duplicating logic.

-private readonly sqsClient: SQSClient;
+private readonly sqsClient: SQSClient;
+protected stringify = JSON.stringify; // overridable for testing
packages/api/src/command/job/patient/status/cancel.ts (2)

7-18: Misleading JSDoc header & param descriptions

The block states “Fails a patient job” while the function purpose is cancellation. Param docs follow the same pattern.
Update to prevent future confusion and inaccurate generated docs.


26-26: Log prefix does not match the action

updateJobTracking neither conveys “cancel” nor matches the function name.
Consider a clearer prefix, e.g. cancelPatientJob.

packages/core/src/command/job/patient/api/run-job.ts (1)

12-18: Comment header still says “complete”

The function now runs a job, not completes it.
Please update comment & tag names to avoid misleading IntelliSense.

packages/api/src/command/job/patient/status/fail.ts (2)

7-18: Docstrings copied from cancel handler

The header says Cancels and params mention “cancelling”.
Please align wording with failure semantics.


26-26: Same log prefix issue as cancel handler

updateJobTracking is generic—consider failPatientJob for traceability.

packages/shared/src/domain/job/job-status.ts (1)

70-77: Status transition rule too restrictive

The restriction waiting → cancelled disallows cancelling jobs stuck in processing.
If an operator needs to abort a long-running job this path won’t work.
Consider permitting processing → cancelled when forceStatusUpdate is true.

packages/api/src/routes/internal/medical/patient-jobs/ehr.ts (2)

18-25: JSDoc path & parameter description don’t match actual route

The JSDoc advertises
POST /internal/medical/patient-job/ehr/... and says req.query.jobId, but the implementation registers
/athenahealth-create-resource-diff-bundles/:jobId/run (and the Canvas variant) and extracts jobId from req.params.

Keep the documentation in sync or future maintainers will waste time debugging the “wrong” URL/param.

- * POST /internal/medical/patient-job/ehr/athenahealth-create-resource-diff-bundles/run
+ * POST /internal/medical/patient-jobs/ehr/athenahealth-create-resource-diff-bundles/:jobId/run
...
- * @param req.query.jobId - The job ID.
+ * @param req.params.jobId - The job ID.

26-74: Duplicate handler bodies → extract a helper to reduce copy-paste

Everything inside the two router.post callbacks is identical except the EHR source enum value. Small refactor keeps this DRY and avoids divergence later:

+function buildHandler(ehr: EhrSources) {
+  return asyncHandler(async (req: Request, res: Response) => {
+    const cxId = getFromQueryOrFail("cxId", req);
+    const jobId = getFrom("params").orFail("jobId", req);
+    const job   = await initializePatientJob({ cxId, jobId });
+    const { practiceId, ehrPatientId } = paramsOpsSchema.parse(job.paramsOps);
+
+    await runJob({
+      jobId,
+      ehr,
+      cxId,
+      practiceId,
+      metriportPatientId: job.patientId,
+      ehrPatientId,
+    });
+    return res.sendStatus(httpStatus.OK);
+  });
+}
+
 router.post(
-  "/athenahealth-create-resource-diff-bundles/:jobId/run",
-  requestLogger,
-  asyncHandler(async (...) => { ... })
+  "/athenahealth-create-resource-diff-bundles/:jobId/run",
+  requestLogger,
+  buildHandler(EhrSources.athena)
 );
 ...
 router.post(
-  "/canvas-create-resource-diff-bundles/:jobId/run",
-  requestLogger,
-  asyncHandler(async (...) => { ... })
+  "/canvas-create-resource-diff-bundles/:jobId/run",
+  requestLogger,
+  buildHandler(EhrSources.canvas)
 );
packages/core/src/command/job/patient/job/start-jobs/steps/get/get-jobs-cloud.ts (1)

24-32: Missing structured error context on retry failure

executeWithNetworkRetries will eventually throw after exhaust­ing retries, but the current code doesn’t enrich the error with the lambda name/payload, which makes on-call debugging harder.

 await executeWithNetworkRetries(async () =>
-  this.lambdaClient
+  this.lambdaClient
     .invoke({
       FunctionName: this.getPatientJobsLambdaName,
       InvocationType: "Event",
       Payload: payload,
     })
     .promise()
-, `Invoke ${this.getPatientJobsLambdaName}`
+).catch(err => {
+  err.message = `Invoke ${this.getPatientJobsLambdaName} failed – ${err.message}`;
+  err.payload = payload;
+  throw err;
+});
packages/lambdas/src/job/patient/run-job.ts (1)

49-54: Surface JSON-parse failures with more context

JSON.parse can throw, but the resulting stack trace does not indicate which job failed. Wrap it so the job id (if extractable) or the full message is included in the thrown MetriportError’s additionalInfo.

-  const bodyAsJson = JSON.parse(bodyString);
+  let bodyAsJson: unknown;
+  try {
+    bodyAsJson = JSON.parse(bodyString);
+  } catch (err) {
+    throw new MetriportError("Unable to parse SQS message body", { cause: err, additionalInfo: { bodySnippet: bodyString.slice(0, 200) } });
+  }

This will massively speed up triage when malformed messages appear.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1327494 and e0945c0.

📒 Files selected for processing (55)
  • packages/api/src/command/job/patient/create.ts (1 hunks)
  • packages/api/src/command/job/patient/get.ts (2 hunks)
  • packages/api/src/command/job/patient/scheduler/start-jobs.ts (1 hunks)
  • packages/api/src/command/job/patient/status/cancel.ts (1 hunks)
  • packages/api/src/command/job/patient/status/complete.ts (1 hunks)
  • packages/api/src/command/job/patient/status/fail.ts (1 hunks)
  • packages/api/src/command/job/patient/status/initialize.ts (1 hunks)
  • packages/api/src/command/job/patient/update/set-entry-status.ts (1 hunks)
  • packages/api/src/command/job/patient/update/update-runtime-data.ts (1 hunks)
  • packages/api/src/command/job/patient/update/update-total.ts (1 hunks)
  • packages/api/src/command/job/shared.ts (2 hunks)
  • packages/api/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/run-job.ts (1 hunks)
  • packages/api/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/start-job.ts (2 hunks)
  • packages/api/src/external/ehr/shared/utils/job.ts (1 hunks)
  • packages/api/src/models/patient-job.ts (3 hunks)
  • packages/api/src/routes/internal/ehr/patient.ts (1 hunks)
  • packages/api/src/routes/internal/medical/patient-job.ts (3 hunks)
  • packages/api/src/routes/internal/medical/patient-jobs/ehr.ts (1 hunks)
  • packages/api/src/routes/internal/medical/patient-jobs/index.ts (1 hunks)
  • packages/api/src/sequelize/migrations/2025-06-10_00_add-patient-job-schedule.ts (1 hunks)
  • packages/api/src/shared/config.ts (1 hunks)
  • packages/core/src/command/job/patient/api/complete-job.ts (2 hunks)
  • packages/core/src/command/job/patient/api/initialize-job.ts (2 hunks)
  • packages/core/src/command/job/patient/api/run-job.ts (1 hunks)
  • packages/core/src/command/job/patient/api/set-entry-status.ts (2 hunks)
  • packages/core/src/command/job/patient/api/shared.ts (1 hunks)
  • packages/core/src/command/job/patient/api/update-job-total.ts (2 hunks)
  • packages/core/src/command/job/patient/job/start-jobs/steps/get/get-jobs-cloud.ts (1 hunks)
  • packages/core/src/command/job/patient/job/start-jobs/steps/get/get-jobs-direct.ts (1 hunks)
  • packages/core/src/command/job/patient/job/start-jobs/steps/get/get-jobs-factory.ts (1 hunks)
  • packages/core/src/command/job/patient/job/start-jobs/steps/get/get-jobs.ts (1 hunks)
  • packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job-cloud.ts (1 hunks)
  • packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job-direct.ts (1 hunks)
  • packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job-factory.ts (1 hunks)
  • packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job.ts (1 hunks)
  • packages/core/src/external/ehr/api/job/shared.ts (0 hunks)
  • packages/core/src/external/ehr/job/create-resource-diff-bundles/steps/compute/ehr-compute-resource-diff-bundles-cloud.ts (1 hunks)
  • packages/core/src/external/ehr/job/create-resource-diff-bundles/steps/compute/ehr-compute-resource-diff-bundles.ts (1 hunks)
  • packages/core/src/external/ehr/job/create-resource-diff-bundles/steps/refresh/ehr-refresh-ehr-bundles-cloud.ts (1 hunks)
  • packages/core/src/external/ehr/job/create-resource-diff-bundles/steps/refresh/ehr-refresh-ehr-bundles.ts (1 hunks)
  • packages/core/src/util/config.ts (2 hunks)
  • packages/infra/config/env-config.ts (2 hunks)
  • packages/infra/config/example.ts (1 hunks)
  • packages/infra/lib/api-stack.ts (4 hunks)
  • packages/infra/lib/api-stack/api-service.ts (6 hunks)
  • packages/infra/lib/jobs/jobs-stack.ts (1 hunks)
  • packages/infra/lib/jobs/types.ts (1 hunks)
  • packages/infra/lib/secrets-stack.ts (1 hunks)
  • packages/infra/lib/shared/secrets.ts (1 hunks)
  • packages/infra/lib/surescripts/surescripts-stack.ts (1 hunks)
  • packages/lambdas/src/job/patient/get-jobs.ts (1 hunks)
  • packages/lambdas/src/job/patient/run-job.ts (1 hunks)
  • packages/shared/src/common/response.ts (1 hunks)
  • packages/shared/src/domain/job/job-status.ts (2 hunks)
  • packages/shared/src/domain/job/patient-job.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/core/src/external/ehr/api/job/shared.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.ts`: - Use the Onion Pattern to organize a package's code in layers - Try to use immutable code and avoid sharing state across different functions, objects, and systems - Try...

**/*.ts: - Use the Onion Pattern to organize a package's code in layers

  • Try to use immutable code and avoid sharing state across different functions, objects, and systems
  • Try to build code that's idempotent whenever possible
  • Prefer functional programming style functions: small, deterministic, 1 input, 1 output
  • Minimize coupling / dependencies
  • Avoid modifying objects received as parameter
  • Only add comments to code to explain why something was done, not how it works
  • Naming
    • classes, enums: PascalCase
    • constants, variables, functions: camelCase
    • file names: kebab-case
    • table and column names: snake_case
    • Use meaningful names, so whoever is reading the code understands what it means
    • Don’t use negative names, like notEnabled, prefer isDisabled
    • For numeric values, if the type doesn’t convey the unit, add the unit to the name
  • Typescript
    • Use types
    • Prefer const instead of let
    • Avoid any and casting from any to other types
    • Type predicates: only applicable to narrow down the type, not to force a complete type conversion
    • Prefer deconstructing parameters for functions instead of multiple parameters that might be of
      the same type
    • Don’t use null inside the app, only on code interacting with external interfaces/services,
      like DB and HTTP; convert to undefined before sending inwards into the code
    • Use async/await instead of .then()
    • Use the strict equality operator ===, don’t use abstract equality operator ==
    • When calling a Promise-returning function asynchronously (i.e., not awaiting), use .catch() to
      handle errors (see processAsyncError and emptyFunction depending on the case)
    • Date and Time
      • Always use buildDayjs() to create dayjs instances
      • Prefer dayjs.duration(...) to create duration consts and keep them as duration
  • Prefer Nullish Coalesce (??) than the OR operator (||) to provide a default value
  • Avoid creating arrow functions
  • Use truthy syntax instead of in - i.e., if (data.link) not if ('link' in data)
  • Error handling
    • Pass the original error as the new one’s cause so the stack trace is persisted
    • Error messages should have a static message - add dynamic data to MetriportError's additionalInfo prop
    • Avoid sending multiple events to Sentry for a single error
  • Global constants and variables
    • Move literals to constants declared after imports when possible (avoid magic numbers)
    • Avoid shared, global objects
  • Avoid using console.log and console.error in packages other than utils, infra and shared,
    and try to use out().log instead
  • Avoid multi-line logs
    • don't send objects as a second parameter to console.log() or out().log()
    • don't create multi-line strings when using JSON.stringify()
  • Use eslint to enforce code style
  • Use prettier to format code
  • max column length is 100 chars
  • multi-line comments use /** */
  • scripts: top-level comments go after the import
  • packages/core/src/external/ehr/job/create-resource-diff-bundles/steps/refresh/ehr-refresh-ehr-bundles-cloud.ts
  • packages/api/src/routes/internal/ehr/patient.ts
  • packages/core/src/external/ehr/job/create-resource-diff-bundles/steps/compute/ehr-compute-resource-diff-bundles-cloud.ts
  • packages/core/src/external/ehr/job/create-resource-diff-bundles/steps/refresh/ehr-refresh-ehr-bundles.ts
  • packages/core/src/command/job/patient/api/complete-job.ts
  • packages/core/src/command/job/patient/api/initialize-job.ts
  • packages/api/src/command/job/patient/update/update-total.ts
  • packages/api/src/command/job/patient/status/initialize.ts
  • packages/core/src/external/ehr/job/create-resource-diff-bundles/steps/compute/ehr-compute-resource-diff-bundles.ts
  • packages/api/src/command/job/patient/status/complete.ts
  • packages/core/src/command/job/patient/api/set-entry-status.ts
  • packages/infra/lib/secrets-stack.ts
  • packages/infra/lib/surescripts/surescripts-stack.ts
  • packages/api/src/command/job/patient/update/set-entry-status.ts
  • packages/api/src/routes/internal/medical/patient-jobs/index.ts
  • packages/infra/lib/shared/secrets.ts
  • packages/core/src/command/job/patient/api/shared.ts
  • packages/api/src/shared/config.ts
  • packages/infra/lib/jobs/types.ts
  • packages/api/src/command/job/patient/scheduler/start-jobs.ts
  • packages/api/src/sequelize/migrations/2025-06-10_00_add-patient-job-schedule.ts
  • packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job-factory.ts
  • packages/core/src/command/job/patient/job/start-jobs/steps/get/get-jobs-factory.ts
  • packages/lambdas/src/job/patient/run-job.ts
  • packages/core/src/command/job/patient/api/update-job-total.ts
  • packages/infra/config/example.ts
  • packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job.ts
  • packages/api/src/command/job/patient/update/update-runtime-data.ts
  • packages/shared/src/domain/job/job-status.ts
  • packages/core/src/command/job/patient/job/start-jobs/steps/get/get-jobs-cloud.ts
  • packages/infra/config/env-config.ts
  • packages/lambdas/src/job/patient/get-jobs.ts
  • packages/api/src/external/ehr/shared/utils/job.ts
  • packages/core/src/util/config.ts
  • packages/api/src/command/job/shared.ts
  • packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job-direct.ts
  • packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job-cloud.ts
  • packages/api/src/command/job/patient/status/fail.ts
  • packages/api/src/command/job/patient/status/cancel.ts
  • packages/api/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/start-job.ts
  • packages/shared/src/common/response.ts
  • packages/core/src/command/job/patient/job/start-jobs/steps/get/get-jobs.ts
  • packages/shared/src/domain/job/patient-job.ts
  • packages/api/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/run-job.ts
  • packages/api/src/models/patient-job.ts
  • packages/infra/lib/api-stack/api-service.ts
  • packages/core/src/command/job/patient/api/run-job.ts
  • packages/core/src/command/job/patient/job/start-jobs/steps/get/get-jobs-direct.ts
  • packages/api/src/routes/internal/medical/patient-jobs/ehr.ts
  • packages/infra/lib/api-stack.ts
  • packages/infra/lib/jobs/jobs-stack.ts
  • packages/api/src/command/job/patient/create.ts
  • packages/api/src/command/job/patient/get.ts
  • packages/api/src/routes/internal/medical/patient-job.ts
🧠 Learnings (1)
packages/api/src/routes/internal/medical/patient-job.ts (1)
Learnt from: leite08
PR: metriport/metriport#3814
File: packages/api/src/routes/internal/medical/patient-consolidated.ts:141-174
Timestamp: 2025-05-20T21:26:26.804Z
Learning: The functionality introduced in packages/api/src/routes/internal/medical/patient-consolidated.ts is planned to be refactored in downstream PR #3857, including improvements to error handling and validation.
🧬 Code Graph Analysis (20)
packages/core/src/command/job/patient/api/complete-job.ts (1)
packages/shared/src/common/response.ts (1)
  • logAxiosResponse (17-23)
packages/core/src/command/job/patient/api/initialize-job.ts (1)
packages/shared/src/common/response.ts (1)
  • logAxiosResponse (17-23)
packages/core/src/command/job/patient/api/set-entry-status.ts (1)
packages/shared/src/common/response.ts (1)
  • logAxiosResponse (17-23)
packages/core/src/command/job/patient/api/shared.ts (1)
packages/shared/src/domain/job/patient-job.ts (1)
  • PatientJob (4-26)
packages/api/src/command/job/patient/scheduler/start-jobs.ts (2)
packages/api/src/command/job/shared.ts (1)
  • StartJobsParams (3-9)
packages/core/src/command/job/patient/job/start-jobs/steps/get/get-jobs-factory.ts (1)
  • buildGetJobsHandler (6-13)
packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job-factory.ts (3)
packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job.ts (1)
  • RunJobHandler (6-8)
packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job-direct.ts (1)
  • RunJobDirect (4-12)
packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job-cloud.ts (1)
  • RunJobCloud (6-19)
packages/core/src/command/job/patient/api/update-job-total.ts (1)
packages/shared/src/common/response.ts (1)
  • logAxiosResponse (17-23)
packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job.ts (1)
packages/shared/src/domain/job/patient-job.ts (1)
  • PatientJob (4-26)
packages/api/src/command/job/patient/update/update-runtime-data.ts (3)
packages/api/src/command/job/shared.ts (1)
  • UpdateJobRuntimeDataParams (45-49)
packages/shared/src/domain/job/patient-job.ts (1)
  • PatientJob (4-26)
packages/api/src/command/job/patient/get.ts (1)
  • getPatientJobModelOrFail (27-31)
packages/api/src/external/ehr/shared/utils/job.ts (1)
packages/shared/src/interface/external/ehr/source.ts (1)
  • EhrSource (9-9)
packages/api/src/command/job/shared.ts (1)
packages/shared/src/domain/job/job-status.ts (1)
  • JobStatus (4-4)
packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job-direct.ts (2)
packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job.ts (2)
  • RunJobHandler (6-8)
  • RunJobRequest (3-4)
packages/core/src/command/job/patient/api/run-job.ts (1)
  • runJob (19-39)
packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job-cloud.ts (1)
packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job.ts (2)
  • RunJobHandler (6-8)
  • RunJobRequest (3-4)
packages/api/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/start-job.ts (4)
packages/api/src/external/ehr/shared/utils/job.ts (1)
  • getCreateResourceDiffBundlesJobType (33-35)
packages/api/src/command/job/patient/get.ts (1)
  • getLatestPatientJob (71-92)
packages/shared/src/index.ts (1)
  • BadRequestError (40-40)
packages/api/src/command/job/patient/create.ts (1)
  • createPatientJob (12-41)
packages/shared/src/common/response.ts (1)
packages/shared/src/index.ts (1)
  • MetriportError (41-41)
packages/core/src/command/job/patient/job/start-jobs/steps/get/get-jobs.ts (1)
packages/shared/src/domain/job/patient-job.ts (1)
  • PatientJob (4-26)
packages/infra/lib/api-stack/api-service.ts (1)
packages/infra/lib/jobs/types.ts (1)
  • JobsAssets (4-8)
packages/infra/lib/api-stack.ts (1)
packages/infra/lib/jobs/jobs-stack.ts (1)
  • JobsNestedStack (69-209)
packages/infra/lib/jobs/jobs-stack.ts (4)
packages/infra/lib/shared/settings.ts (2)
  • LambdaSettings (35-35)
  • QueueAndLambdaSettings (5-33)
packages/infra/lib/shared/secrets.ts (1)
  • buildSecret (16-18)
packages/infra/lib/shared/lambda-scheduled.ts (1)
  • createScheduledLambda (28-61)
packages/infra/lib/jobs/types.ts (1)
  • JobsAssets (4-8)
packages/api/src/routes/internal/medical/patient-job.ts (9)
packages/terminology/src/util.ts (1)
  • asyncHandler (6-22)
packages/shared/src/domain/job/job-status.ts (2)
  • isValidJobStatus (6-14)
  • JobStatus (4-4)
packages/api/src/command/job/patient/get.ts (1)
  • getPatientJobs (40-62)
packages/shared/src/common/date.ts (1)
  • buildDayjs (86-88)
packages/api/src/command/job/patient/scheduler/start-jobs.ts (1)
  • startJobs (14-25)
packages/api/src/routes/schemas/uuid.ts (1)
  • getUUIDFrom (25-30)
packages/api/src/command/job/patient/status/fail.ts (1)
  • failPatientJob (19-46)
packages/api/src/command/job/patient/status/cancel.ts (1)
  • cancelPatientJob (19-48)
packages/api/src/command/job/patient/update/update-runtime-data.ts (1)
  • updatePatientJobRuntimeData (15-26)
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (26)
packages/core/src/external/ehr/job/create-resource-diff-bundles/steps/refresh/ehr-refresh-ehr-bundles-cloud.ts (1)

5-5: Consistent import path for shared utilities.
The import for createSqsGroupId has been updated to reference the consolidated shared module, aligning with the other diff bundle steps.

packages/core/src/external/ehr/job/create-resource-diff-bundles/steps/compute/ehr-compute-resource-diff-bundles-cloud.ts (1)

5-5: Aligned import for shared utilities.
Switching the createSqsGroupId import to "../../shared" maintains consistency with the refactored module structure.

packages/core/src/external/ehr/job/create-resource-diff-bundles/steps/compute/ehr-compute-resource-diff-bundles.ts (1)

1-1: Update type import to centralized shared module.
The CreateResourceDiffBundlesBaseRequest type now imports from the consolidated "../../shared" path, matching other step modules.

packages/core/src/external/ehr/job/create-resource-diff-bundles/steps/refresh/ehr-refresh-ehr-bundles.ts (1)

1-1: Centralize base request type import.
Importing CreateResourceDiffBundlesBaseRequest from "../../shared" keeps shared types in one place.

packages/api/src/routes/internal/ehr/patient.ts (1)

7-7: Updated import for patient job status command.
The setPatientJobEntryStatus function now correctly points to the update submodule, reflecting the restructured command directory.

packages/core/src/command/job/patient/api/complete-job.ts (1)

23-23: Double-check PHI/PII exposure in debug logs

logAxiosResponse dumps response.data verbatim. For patient-level APIs this may include protected health information. Ensure:

  1. The debug logger is disabled or redacted in production.
  2. No log aggregation sink stores raw payloads.

If not guaranteed, consider masking or selectively logging.

packages/core/src/command/job/patient/api/initialize-job.ts (1)

23-23: Validate success criteria explicitly

validateAndLogResponse previously enforced a schema / status check. Now we only log. Although Axios rejects non-2xx responses, any contract validation (e.g., required fields) is now skipped. If that validation was important, re-introduce it (e.g., zod schema).

packages/api/src/command/job/patient/update/update-total.ts (1)

2-3: Import-path tweak LGTM

The relative-path correction (../shared../../shared) and move of getPatientJobModelOrFail are correct and compile-time safe.

packages/api/src/command/job/patient/status/complete.ts (1)

4-5: Path update looks good

The import-path changes keep the module layout coherent with the new folder structure. No behavioural impact.

packages/infra/config/env-config.ts (1)

285-289: New jobs section is mandatory – could break existing config files

Making the jobs object required means all non-updated env-config.ts variants (sandbox, staging forks, developer-specific overrides) will fail type-checking and at runtime.
If that disruption is intentional, ignore this comment.
Otherwise mark it optional with sensible defaults:

-  jobs: {
+  jobs?: {
     roUsername: string;
     startPatientJobsSchedulerScheduleExpression: string;
     startPatientJobsSchedulerUrl: string;
   };

and update consuming code to handle the undefined case gracefully.

packages/api/src/command/job/patient/update/set-entry-status.ts (1)

2-7: Import path updates look correct

Imports now point to the shared domain package and the refactored folder layout; build should resolve them without issues. No further action required.

packages/infra/lib/surescripts/surescripts-stack.ts (1)

14-14: Centralising buildSecret is the right move – good cleanup

Importing the helper from ../shared/secrets removes duplication across stacks and keeps secret handling consistent. No additional concerns here.

packages/infra/config/example.ts (1)

231-235: Ensure type definitions & downstream stacks include jobs block

jobs is added only in the example file. Confirm that:

  1. EnvConfigNonSandbox (and any related runtime validation) now includes the jobs property.
  2. SecretsStack, JobsNestedStack, etc., read these new fields from typed config rather than any, otherwise compilation will break.
packages/api/src/external/ehr/shared/utils/job.ts (1)

14-21: Addition looks correct and aligns with new workflow

RunCreateResourceDiffBundlesJobParams captures the extra identifiers (jobId, metriportPatientId) needed at run time. No issues spotted.

packages/infra/lib/jobs/types.ts (1)

4-8: Type-alias looks good and follows naming conventions

JobsAssets provides a clear contract for the CDK stack, and the alias fits the project’s naming/style guidelines. No concerns here.

packages/core/src/command/job/patient/job/start-jobs/steps/get/get-jobs-factory.ts (1)

6-13: Ensure required env vars exist in every non-dev deployment

Config.getPatientJobsLambdaName() will throw at runtime if GET_PATIENT_JOBS_LAMBDA_NAME is missing. That’s correct behaviour, but please double-check that:

  1. The var is injected in all CI/CD targets (staging / prod / sandbox).
  2. Integration tests cover a mis-configured environment so failures surface early.

If that’s already covered, feel free to ignore.

packages/shared/src/domain/job/patient-job.ts (1)

32-53: Constant mapping looks correct

All new columns are mapped consistently to their snake_case counterparts.

packages/infra/lib/api-stack/api-service.ts (2)

29-30: Missing dependency check on upstream stack

JobsAssets is imported but the calling stacks must now always provide it. Verify every createAPIService invocation, including tests and non-prod stacks, passes this argument to avoid compilation failures.


524-528: Queue permission looks good

Granting send access to the run-job queue is correct and consistent with other queues in the file. 👍

packages/core/src/command/job/patient/job/start-jobs/steps/run/run-job-cloud.ts (1)

13-18: Potential Date serialization issue

JSON.stringify converts Date objects to ISO strings. If downstream consumers expect JS Date instances, they’ll instead receive strings, which may cause subtle bugs. Clarify the contract in RunJobRequest or explicitly transform dates.

packages/api/src/command/job/patient/status/fail.ts (1)

30-34: Retried fail requests are not idempotent

Analogous to cancellation, re-failing an already failed job throws.
Evaluate whether idempotency is desirable for public endpoints.

packages/api/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/start-job.ts (1)

41-55:

❌ Incorrect review comment

Concurrency guard ignores scheduled jobs

The “only one job at a time” check filters by statuses waiting | processing, but scheduled jobs may also clash if they execute soon after. Consider including scheduled (or whatever status you use) to truly prevent duplicates.

Would adding it break any legitimate flow?
Search for all status values in patient-job lifecycle to confirm:


🏁 Script executed:

#!/bin/bash
# list status literals in shared domain
rg -o $'\"(scheduled|waiting|processing)\"' -n packages/shared | sort -u

Length of output: 2693


🏁 Script executed:

#!/bin/bash
# Locate and display the first 200 lines of the job-status definition
job_status_file=$(rg -l 'job-status.ts' -n packages/shared | head -n1)
echo "Inspecting: $job_status_file"
sed -n '1,200p' "$job_status_file"

Length of output: 170


#!/bin/bash
set -e
# Find the job-status definition file
file=$(find packages/shared -type f -name 'job-status.ts' | head -n1)
if [[ -z "$file" ]]; then
  echo "ERROR: job-status.ts not found" >&2
  exit 1
fi
echo "Inspecting: $file"
echo "----- First 200 lines -----"
head -n200 "$file"
echo
echo "----- All status literals -----"
grep -Eo "['\"][a-zA-Z]+['\"]" "$file" | sort -u

Ignore suggestion to include “scheduled”; no such status exists
A search across packages/shared shows only “waiting” and “processing” literals in src/domain/job/job-status.ts. There isn’t a “scheduled” status defined, so the current guard is correct and no filter change is needed.

Likely an incorrect or invalid review comment.

packages/api/src/command/job/patient/get.ts (2)

33-62: Well-implemented filtering with proper ORM usage

The new ListPatientJobsParams type and getPatientJobs function are well-designed. The implementation correctly uses Sequelize's ORM features with parameterized queries, avoiding SQL injection risks.


64-77: Good separation of concerns with specific type naming

The type rename from ListPatientJobsParams to ListLatestPatientJobsParams improves code clarity by distinguishing between the general job listing and latest job retrieval use cases.

packages/infra/lib/jobs/jobs-stack.ts (1)

74-111: Well-structured infrastructure with proper security practices

The stack implementation follows AWS CDK best practices:

  • Enables termination protection for production safety
  • Uses read-only database credentials from Secrets Manager
  • Properly configures Lambda functions with appropriate permissions
packages/api/src/routes/internal/medical/patient-job.ts (1)

43-68: Well-implemented endpoint with proper validation

The GET endpoint properly validates the status parameter and safely parses date inputs using buildDayjs.

Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Copy link
@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

🧹 Nitpick comments (1)
packages/core/src/command/job/patient/command/run-job/run-job-cloud.ts (1)

8-8: Consider dependency injection for the SQS client.

The SQS client is instantiated directly within the class, which reduces testability and flexibility. Consider injecting the SQS client through the constructor to improve testability and follow dependency inversion principles.

export class RunJobCloud implements RunJobHandler {
-  private readonly sqsClient: SQSClient = new SQSClient({ region: Config.getAWSRegion() });
+  private readonly sqsClient: SQSClient;

-  constructor(private readonly runJobQueueUrl: string) {}
+  constructor(
+    private readonly runJobQueueUrl: string,
+    sqsClient?: SQSClient
+  ) {
+    this.sqsClient = sqsClient ?? new SQSClient({ region: Config.getAWSRegion() });
+  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d964cad and 207e957.

📒 Files selected for processing (5)
  • packages/core/src/command/job/patient/command/run-job/run-job-cloud.ts (1 hunks)
  • packages/core/src/util/config.ts (1 hunks)
  • packages/infra/lib/api-stack.ts (5 hunks)
  • packages/infra/lib/api-stack/api-service.ts (5 hunks)
  • packages/infra/lib/surescripts/surescripts-stack.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/core/src/util/config.ts
  • packages/infra/lib/surescripts/surescripts-stack.ts
  • packages/infra/lib/api-stack/api-service.ts
  • packages/infra/lib/api-stack.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.ts`: - Use the Onion Pattern to organize a package's code in layers - Try to use immutable code and avoid sharing state across different functions, objects, and systems - Try...

**/*.ts: - Use the Onion Pattern to organize a package's code in layers

  • Try to use immutable code and avoid sharing state across different functions, objects, and systems
  • Try to build code that's idempotent whenever possible
  • Prefer functional programming style functions: small, deterministic, 1 input, 1 output
  • Minimize coupling / dependencies
  • Avoid modifying objects received as parameter
  • Only add comments to code to explain why something was done, not how it works
  • Naming
    • classes, enums: PascalCase
    • constants, variables, functions: camelCase
    • file names: kebab-case
    • table and column names: snake_case
    • Use meaningful names, so whoever is reading the code understands what it means
    • Don’t use negative names, like notEnabled, prefer isDisabled
    • For numeric values, if the type doesn’t convey the unit, add the unit to the name
  • Typescript
    • Use types
    • Prefer const instead of let
    • Avoid any and casting from any to other types
    • Type predicates: only applicable to narrow down the type, not to force a complete type conversion
    • Prefer deconstructing parameters for functions instead of multiple parameters that might be of
      the same type
    • Don’t use null inside the app, only on code interacting with external interfaces/services,
      like DB and HTTP; convert to undefined before sending inwards into the code
    • Use async/await instead of .then()
    • Use the strict equality operator ===, don’t use abstract equality operator ==
    • When calling a Promise-returning function asynchronously (i.e., not awaiting), use .catch() to
      handle errors (see processAsyncError and emptyFunction depending on the case)
    • Date and Time
      • Always use buildDayjs() to create dayjs instances
      • Prefer dayjs.duration(...) to create duration consts and keep them as duration
  • Prefer Nullish Coalesce (??) than the OR operator (||) to provide a default value
  • Avoid creating arrow functions
  • Use truthy syntax instead of in - i.e., if (data.link) not if ('link' in data)
  • Error handling
    • Pass the original error as the new one’s cause so the stack trace is persisted
    • Error messages should have a static message - add dynamic data to MetriportError's additionalInfo prop
    • Avoid sending multiple events to Sentry for a single error
  • Global constants and variables
    • Move literals to constants declared after imports when possible (avoid magic numbers)
    • Avoid shared, global objects
  • Avoid using console.log and console.error in packages other than utils, infra and shared,
    and try to use out().log instead
  • Avoid multi-line logs
    • don't send objects as a second parameter to console.log() or out().log()
    • don't create multi-line strings when using JSON.stringify()
  • Use eslint to enforce code style
  • Use prettier to format code
  • max column length is 100 chars
  • multi-line comments use /** */
  • scripts: top-level comments go after the import
  • packages/core/src/command/job/patient/command/run-job/run-job-cloud.ts
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
packages/core/src/command/job/patient/command/run-job/run-job-cloud.ts (1)

12-21: LGTM with proper error handling via network retries.

The method implementation follows good practices:

  • Uses async/await as per guidelines
  • Implements proper JSON serialization
  • Uses network retry mechanism for reliability
  • Follows functional programming style with single responsibility
  • Uses meaningful parameter names

The retry mechanism adequately handles transient failures, and the FIFO queue configuration with message grouping ensures proper job ordering.

Thomas Yopes added 3 commits June 18, 2025 10:13
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
@thomasyopes thomasyopes requested a review from leite08 June 18, 2025 17:39
Comment on lines 79 to 80
const msg = "Failed to run some scheduled patient jobs";
capture.message(msg, {
Copy link
Member

Choose a reason for hiding this comment

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

log it too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think we need to log it unless there's some extra data not going to sentry. I know sending to sentry isn't bullet-proof but should be sufficient for what we need.

Copy link
Member

Choose a reason for hiding this comment

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

Logging is super errors is super useful when we're debugging stuff in Cloudwatch. If I don't see a long on CWL I assume the code worked.

Copy link
Contributor

Choose a reason for hiding this comment

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

💯 lets add logs

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Thomas Yopes added 2 commits June 18, 2025 14:16
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
… eng-433-scheduler-for-patient-job

Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Copy link
Contributor
@RamilGaripov RamilGaripov left a comment

Choose a reason for hiding this comment

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

Approving with 2 nits

Comment on lines 79 to 80
const msg = "Failed to run some scheduled patient jobs";
capture.message(msg, {
Copy link
Contributor

Choose a reason for hiding this comment

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

💯 lets add logs

*
* @param jobId - The job ID.
* @param cxId - The customer ID.
* @param reason - The reason for cancelling the job.
Copy link
Contributor

Choose a reason for hiding this comment

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

hypernit: The reason for failing the job.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch. Done.

Comment on lines 25 to 26
* @param req.query.cxId - The CX ID.
* @param req.query.jobId - The job ID.
Copy link
Contributor

Choose a reason for hiding this comment

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

these are no longer query params

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch. Done.

return res.sendStatus(httpStatus.OK);
})
);

/**
* POST /internal/patient/job/:jobId/update-total
Copy link
Contributor

Choose a reason for hiding this comment

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

btw, this is wrong. The route is /internal/patient/job/:jobId/total

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch. Done.

Thomas Yopes added 3 commits June 19, 2025 07:33
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
… eng-433-scheduler-for-patient-job

Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
}));
log(
`${msg}. Cause: ${errors
.map(error => `\n${error.id} ${error.cxId} ${error.error}`)
Copy link
Member

Choose a reason for hiding this comment

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

nit: cxId and patientId can go on the log prefix since its a param for the fn, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Member

Choose a reason for hiding this comment

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

Is this due to an outdated pull from develop? Can you merge/rebase to make sure, please

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's the result of shift+option+O

Ref: ENG-281

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Copy link
Contributor

Choose a reason for hiding this comment

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

AFAICT, this isn't used anywhere. Let's remove it.

Copy link
Contributor

Choose a reason for hiding this comment

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

The route it's pointing to doesnt even exist

Copy link
Contributor Author

Choose a reason for hiding this comment

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

/**
 * POST /internal/patient/job/:jobId/total
 *
 * Updates the total of the job.
 * @param req.query.cxId - The CX ID.
 * @param req.params.jobId - The job ID.
 * @param req.query.total - The total number of entries to process.
 * @returns 200 OK
 */
router.post(
  "/:jobId/total",
  requestLogger,
  asyncHandler(async (req: Request, res: Response) => {
    const cxId = getUUIDFrom("query", req, "cxId").orFail();
    const jobId = getFrom("params").orFail("jobId", req);
    const total = getFromQueryOrFail("total", req);
    if (isNaN(+total)) {
      throw new BadRequestError("Total must be a number");
    }
    await updatePatientJobTotal({
      jobId,
      cxId,
      total: +total,
    });
    return res.sendStatus(httpStatus.OK);
  })
);

in patient-job.ts route file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed the core command.

Thomas Yopes added 2 commits June 19, 2025 13:02
Ref: ENG-433

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
… eng-433-scheduler-for-patient-job

Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
@thomasyopes thomasyopes added this pull request to the merge queue Jun 19, 2025
Merged via the queue into develop with commit 7f7f638 Jun 19, 2025
21 checks passed
@thomasyopes thomasyopes deleted the eng-433-scheduler-for-patient-job branch June 19, 2025 20:24
@thomasyopes thomasyopes mentioned this pull request Jun 19, 2025
2 tasks
This was referenced Jun 25, 2025
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.

3 participants
0