Skip to content

Conversation

thomasyopes
Copy link
Contributor

@thomasyopes thomasyopes commented May 19, 2025

Ref: ENG-292

Issues:

Dependencies

Description

ENTRYPONT: Canvas process-patients-from-appointments.ts

  • adding new get-appointments lambda (currently Canvas get appointments only) and command lookup map
  • adding new get-token-info internal endpoint for getting token info for a client in a lambda
  • adding new get-secrets internal endpoint per practice for getting secrets in the event the token is expired and needs to be refreshed
  • moved secrets code to core in preparation for https://linear.app/metriport/issue/ENG-405/move-ehr-secrets-management-to-lambda-oriented-structure-to-support
  • introduced new command folder
  • wired Canavs get-appointments API command to use new lambda connector
  • added infra for get-appointments lambda connector
  • added lambda code for get-appointments lambda connector + adding permissions to write to the EHR responses bucket
  • converted async s3 writes in the shared request handler to synchronous now that it'll be running on a lambda sometimes

Testing

  • Local
    • canvas get-appointments runs correctly on "local" implementation with no token info (will force the client to fetch secrets and create a NEW token from handler)
    • canvas get-appointments runs correctly on "local" implementation with token id (will have the client fetch the token info)
    • other EHRs still work as expected for client generation
  • Staging
    • canvas get-appointments runs correctly on "local" implementation with token id (will have the client fetch the token info)
    • other EHRs still work as expected for client generation
  • Sandbox
    • N/A
  • Production
    • canvas get-appointments runs correctly on "local" implementation with token id (will have the client fetch the token info)
    • other EHRs still work as expected for client generation

Release Plan

  • Merge this

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added internal endpoints to retrieve EHR practice secrets and JWT token information.
    • Introduced support for appointment retrieval from EHR systems with a new Lambda handler and API integration.
    • Added environment and credential management modules for AthenaHealth, Canvas, Elation, Healthie, and EClinicalWorks.
    • Added new Zod schemas for appointment requests and secret types.
    • Introduced factory and handler classes for EHR appointment fetching with direct and Lambda invocation methods.
  • Improvements

    • Enhanced EHR client creation functions to include token ID and environment metadata.
    • Centralized environment and credential retrieval for all EHR providers.
    • Improved appointment lookup window logic for Healthie integration.
    • Updated error handling and logging for secret and token retrieval operations.
    • Added permissions and environment variables for new EHR appointments Lambda in infrastructure.
    • Added controlled delays between paginated API requests across multiple EHR clients to manage request pacing.
    • Enhanced error handling for asynchronous S3 uploads and refined FHIR validation error detection.
  • Bug Fixes

    • Corrected time range calculations to ensure proper appointment look-forward windows.
  • Chores

    • Refactored import paths and reorganized code structure for improved maintainability.
    • Removed unused fake data generation utilities for Canvas EHR.
    • Added explicit error handling for asynchronous S3 uploads.
    • Updated configuration management by moving EHR environment variables to centralized modules.

Ref: ENG-292

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

linear bot commented May 19, 2025

Copy link

coderabbitai bot commented May 19, 2025

Walkthrough

This change introduces a new, modular approach for retrieving EHR appointment data and handling EHR client environments and secrets. It adds a Lambda-based get-appointments handler, new environment/secret retrieval modules for multiple EHRs, refactors client creation logic, updates import paths, and extends internal API routes for practice and token management.

Changes

Files/Groups Change Summary
packages/core/src/external/ehr/command/get-appointments/…
packages/lambdas/src/ehr/get-appointments.ts
packages/api/src/external/ehr/canvas/command/process-patients-from-appointments.ts
Added Lambda-based and direct appointment retrieval handlers, request/response types, and wiring for Canvas get-appointments; introduced new Lambda handler and schema validation.
packages/core/src/external/ehr/athenahealth/environment.ts
canvas/environment.ts
elation/environment.ts
healthie/environment.ts
eclinicalworks/environment.ts
Added new environment/secret retrieval modules for Athena, Canvas, Elation, Healthie, and EClinicalWorks, with error handling and validation.
packages/core/src/external/ehr/environment.ts
shared/utils/client.ts
Centralized EHR environment and client credential types; updated client creation to return metadata (tokenId, environment).
packages/api/src/external/ehr/{athenahealth,canvas,elation,healthie}/shared.ts Refactored EHR client creation: moved env/secret retrieval to new modules, updated signatures to return metadata, preserved legacy interface for backwards compatibility.
packages/core/src/external/ehr/command/get-secrets.ts Added generic getSecrets function to route secret retrieval for different EHRs.
packages/core/src/external/ehr/api/get-client-key-and-secret.ts
api/get-token-info.ts
Added API utilities for fetching EHR client secrets and JWT token info, with validation and error handling.
packages/core/src/external/ehr/canvas/shared.ts Added new async Canvas client creation function supporting token info retrieval.
packages/core/src/external/ehr/canvas/command/get-appointments.ts Added Canvas-specific appointment retrieval function supporting token and environment.
packages/core/src/external/ehr/shared.ts Added Zod schemas/types for OAuth and API key secrets; improved error logging for S3 uploads.
packages/core/src/external/ehr/shared/utils/appointment.ts Fixed time range calculation for appointment look-forward with offset.
packages/api/src/external/ehr/healthie/command/process-patients-from-appointments.ts Updated appointment look-forward logic and import paths.
packages/api/src/external/ehr/eclinicalworks/shared.ts Delegated EClinicalWorks environment retrieval to new external module.
packages/api/src/external/ehr/elation/command/process-patients-from-appointments.ts
athenahealth/command/process-patients-from-appointments.ts
canvas/command/process-patients-from-appointments.ts
Updated import paths for sync/link patient handlers to new command locations.
packages/api/src/routes/ehr/{canvas,elation,healthie}/patient-webhook.ts
elation/appointment-webhook.ts
Updated import paths for sync patient handler.
packages/api/src/routes/ehr/util.ts Added explicit curly braces in conditional block for clarity.
packages/api/src/routes/internal/ehr/practice.ts
internal/ehr/index.ts
Added new internal route for /internal/ehr/:id/secrets to retrieve practice secrets.
packages/api/src/routes/internal/jwt-token/token.ts
internal/jwt-token/index.ts
Added new internal route for /internal/token/:id to retrieve JWT token info.
packages/core/src/external/aws/lambda.ts Added LambdaClient type alias and explicit return type for client creation.
packages/core/src/util/config.ts Added static methods to retrieve EHR-related environment variables and Lambda name.
packages/infra/lib/ehr-nested-stack.ts
api-stack.ts
api-stack/api-service.ts
Provisioned new Lambda for get-appointments, wired to API service, added env var and permissions.
packages/core/src/external/ehr/canvas/data.ts Removed fake FHIR bundle/data generation utilities.
packages/lambdas/src/shared/ehr.ts Added Zod schema for get-appointments requests; updated import paths.
packages/shared/src/domain/jwt-token.ts Added optional id property to JwtTokenInfo type.
packages/core/src/external/ehr/command/sync-patient/…
elation/command/link-patient/…
healthie/command/link-patient/…
job/create-resource-diff-bundles/steps/…
Updated import paths for config, SQS, and other modules for consistency and new structure.
packages/core/src/external/ehr/athenahealth/index.ts
canvas/index.ts
elation/index.ts
healthie/index.ts
Added delays (sleep) before paginated API requests to throttle calls in EHR clients.

Sequence Diagram(s)

EHR Get Appointments Lambda (Cloud Path)

sequenceDiagram
    participant API Service
    participant EhrGetAppointmentsCloud
    participant AWS Lambda (get-appointments)
    participant CanvasApi

    API Service->>EhrGetAppointmentsCloud: getAppointments(request)
    EhrGetAppointmentsCloud->>AWS Lambda (get-appointments): Invoke with request
    AWS Lambda (get-appointments)->>CanvasApi: getAppointments(params)
    CanvasApi-->>AWS Lambda (get-appointments): Appointments[]
    AWS Lambda (get-appointments)-->>EhrGetAppointmentsCloud: Appointments[]
    EhrGetAppointmentsCloud-->>API Service: Appointments[]
Loading

EHR Client Creation with Secret Retrieval

sequenceDiagram
    participant Caller
    participant createEhrClientWithTokenIdAndEnvironment
    participant getSecrets (per EHR)
    participant getTokenInfo (optional)
    participant ApiClient

    Caller->>createEhrClientWithTokenIdAndEnvironment: (cxId, practiceId, tokenId?)
    createEhrClientWithTokenIdAndEnvironment->>getSecrets: (cxId, practiceId)
    getSecrets-->>createEhrClientWithTokenIdAndEnvironment: { env, clientKey, clientSecret }
    alt tokenId provided
        createEhrClientWithTokenIdAndEnvironment->>getTokenInfo: (tokenId)
        getTokenInfo-->>createEhrClientWithTokenIdAndEnvironment: tokenInfo
    end
    createEhrClientWithTokenIdAndEnvironment->>ApiClient: create({ env, credentials, tokenInfo })
    ApiClient-->>createEhrClientWithTokenIdAndEnvironment: client
    createEhrClientWithTokenIdAndEnvironment-->>Caller: { client, tokenId, environment }
Loading

Possibly related PRs

  • metriport/metriport#3788: Refactors and reorganizes EHR resource diff job logic and shared modules, closely related to this PR's restructuring of EHR job and handler code.
  • metriport/metriport#3754: Introduces Healthie EHR integration, overlapping with this PR's updates to Healthie client creation, environment handling, and command modules.
  • metriport/metriport#3874: Implements initial EClinicalWorks client and JWT routing, related to this PR's addition of EClinicalWorks environment handling and JWT token info retrieval.

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: C04CA0905B7F0000: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-05T14_00_01_722Z-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 a64ac52 and fe49db6.

📒 Files selected for processing (2)
  • packages/core/src/external/ehr/canvas/index.ts (5 hunks)
  • packages/lambdas/src/ehr/get-appointments.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/lambdas/src/ehr/get-appointments.ts
  • packages/core/src/external/ehr/canvas/index.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 added 6 commits May 20, 2025 10:27
Ref: ENG-199

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

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

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

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

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

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
@@ -220,7 +219,7 @@ async function getAppointmentsFromApi({
}
if (lookupMode === LookupModes.Appointments48hr) {
const { startRange, endRange } = getLookForwardTimeRangeWithOffset({
lookForward: appointmentsLookForward48hr,
lookForward: appointmentsLookForward,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

offset is now applied to both dates so it's now just a 1day look-forward + 1day offset

Ref: ENG-199

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Thomas Yopes added 4 commits May 20, 2025 12:04
Ref: ENG-199

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

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

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

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
@thomasyopes thomasyopes changed the title feat(ehr): moving appointments + resource fetching to lambda feat(ehr): moving appointments for Canvas May 20, 2025
@thomasyopes thomasyopes marked this pull request as ready for review May 20, 2025 19:31
@thomasyopes thomasyopes changed the title feat(ehr): moving appointments for Canvas feat(ehr): moving get-appointments for Canvas to lambda May 20, 2025
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: 11

♻️ Duplicate comments (1)
packages/api/src/external/ehr/healthie/command/process-patients-from-appointments.ts (1)

222-224: LGTM! Standardized appointment lookup duration.

The change to use appointmentsLookForward for the 48hr lookup mode (with an offset) standardizes the approach to time range calculations across different modes.

🧹 Nitpick comments (6)
packages/core/src/external/ehr/lambdas/elation/link-patient/elation-link-patient-local.ts (1)

3-3: Consistent import path update for linkPatient.
The import has been updated to reflect the new lambdas directory structure. Ensure this relative path remains valid and consider using a path alias (e.g., @metriport/api) to prevent brittle deep-relative imports when refactoring folder layouts.

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

13-15: Incorrect HTTP method in documentation

The JSDoc comment specifies a POST endpoint, but the implementation uses a GET endpoint.

-/**
- * POST /internal/ehr/:ehrId/practice/:id/secrets
- *
+/**
+ * GET /internal/ehr/:ehrId/practice/:id/secrets
+ *
packages/lambdas/src/ehr-get-appointments.ts (2)

18-19: Address the TODO comment regarding Sentry wrapper

The TODO comment indicates that the Sentry wrapping should be moved to the capture.wrapHandler() function.

Consider replacing the direct Sentry.AWSLambda.wrapHandler with your own utility:

-// TODO move to capture.wrapHandler()
-export const handler = Sentry.AWSLambda.wrapHandler(async (event: SQSEvent) => {
+export const handler = capture.wrapHandler(async (event: SQSEvent) => {

27-28: Consider simplifying with destructuring assignment

You could destructure properties directly from parseBody() to avoid the intermediate variable.

-  const parsedBody = parseBody(message.body);
-  const { ehr, cxId, practiceId } = parsedBody;
+  const { ehr, cxId, practiceId, ...rest } = parseBody(message.body);
packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments-cloud.ts (1)

8-46: Fix typo in filename path.

The file is located in a directory with a misspelled name: appoinment/get-appoinemtns/ should be appointment/get-appointments/.

packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments.ts (1)

21-25: Fix typo in enum value.

The enum value contains a typo: "canvasGetAppoinemtns" should be "canvasGetAppointments".

export enum AppointmentMethods {
-  canvasGetAppointments = "canvasGetAppoinemtns",
+  canvasGetAppointments = "canvasGetAppointments",
}

Also note that the folder path in the file location contains the same typo ("get-appoinemtns" instead of "get-appointments").

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 741f3bc and 358826d.

📒 Files selected for processing (56)
  • packages/api/src/command/jwt-token.ts (1 hunks)
  • packages/api/src/external/ehr/athenahealth/command/process-patients-from-appointments.ts (1 hunks)
  • packages/api/src/external/ehr/athenahealth/shared.ts (3 hunks)
  • packages/api/src/external/ehr/canvas/command/process-patients-from-appointments.ts (3 hunks)
  • packages/api/src/external/ehr/canvas/shared.ts (2 hunks)
  • packages/api/src/external/ehr/elation/command/process-patients-from-appointments.ts (1 hunks)
  • packages/api/src/external/ehr/elation/shared.ts (3 hunks)
  • packages/api/src/external/ehr/healthie/command/process-patients-from-appointments.ts (2 hunks)
  • packages/api/src/external/ehr/healthie/shared.ts (1 hunks)
  • packages/api/src/external/ehr/shared/command/secrets/get-secrets.ts (1 hunks)
  • packages/api/src/external/ehr/shared/utils/appointment.ts (1 hunks)
  • packages/api/src/external/ehr/shared/utils/client.ts (6 hunks)
  • packages/api/src/external/ehr/shared/utils/secrets.ts (1 hunks)
  • packages/api/src/routes/ehr/canvas/patient-webhook.ts (1 hunks)
  • packages/api/src/routes/ehr/elation/appointment-webhook.ts (1 hunks)
  • packages/api/src/routes/ehr/elation/patient-webhook.ts (1 hunks)
  • packages/api/src/routes/ehr/healthie/appointment-webhook.ts (1 hunks)
  • packages/api/src/routes/ehr/healthie/patient-webhook.ts (1 hunks)
  • packages/api/src/routes/ehr/util.ts (1 hunks)
  • packages/api/src/routes/internal/ehr/index.ts (2 hunks)
  • packages/api/src/routes/internal/ehr/practice.ts (1 hunks)
  • packages/api/src/routes/internal/jwt-token/index.ts (2 hunks)
  • packages/api/src/routes/internal/jwt-token/token.ts (1 hunks)
  • packages/core/src/external/aws/lambda.ts (1 hunks)
  • packages/core/src/external/ehr/api/get-client-key-and-secret.ts (1 hunks)
  • packages/core/src/external/ehr/api/get-token-info.ts (1 hunks)
  • packages/core/src/external/ehr/athenahealth/index.ts (5 hunks)
  • packages/core/src/external/ehr/canvas/command/get-appointments.ts (1 hunks)
  • packages/core/src/external/ehr/canvas/data.ts (0 hunks)
  • packages/core/src/external/ehr/canvas/index.ts (4 hunks)
  • packages/core/src/external/ehr/elation/index.ts (3 hunks)
  • packages/core/src/external/ehr/healthie/index.ts (3 hunks)
  • packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments-cloud.ts (1 hunks)
  • packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments-factory.ts (1 hunks)
  • packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments-local.ts (1 hunks)
  • packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments.ts (1 hunks)
  • packages/core/src/external/ehr/lambdas/elation/link-patient/elation-link-patient-cloud.ts (1 hunks)
  • packages/core/src/external/ehr/lambdas/elation/link-patient/elation-link-patient-factory.ts (1 hunks)
  • packages/core/src/external/ehr/lambdas/elation/link-patient/elation-link-patient-local.ts (1 hunks)
  • packages/core/src/external/ehr/lambdas/healthie/link-patient/healthie-link-patient-cloud.ts (1 hunks)
  • packages/core/src/external/ehr/lambdas/healthie/link-patient/healthie-link-patient-factory.ts (1 hunks)
  • packages/core/src/external/ehr/lambdas/healthie/link-patient/healthie-link-patient-local.ts (1 hunks)
  • packages/core/src/external/ehr/lambdas/sync-patient/ehr-sync-patient-cloud.ts (1 hunks)
  • packages/core/src/external/ehr/lambdas/sync-patient/ehr-sync-patient-factory.ts (1 hunks)
  • packages/core/src/external/ehr/lambdas/sync-patient/ehr-sync-patient-local.ts (1 hunks)
  • packages/core/src/external/ehr/shared.ts (1 hunks)
  • packages/core/src/util/config.ts (1 hunks)
  • packages/infra/lib/api-stack.ts (3 hunks)
  • packages/infra/lib/api-stack/api-service.ts (4 hunks)
  • packages/infra/lib/ehr-nested-stack.ts (5 hunks)
  • packages/lambdas/src/ehr-get-appointments.ts (1 hunks)
  • packages/lambdas/src/ehr-sync-patient.ts (1 hunks)
  • packages/lambdas/src/elation-link-patient.ts (1 hunks)
  • packages/lambdas/src/healthie-link-patient.ts (1 hunks)
  • packages/lambdas/src/shared/ehr.ts (2 hunks)
  • packages/shared/src/domain/jwt-token.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/core/src/external/ehr/canvas/data.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/lambdas/sync-patient/ehr-sync-patient-factory.ts
  • packages/api/src/routes/ehr/util.ts
  • packages/lambdas/src/elation-link-patient.ts
  • packages/lambdas/src/ehr-sync-patient.ts
  • packages/api/src/external/ehr/shared/utils/appointment.ts
  • packages/core/src/external/ehr/lambdas/sync-patient/ehr-sync-patient-cloud.ts
  • packages/core/src/external/ehr/lambdas/elation/link-patient/elation-link-patient-factory.ts
  • packages/core/src/external/ehr/lambdas/healthie/link-patient/healthie-link-patient-local.ts
  • packages/api/src/routes/ehr/canvas/patient-webhook.ts
  • packages/api/src/routes/ehr/elation/patient-webhook.ts
  • packages/api/src/routes/ehr/elation/appointment-webhook.ts
  • packages/api/src/routes/internal/jwt-token/index.ts
  • packages/core/src/external/ehr/lambdas/sync-patient/ehr-sync-patient-local.ts
  • packages/core/src/external/ehr/lambdas/healthie/link-patient/healthie-link-patient-cloud.ts
  • packages/api/src/routes/ehr/healthie/patient-webhook.ts
  • packages/api/src/external/ehr/athenahealth/command/process-patients-from-appointments.ts
  • packages/lambdas/src/shared/ehr.ts
  • packages/api/src/routes/ehr/healthie/appointment-webhook.ts
  • packages/core/src/external/ehr/lambdas/elation/link-patient/elation-link-patient-cloud.ts
  • packages/shared/src/domain/jwt-token.ts
  • packages/infra/lib/api-stack.ts
  • packages/api/src/routes/internal/jwt-token/token.ts
  • packages/api/src/external/ehr/elation/command/process-patients-from-appointments.ts
  • packages/lambdas/src/healthie-link-patient.ts
  • packages/core/src/external/ehr/api/get-token-info.ts
  • packages/api/src/routes/internal/ehr/index.ts
  • packages/core/src/external/aws/lambda.ts
  • packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments-factory.ts
  • packages/core/src/util/config.ts
  • packages/api/src/external/ehr/healthie/shared.ts
  • packages/lambdas/src/ehr-get-appointments.ts
  • packages/infra/lib/api-stack/api-service.ts
  • packages/api/src/external/ehr/canvas/command/process-patients-from-appointments.ts
  • packages/api/src/command/jwt-token.ts
  • packages/api/src/external/ehr/healthie/command/process-patients-from-appointments.ts
  • packages/api/src/routes/internal/ehr/practice.ts
  • packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments.ts
  • packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments-local.ts
  • packages/core/src/external/ehr/canvas/command/get-appointments.ts
  • packages/api/src/external/ehr/shared/utils/secrets.ts
  • packages/api/src/external/ehr/shared/command/secrets/get-secrets.ts
  • packages/core/src/external/ehr/api/get-client-key-and-secret.ts
  • packages/core/src/external/ehr/lambdas/elation/link-patient/elation-link-patient-local.ts
  • packages/core/src/external/ehr/elation/index.ts
  • packages/core/src/external/ehr/healthie/index.ts
  • packages/core/src/external/ehr/athenahealth/index.ts
  • packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments-cloud.ts
  • packages/core/src/external/ehr/lambdas/healthie/link-patient/healthie-link-patient-factory.ts
  • packages/api/src/external/ehr/elation/shared.ts
  • packages/api/src/external/ehr/canvas/shared.ts
  • packages/core/src/external/ehr/canvas/index.ts
  • packages/api/src/external/ehr/shared/utils/client.ts
  • packages/infra/lib/ehr-nested-stack.ts
  • packages/core/src/external/ehr/shared.ts
  • packages/api/src/external/ehr/athenahealth/shared.ts
🧠 Learnings (3)
packages/lambdas/src/ehr-sync-patient.ts (1)
Learnt from: thomasyopes
PR: metriport/metriport#3427
File: packages/core/src/external/ehr/api/sync-patient.ts:16-55
Timestamp: 2025-03-11T20:42:46.516Z
Learning: In the patient synchronization architecture, the flow follows this pattern: (1) `ehr-sync-patient-cloud.ts` sends messages to an SQS queue, (2) the `ehr-sync-patient` Lambda consumes these messages, and (3) the Lambda uses the `syncPatient` function to make the API calls to process the patient data.
packages/core/src/external/ehr/lambdas/sync-patient/ehr-sync-patient-cloud.ts (1)
Learnt from: thomasyopes
PR: metriport/metriport#3427
File: packages/core/src/external/ehr/api/sync-patient.ts:16-55
Timestamp: 2025-03-11T20:42:46.516Z
Learning: In the patient synchronization architecture, the flow follows this pattern: (1) `ehr-sync-patient-cloud.ts` sends messages to an SQS queue, (2) the `ehr-sync-patient` Lambda consumes these messages, and (3) the Lambda uses the `syncPatient` function to make the API calls to process the patient data.
packages/api/src/external/ehr/athenahealth/command/process-patients-from-appointments.ts (1)
Learnt from: thomasyopes
PR: metriport/metriport#3427
File: packages/core/src/external/ehr/api/sync-patient.ts:16-55
Timestamp: 2025-03-11T20:42:46.516Z
Learning: In the patient synchronization architecture, the flow follows this pattern: (1) `ehr-sync-patient-cloud.ts` sends messages to an SQS queue, (2) the `ehr-sync-patient` Lambda consumes these messages, and (3) the Lambda uses the `syncPatient` function to make the API calls to process the patient data.
🧬 Code Graph Analysis (20)
packages/api/src/routes/ehr/util.ts (1)
packages/shared/src/index.ts (1)
  • BadRequestError (39-39)
packages/api/src/external/ehr/shared/utils/appointment.ts (1)
packages/shared/src/common/date.ts (1)
  • buildDayjs (70-72)
packages/api/src/routes/internal/jwt-token/token.ts (1)
packages/api/src/command/jwt-token.ts (1)
  • getJwtTokenByIdOrFail (66-70)
packages/core/src/external/ehr/api/get-token-info.ts (5)
packages/shared/src/domain/jwt-token.ts (1)
  • JwtTokenInfo (1-5)
packages/core/src/util/config.ts (1)
  • Config (9-225)
packages/shared/src/net/retry.ts (1)
  • executeWithNetworkRetries (108-135)
packages/core/src/external/ehr/api/api-shared.ts (1)
  • validateAndLogResponse (13-20)
packages/shared/src/index.ts (1)
  • errorToString (42-42)
packages/api/src/routes/internal/ehr/index.ts (1)
packages/api/src/routes/internal/ehr/middleware.ts (1)
  • processEhrId (6-11)
packages/api/src/external/ehr/healthie/shared.ts (3)
packages/api/src/shared/config.ts (1)
  • getHealthieEnv (344-346)
packages/api/src/external/ehr/shared/utils/client.ts (2)
  • EhrPerPracticeParams (30-30)
  • EhrEnvAndApiKey (19-22)
packages/core/src/external/ehr/healthie/index.ts (1)
  • HealthieEnv (47-47)
packages/lambdas/src/ehr-get-appointments.ts (5)
packages/lambdas/src/shared/capture.ts (1)
  • capture (18-103)
packages/lambdas/src/shared/sqs.ts (1)
  • getSingleMessageOrFail (62-83)
packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments-local.ts (1)
  • EhrGetAppointmentsLocal (7-12)
packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments.ts (1)
  • GetAppointmentsRequest (6-15)
packages/lambdas/src/shared/ehr.ts (1)
  • ehrGetAppointmentsSchema (92-101)
packages/api/src/command/jwt-token.ts (2)
packages/api/src/domain/jwt-token.ts (1)
  • JwtToken (30-30)
packages/shared/src/index.ts (1)
  • NotFoundError (41-41)
packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments.ts (2)
packages/shared/src/interface/external/ehr/source.ts (1)
  • EhrSource (8-8)
packages/shared/src/index.ts (1)
  • BadRequestError (39-39)
packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments-local.ts (1)
packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments.ts (3)
  • EhrGetAppointmentsHandler (17-19)
  • GetAppointmentsRequest (6-15)
  • getEhrGetAppointmentsHandler (47-56)
packages/core/src/external/ehr/canvas/command/get-appointments.ts (6)
packages/core/src/external/ehr/canvas/index.ts (1)
  • getAppointments (860-915)
packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments.ts (1)
  • GetAppointmentsClientRequest (21-21)
packages/shared/src/index.ts (1)
  • BadRequestError (39-39)
packages/core/src/external/ehr/api/get-token-info.ts (1)
  • getTokenInfo (17-39)
packages/core/src/external/ehr/api/get-client-key-and-secret.ts (1)
  • getSecrets (17-44)
packages/core/src/external/ehr/shared.ts (2)
  • GetSecretsOauthResult (43-43)
  • getSecretsOauthSchema (39-42)
packages/api/src/external/ehr/shared/utils/secrets.ts (7)
packages/api/src/external/ehr/shared/utils/client.ts (4)
  • EhrEnv (12-12)
  • EhrPerPracticeParams (30-30)
  • EhrEnvAndClientCredentials (13-17)
  • EhrEnvAndApiKey (19-22)
packages/shared/src/interface/external/ehr/source.ts (1)
  • EhrSource (8-8)
packages/api/src/external/ehr/canvas/shared.ts (1)
  • getCanvasEnv (11-37)
packages/api/src/external/ehr/athenahealth/shared.ts (1)
  • getAthenaEnv (15-29)
packages/api/src/external/ehr/elation/shared.ts (1)
  • getElationEnv (86-106)
packages/api/src/external/ehr/healthie/shared.ts (1)
  • getHealthieEnv (84-101)
packages/shared/src/index.ts (1)
  • BadRequestError (39-39)
packages/api/src/external/ehr/shared/command/secrets/get-secrets.ts (4)
packages/core/src/external/ehr/api/get-client-key-and-secret.ts (1)
  • getSecrets (17-44)
packages/api/src/external/ehr/shared/utils/client.ts (4)
  • EhrPerPracticeParams (30-30)
  • EhrEnvAndClientCredentials (13-17)
  • EhrEnv (12-12)
  • EhrEnvAndApiKey (19-22)
packages/shared/src/interface/external/ehr/source.ts (1)
  • EhrSource (8-8)
packages/api/src/external/ehr/shared/utils/secrets.ts (1)
  • getSecretsFunction (28-34)
packages/core/src/external/ehr/api/get-client-key-and-secret.ts (4)
packages/core/src/external/ehr/api/api-shared.ts (2)
  • ApiBaseParams (5-11)
  • validateAndLogResponse (13-20)
packages/core/src/util/config.ts (1)
  • Config (9-225)
packages/shared/src/net/retry.ts (1)
  • executeWithNetworkRetries (108-135)
packages/shared/src/index.ts (1)
  • errorToString (42-42)
packages/core/src/external/ehr/elation/index.ts (1)
packages/core/src/external/ehr/shared.ts (2)
  • GetSecretsOauthFunction (50-50)
  • processOauthSecrets (53-76)
packages/core/src/external/ehr/athenahealth/index.ts (1)
packages/core/src/external/ehr/shared.ts (2)
  • GetSecretsOauthFunction (50-50)
  • processOauthSecrets (53-76)
packages/api/src/external/ehr/elation/shared.ts (2)
packages/api/src/external/ehr/shared/utils/client.ts (1)
  • EhrPerPracticeParams (30-30)
packages/core/src/external/ehr/elation/index.ts (1)
  • ElationEnv (56-56)
packages/api/src/external/ehr/shared/utils/client.ts (2)
packages/core/src/external/ehr/healthie/index.ts (1)
  • HealthieEnv (47-47)
packages/api/src/command/jwt-token.ts (1)
  • findOrCreateJwtToken (11-21)
packages/core/src/external/ehr/shared.ts (3)
packages/shared/src/interface/external/ehr/source.ts (1)
  • EhrSource (8-8)
packages/core/src/external/ehr/api/get-client-key-and-secret.ts (1)
  • getSecrets (17-44)
packages/shared/src/domain/jwt-token.ts (1)
  • JwtTokenInfo (1-5)
packages/api/src/external/ehr/athenahealth/shared.ts (2)
packages/api/src/external/ehr/shared/utils/client.ts (2)
  • EhrEnvAndClientCredentials (13-17)
  • EhrPerPracticeParams (30-30)
packages/core/src/external/ehr/athenahealth/index.ts (1)
  • AthenaEnv (95-95)
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: check-branch
  • 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 (95)
packages/core/src/external/ehr/lambdas/sync-patient/ehr-sync-patient-factory.ts (1)

1-1: Import path update looks good

The Config import path has been properly updated to reflect the new directory structure.

packages/core/src/external/ehr/lambdas/elation/link-patient/elation-link-patient-factory.ts (1)

1-1: Import path update looks good

The Config import path has been correctly updated to reflect the new directory structure.

packages/core/src/external/ehr/lambdas/sync-patient/ehr-sync-patient-cloud.ts (1)

3-4: Import paths correctly updated

Both import paths have been properly adjusted to reflect the new directory structure.

packages/api/src/routes/ehr/elation/appointment-webhook.ts (1)

1-1: Align import to new Lambda factory location.
The buildEhrSyncPatientHandler path correctly points to the lambdas subdirectory. Confirm that the factory export signature hasn't changed and that error handling remains consistent when invoking the new handler.

packages/api/src/routes/ehr/canvas/patient-webhook.ts (1)

1-1:

✅ Verification successful

Update sync handler import for Canvas.
Importing buildEhrSyncPatientHandler from the lambdas directory aligns with the Canvas refactor. Verify that this handler logic supports appointment retrieval for Canvas (e.g., buildEhrGetAppointmentsHandler elsewhere) and that no residual direct client calls remain.

Run this to locate the new appointments handler and ensure it's wired correctly:


🏁 Script executed:

#!/bin/bash
rg -g "*.ts" "buildEhrGetAppointmentsHandler" -L

Length of output: 815


Import Update Verified

  • Confirmed that buildEhrSyncPatientHandler is correctly imported from
    @metriport/core/external/ehr/lambdas/sync-patient/ehr-sync-patient-factory.
  • Located buildEhrGetAppointmentsHandler in
    packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments-factory.ts
    and saw it wired in
    packages/api/src/external/ehr/canvas/command/process-patients-from-appointments.ts.
  • No direct EHR client calls remain in
    packages/api/src/routes/ehr/canvas/patient-webhook.ts.

Everything aligns with the Canvas refactor. Approved.

packages/api/src/routes/ehr/healthie/patient-webhook.ts (1)

1-1: Consistent import path refactoring for Healthie sync handler.
The import now targets the lambdas-based sync factory. Please ensure that CI’s type-checker catches any mismatches if the factory’s API changed, and update documentation/comments if the handler signature was modified.

packages/lambdas/src/ehr-sync-patient.ts (1)

1-2:

✅ Verification successful

Adopt new lambdas module paths for local sync handler.
The ProcessSyncPatientRequest and EhrSyncPatientLocal imports have been updated to the lambdas directory. Verify that ehr-sync-patient-local.ts still exports the expected classes and run a scan to catch any lingering legacy imports:


🏁 Script executed:

#!/bin/bash
rg -g "*.ts" "@metriport/core/external/ehr/sync-patient/" --files

Length of output: 148


I suspect the initial ripgrep invocation targeted a non‐existent directory. Let’s instead scan all .ts files for the legacy import string:


🏁 Script executed:

#!/bin/bash
# Find any remaining imports from the old path
rg '@metriport/core/external/ehr/sync-patient/' -g '*.ts'

Length of output: 57


🏁 Script executed:

#!/bin/bash
# Locate the new local sync handler file and inspect its exports
fd -e ts 'ehr-sync-patient-local.ts' --exec sed -n '1,50p' {}

Length of output: 769


🏁 Script executed:

#!/bin/bash
# Inspect ehr-sync-patient.ts in both the lambdas package (under review) and the external core package
for file in $(fd -e ts 'ehr-sync-patient.ts'); do
  echo "=== $file ==="
  sed -n '1,50p' "$file"
done

Length of output: 2985


Approved: Lambdas imports updated and no legacy references found
Verified that ehr-sync-patient-local.ts still exports EhrSyncPatientLocal and ProcessSyncPatientRequest, and a repo-wide search returned no remaining imports from the old @metriport/core/external/ehr/sync-patient/ path. All looks correct.

packages/api/src/routes/ehr/elation/patient-webhook.ts (1)

1-1: Consistent import path update for lambda-based handler
The import path has been correctly updated to reference the new lambdas directory for the EHR sync-patient factory. This aligns with the overall refactor to centralize Lambda handlers.

packages/lambdas/src/elation-link-patient.ts (1)

1-2: Align imports with new lambda module structure
The ProcessLinkPatientRequest and ElationLinkPatientLocal imports now point to the lambdas subdirectory, matching the reorganization across EHR handlers.

packages/api/src/routes/ehr/util.ts (1)

15-17: Add explicit braces for clarity
Wrapping the throw statement in {} improves readability and aligns with the project’s style of always using braces around conditional blocks, even single-line ones.

packages/core/src/external/ehr/lambdas/sync-patient/ehr-sync-patient-local.ts (1)

2-2: Update relative import to reflect directory restructure
The import for syncPatient now uses the correct relative path after moving handlers under lambdas. This keeps module resolution accurate with the new folder layout.

packages/core/src/external/ehr/lambdas/elation/link-patient/elation-link-patient-cloud.ts (1)

3-4: Correct deep relative imports for AWS utilities
The SQSClient and Config imports have been updated to the deeper relative paths under external/aws and util. This matches the refactored directory structure for cloud handlers.

packages/api/src/routes/ehr/healthie/appointment-webhook.ts (1)

1-1: Import path updated to new lambdas directory
The import for buildEhrSyncPatientHandler has been adjusted to point at @metriport/core/external/ehr/lambdas/sync-patient/.... This aligns with the reorganized folder structure—please verify that all similar EHR route handlers use the updated path to prevent resolution errors.

packages/core/src/external/ehr/lambdas/healthie/link-patient/healthie-link-patient-local.ts (1)

3-3: Corrected relative import for linkPatient
The linkPatient import path has been updated to match the relocated API module under the lambdas directory. Confirm that this reference resolves correctly and remove any stale imports if present.

packages/core/src/external/ehr/lambdas/healthie/link-patient/healthie-link-patient-factory.ts (1)

1-1: Adjusted Config import path
The Config import now traverses one additional directory level to accommodate the new lambdas layout. Ensure consistency with other factory modules and that util/config remains reachable from this location.

packages/api/src/external/ehr/athenahealth/command/process-patients-from-appointments.ts (1)

2-2: Updated sync-patient factory import
The import for buildEhrSyncPatientHandler now points to the lambdas/sync-patient path under @metriport/core. This reflects the new architecture—please double-check that all AthenaHealth and other EHR modules have received the same update.

packages/shared/src/domain/jwt-token.ts (1)

4-4: Extend JwtTokenInfo type with optional id
Introducing an optional id property enables identifying JWT tokens by their unique identifier, supporting the new getJwtTokenById API and related command functions. This change is backward-compatible and aligns with the enhanced token-management flow.

packages/lambdas/src/healthie-link-patient.ts (1)

1-2: Import paths updated correctly to match relocated lambda code structure.

The import paths now properly point to the new location under the lambdas subdirectory, which aligns with the PR objective of relocating linking and syncing lambda code into a dedicated lambdas folder.

packages/core/src/external/ehr/lambdas/healthie/link-patient/healthie-link-patient-cloud.ts (1)

3-4: Import paths correctly adjusted for new file location.

The imports have been updated with proper relative paths after moving the file into the new lambdas directory structure. This maintains the correct module resolution and adheres to the project's modular architecture.

packages/api/src/external/ehr/elation/command/process-patients-from-appointments.ts (1)

1-2: Import paths properly updated to use handlers from the new lambdas structure.

The import paths now correctly reference the handlers from the reorganized lambdas subdirectory, maintaining consistent implementation with the code refactoring across the codebase.

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

215-217: New config method added for EHR appointments lambda.

The addition of getEhrGetAppointmentsLambdaName() properly supports the new lambda connector for retrieving EHR appointments, following the established pattern for config methods in this class.

packages/api/src/routes/internal/jwt-token/index.ts (2)

6-6: LGTM!

The token import and subsequent route registration in line 17 add a shared JWT token functionality to the router.


16-17: Good implementation of shared token routes

Adding the token routes at the root path is a clean way to organize shared JWT token functionality that applies across EHR types.

packages/api/src/routes/internal/ehr/index.ts (2)

8-8: LGTM!

The practice import follows the established pattern for EHR route modules.


17-17: Good implementation of practice routes

This route registration correctly uses the processEhrId middleware to validate and extract the EHR ID parameter before passing control to the practice router, following the same pattern as the existing patient routes.

packages/infra/lib/api-stack.ts (3)

424-424: LGTM!

Properly destructuring the get appointments Lambda from the EHR nested stack.


558-558: Good integration with API service

The appointments Lambda is correctly passed to the API service creation function.


663-663: Consistent environment variable configuration

This correctly adds the appointments Lambda to the list of Lambdas that need the API_URL environment variable.

packages/api/src/routes/internal/jwt-token/token.ts (1)

18-26: LGTM! Route handler implementation is clean and follows best practices.

The route handler correctly extracts the token ID from parameters with validation, retrieves the token using the appropriate command function, and returns it with the correct HTTP status code.

packages/api/src/external/ehr/healthie/shared.ts (1)

84-87: LGTM! Exporting this function supports the new Lambda architecture.

The change to export getHealthieEnv aligns with the PR objective of supporting Lambda-based appointment handling. The function provides the necessary environment and API key information for the EHR client.

packages/api/src/command/jwt-token.ts (2)

52-61: LGTM! Well-implemented token retrieval by ID function.

The getJwtTokenById function follows the established pattern in the file with proper documentation and error handling. The implementation is clean and focused.


63-70: LGTM! Fail-safe wrapper follows established pattern.

The getJwtTokenByIdOrFail function properly implements the "OrFail" pattern used throughout the codebase, with appropriate error handling and documentation.

packages/api/src/external/ehr/healthie/command/process-patients-from-appointments.ts (1)

2-3: LGTM! Updated imports align with the new Lambda folder structure.

The import paths now correctly point to the new dedicated lambdas directories, supporting the PR's objective of better code organization.

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

115-115: Good addition of the EHR appointments Lambda infrastructure!

The changes properly integrate the new ehrGetAppointmentsLambda into the API service:

  1. Added as a parameter to the createAPIService function
  2. Correctly placed in the expected order in the parameter list
  3. Lambda name included as an environment variable for the Fargate service
  4. Task role granted permission to invoke the Lambda

This follows the established pattern for other Lambda integrations in this file.

Also applies to: 161-161, 305-305, 450-450

packages/core/src/external/ehr/api/get-token-info.ts (1)

1-39: Well-implemented token retrieval with proper error handling.

This function follows best practices:

  • Uses network retries for resilience
  • Properly transforms the API response
  • Includes contextual error information
  • Uses appropriate logging

The implementation aligns with the functional programming style preferred in the codebase.

packages/lambdas/src/shared/ehr.ts (2)

1-4: Good update to Lambda import paths.

The import paths have been properly updated to reference the new location of the lambdas code, which aligns with the PR objective of relocating the linking and syncing lambda code to a dedicated lambdas folder.


92-101: Well-structured Zod schema for appointment requests.

The schema properly validates:

  • EHR source from an enumeration
  • Required string fields (environment, method, tokenId, cxId, practiceId)
  • Optional date fields with type coercion

This provides strong type safety for the appointment Lambda handler.

packages/api/src/external/ehr/shared/command/secrets/get-secrets.ts (1)

1-26: Well-implemented secrets retrieval function

This function follows good practices by providing a clear interface for retrieving EHR secrets while delegating the implementation details to source-specific functions. The return type union correctly handles both OAuth client credentials and API key scenarios.

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

23-38: LGTM - Secure implementation of secrets retrieval endpoint

The endpoint appropriately validates all parameters before proceeding, using proper error handling for invalid inputs. The response is correctly formatted with appropriate HTTP status codes.

packages/lambdas/src/ehr-get-appointments.ts (2)

12-13: Good practice placing Sentry initialization early

Initializing capture as early as possible in the file ensures all errors are properly tracked.


41-50: Good input validation with descriptive error messages

The parseBody function properly validates the message structure and content with clear error messages at each step.

packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments-local.ts (1)

7-12: Clean implementation of handler interface

The class follows a good pattern by implementing the interface and delegating to the appropriate handler based on EHR source and method. The parameter spreading keeps the code clean and maintainable.

packages/api/src/external/ehr/canvas/command/process-patients-from-appointments.ts (2)

99-102: LGTM: Good use of destructuring for client creation

The updated client creation method now returns tokenId and environment alongside the client, which allows for more flexible use of these values.


108-118: LGTM: Good refactoring to use handler-based approach

The appointment fetching logic now uses a handler built by buildEhrGetAppointmentsHandler() instead of directly calling a method on the client. This improves modularity and testability by decoupling the appointment fetching logic from the client implementation.

packages/core/src/external/ehr/api/get-client-key-and-secret.ts (2)

8-8: LGTM: Good type definition

Well-defined type that properly omits fields not needed for fetching secrets.


17-44: LGTM: Well-implemented function for retrieving secrets

This function follows good practices:

  • Uses network retries for resilience
  • Proper error handling with context
  • Strong typing with Zod schema validation
  • Clear logging

The implementation is secure and robust.

packages/core/src/external/ehr/canvas/index.ts (2)

109-109: LGTM: Good addition of dynamic secrets retrieval

Adding a callback function for dynamic secrets retrieval allows for more flexible credential management.


133-142: LGTM: Improved OAuth secrets handling

The fetchTwoLeggedAuthToken method now uses processOauthSecrets to dynamically retrieve OAuth client credentials. This approach is more secure and flexible than hardcoding credentials or storing them directly in configuration.

packages/core/src/external/ehr/canvas/command/get-appointments.ts (1)

13-26: LGTM: Good parameter validation

The function properly validates required parameters and provides detailed error context.

packages/api/src/external/ehr/shared/utils/secrets.ts (4)

1-12: Well-structured imports.

The imports are nicely organized, bringing in necessary error handling, EHR source definitions, and environment-specific secrets retrieval functions for different EHR systems. The local import of client types is appropriately placed last.


14-19: Good type definitions for different secrets functions.

The type definitions create a clear contract for functions that retrieve different types of secrets:

  • OauthSecretsFunction for OAuth client credentials
  • ApiKeySecretsFunction for API key-based authentication
  • SecretsFunction as a union type, providing a unified interface

This design enables flexibility while maintaining type safety.


21-26: Good use of a lookup table for secrets functions.

The record maps each EHR source to its corresponding secret retrieval function, making it easy to extend with additional EHR sources in the future.


28-34: Well-implemented utility function with proper error handling.

The getSecretsFunction implementation appropriately checks for the existence of a secrets function and throws a meaningful error when none is found. This ensures that callers receive clear feedback when trying to fetch secrets for an unsupported EHR.

packages/core/src/external/ehr/elation/index.ts (4)

40-44: Good import additions for secret management.

Adding GetSecretsOauthFunction and processOauthSecrets imports supports the dynamic secret retrieval mechanism being implemented.


73-73: Well-typed new class property for secret retrieval.

The getSecrets property is correctly typed as an optional function, allowing the API to work both with static configuration and dynamic secret retrieval.


81-82: Good initialization of the getSecrets property.

Initializing this.getSecrets from the config ensures the class can access the dynamic secret retrieval function provided during instantiation.


94-125: Improved token retrieval with dynamic secrets management.

The refactored fetchTwoLeggedAuthToken method now uses processOauthSecrets to obtain credentials, making the authentication process more flexible. This change:

  1. Supports retrieving credentials at runtime
  2. Maintains backward compatibility with static credentials
  3. Properly handles the case when neither is available

The code correctly constructs a partial secrets object with existing credentials (if any) and passes it along with the getSecrets function to the processor.

packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments-cloud.ts (3)

1-9: Good imports with proper error handling utilities.

The imports bring in network retry functionality, configuration management, and Lambda client utilities needed for cloud-based appointment retrieval.


11-20: Well-designed class with proper dependency injection.

The EhrGetAppointmentsCloud class:

  1. Implements the required handler interface
  2. Accepts the Lambda function name as a required parameter
  3. Supports optional region and Lambda client parameters
  4. Initializes the Lambda client appropriately when not provided

This design follows good dependency injection principles, making the class more testable.


22-45: Robust implementation of the getAppointments method.

The getAppointments method:

  1. Logs operation details
  2. Properly serializes the request parameters
  3. Uses network retries for resilience
  4. Handles error cases in the Lambda response
  5. Returns an empty array as a fallback
  6. Correctly parses and returns the result

The implementation is resilient to various failure modes while maintaining type safety.

packages/core/src/external/ehr/athenahealth/index.ts (4)

75-79: Good import additions for secret management.

Adding GetSecretsOauthFunction and processOauthSecrets imports prepares the class for dynamic secret retrieval.


151-151: Well-typed new class property for secret retrieval.

The getSecrets property is correctly typed as an optional function, allowing the API to work with both static and dynamic credential retrieval.


160-161: Good initialization of the getSecrets property.

Initializing this.getSecrets from the config ensures the class can access the dynamic secret retrieval function provided during instantiation.


173-207: Improved token retrieval with dynamic secrets management.

The refactored fetchTwoLeggedAuthToken method now uses processOauthSecrets to obtain credentials dynamically. This change:

  1. Supports retrieving credentials at runtime
  2. Maintains backward compatibility with static credentials
  3. Properly handles credential absence
  4. Uses the retrieved credentials correctly in the auth header

This implementation ensures secure and flexible credential management for AthenaHealth API authentication.

packages/api/src/external/ehr/elation/shared.ts (3)

86-106: Export of environment retrieval function is a good enhancement

This is a positive change that makes the getElationEnv function accessible to other modules, which is necessary for the new Lambda-based appointment retrieval architecture being implemented in this PR.


124-133: Function return type enhancement improves flexibility

Good refactoring to return additional metadata (tokenId and environment) alongside the client. This enriched return value supports the new appointment fetching Lambda functionality while maintaining the core functionality.


135-140: Backward compatibility wrapper preserves API contract

The new function preserves the original interface by delegating to the enriched function and returning only the client. This follows good API design principles by minimizing impact on existing code while enabling new functionality.

packages/api/src/external/ehr/canvas/shared.ts (3)

3-3: Improved token source management through centralization

Importing canvasClientSource from a shared module rather than defining constants locally improves maintainability by centralizing token source definitions.


39-48: Enhanced client creation function with metadata

The enriched return type now includes tokenId and environment alongside the Canvas client, enabling more flexible API interactions for the new appointment lambda architecture.


50-55: Backward compatibility is correctly maintained

This function preserves the original interface by extracting just the client part from the enriched function, minimizing impact on existing code while supporting new appointment fetching functionality.

packages/api/src/external/ehr/athenahealth/shared.ts (3)

15-29: Export of environment getter improves module accessibility

Making getAthenaEnv exported allows other modules to access environment configuration, aligning with similar changes in other EHR modules and supporting the new architecture.


31-40: Enhanced client creation with metadata supports Lambda architecture

The function now returns additional context (tokenId and environment) alongside the client, following the same pattern used in other EHR integrations to support the new appointment fetching infrastructure.


42-47: Backward compatibility function maintains API contract

This wrapper function maintains the original API contract by delegating to the new function and returning only the client, which is a good practice for maintaining compatibility with existing code.

packages/infra/lib/ehr-nested-stack.ts (5)

22-22: Well-defined type for Lambda settings

Good refactoring to create a specific type for Lambda settings using TypeScript's Pick utility type, making the code more maintainable and type-safe.


32-40: Appropriate Lambda configuration for appointment retrieval

The configuration for the new appointment retrieval Lambda function is well-defined with a 12-minute timeout and 4GB memory allocation, which should be sufficient for the expected workload.


171-171: Clean addition of Lambda property to stack class

Adding a readonly property for the new Lambda function to the EhrNestedStack class follows good object-oriented design principles by clearly exposing the resource to clients of the class.


189-195: Proper Lambda initialization in constructor

The initialization call in the constructor follows the established pattern in this class, maintaining consistency with the rest of the codebase.


266-292: Well-structured Lambda setup method

The implementation follows the same pattern as other Lambda setup methods in the class, with appropriate configuration for environment variables, layers, VPC settings, and alarms. Unlike other Lambdas in this stack, this one doesn't create an SQS queue or event source, which is appropriate for its intended use case.

packages/api/src/external/ehr/shared/utils/client.ts (6)

4-4: Import from Healthie module looks good.

This import is necessary to add support for the Healthie EHR environment type.


12-12: EHR environment type correctly extended.

Adding HealthieEnv to the EhrEnv union type properly extends the system to support the new Healthie EHR source.


24-24: Type rename improves clarity.

Renaming from EhrClient to EhrOauthClient more accurately reflects that these are OAuth-based clients, which is appropriate since the Healthie client uses API key authentication instead of OAuth.


46-47: Token ID added to support Lambda functions.

Adding the token ID to the returned information provides necessary context for the new Lambda-based architecture where tokens need to be referenced by ID.


69-69: Enhanced return type provides better context.

The expanded return type including tokenId and environment provides richer information to consumers of this function, which is needed for the Lambda-based appointment retrieval system.


82-88: Token persistence and return structure look good.

Using findOrCreateJwtToken appropriately persists the token information. The return structure provides all necessary data for downstream consumers.

Note: The environment.environment access pattern suggests a nested environment property. While this works, it could be simplified in a future refactoring if appropriate.

packages/core/src/external/ehr/lambdas/appoinment/get-appoinemtns/ehr-get-appointments.ts (3)

1-19: Well-structured imports and type definitions.

The imports and type definitions provide a clear foundation for the appointment retrieval system. The GetAppointmentsRequest includes all necessary fields, and the EhrGetAppointmentsHandler interface defines a clean contract with generic typing.


27-45: Extensible mapping structure for EHR appointment handlers.

The map structure provides a clear framework for associating EHR sources and methods with their handler implementations. This architecture allows for easy addition of new sources and methods in the future.


47-56: Well-implemented handler retrieval function.

The getEhrGetAppointmentsHandler function provides a type-safe way to retrieve the appropriate handler for a given EHR source and method. The error handling is appropriate, providing context about the requested handler in the error message.

packages/core/src/external/ehr/healthie/index.ts (4)

27-33: Good utility imports to support dynamic secret retrieval.

The updated imports bring in necessary utilities for implementing the dynamic secret retrieval pattern, consistent with other EHR implementations.


37-41: Interface properly updated for dynamic secrets.

The HealthieApiConfig interface is correctly extended to support optional API key with dynamic retrieval via a getSecrets function. This aligns with the PR objective of retrieving secrets per practice to support Lambda operations.


56-56: Class correctly stores secret retrieval function.

Adding the getSecrets field and initializing it in the constructor provides the necessary state for dynamic secret retrieval.

Also applies to: 64-64


74-86: Robust initialization with dynamic secret retrieval.

The updated initialize method correctly implements the dynamic secret retrieval pattern:

  1. First checks if API key is directly provided
  2. If not, verifies a getSecrets function exists
  3. Throws a clear error if neither is available
  4. Otherwise, uses getSecrets to dynamically retrieve the API key

This implementation is robust and provides clear error messaging.

packages/core/src/external/ehr/shared.ts (4)

39-48: Well-defined schemas for secret validation.

Using Zod schemas for OAuth and API key secrets provides runtime validation and type safety, ensuring that secret data structures are consistent and complete throughout the system.


50-51: Clear function types for secret retrieval.

These function types establish a consistent contract for secret retrieval functions, making the system more maintainable and extensible.


53-76: Robust OAuth secret processing function.

The processOauthSecrets function implements a robust pattern for handling OAuth credentials:

  1. Use provided credentials if available
  2. Fall back to dynamic retrieval if a getSecrets function exists
  3. Provide clear error messages when requirements aren't met

This centralizes the logic and reduces duplication across different EHR client implementations.


81-83: Interface properly updated for dynamic secret retrieval.

Making clientKey and clientSecret optional and adding the getSecrets function to ApiConfig reflects the new pattern for credential management, supporting both direct provision and dynamic retrieval.

Thomas Yopes added 2 commits May 20, 2025 12:49
Ref: ENG-199

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

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: 3

🧹 Nitpick comments (3)
packages/core/src/external/ehr/lambdas/appointment/get-appointments/ehr-get-appointments-local.ts (1)

7-12: Implementation looks good, but consider optimization for future scalability

The EhrGetAppointmentsLocal class follows a good implementation pattern for the interface. It correctly destructures the request and delegates to the appropriate handler.

For future scalability, consider adding error logging specifically for this local implementation. When more EHR sources are added, having detailed logs here will help with troubleshooting:

  async getAppointments<T>({ ehr, method, ...params }: GetAppointmentsRequest): Promise<T[]> {
+   const { log } = out(`EhrGetAppointmentsLocal - ehr ${ehr}, method ${method}`);
    const handler = getEhrGetAppointmentsHandler<T>(ehr, method);
+   log(`Handler found, executing with params`);
    return await handler({ ...params });
  }
packages/core/src/external/ehr/lambdas/appointment/get-appointments/ehr-get-appointments.ts (2)

27-36: Consider a more maintainable type definition approach

The current GetAppointmentsMap type definition will require manual updates each time a new EHR source is added.

Consider using a more dynamic type generation approach:

- export type GetAppointmentsMap = {
-   [EhrSources.athena]: undefined;
-   [EhrSources.elation]: undefined;
-   [EhrSources.healthie]: undefined;
-   [EhrSources.canvas]: {
-     [AppointmentMethods.canvasGetAppointments]: (
-       params: GetAppointmentsClientRequest
-     ) => Promise<SlimBookedAppointment[]>;
-   };
- };
+ export type EhrMethodHandler = (params: GetAppointmentsClientRequest) => Promise<unknown[]>;
+ 
+ export type EhrMethodsMap = {
+   [method in AppointmentMethods]?: EhrMethodHandler;
+ };
+ 
+ export type GetAppointmentsMap = {
+   [source in EhrSource]?: EhrMethodsMap;
+ };

This approach will be more scalable as you add more EHR sources and methods in the future, without requiring repetitive type definitions.


47-56: Handler lookup function is well-implemented with proper error handling

The getEhrGetAppointmentsHandler function correctly handles the lookup and error case.

Consider adding a type guard to improve the explicitness of the type casting:

export function getEhrGetAppointmentsHandler<T>(
  ehr: EhrSource,
  method: AppointmentMethods
): (params: GetAppointmentsClientRequest) => Promise<T[]> {
  const handler = ehrGetAppointmentsMap[ehr]?.[method];
  if (!handler) {
    throw new BadRequestError(`No get appointments handler found`, undefined, { ehr, method });
  }
-  return handler as (params: GetAppointmentsClientRequest) => Promise<T[]>;
+  return ((params: GetAppointmentsClientRequest) => {
+    return handler(params) as Promise<T[]>;
+  });
}

This makes the type conversion more explicit and controlled, though the current implementation is also valid.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 358826d and 83d31e8.

📒 Files selected for processing (10)
  • packages/api/src/external/ehr/canvas/command/process-patients-from-appointments.ts (3 hunks)
  • packages/api/src/routes/internal/jwt-token/token.ts (1 hunks)
  • packages/core/src/external/aws/lambda.ts (1 hunks)
  • packages/core/src/external/ehr/canvas/command/get-appointments.ts (1 hunks)
  • packages/core/src/external/ehr/lambdas/appointment/get-appointments/ehr-get-appointments-cloud.ts (1 hunks)
  • packages/core/src/external/ehr/lambdas/appointment/get-appointments/ehr-get-appointments-factory.ts (1 hunks)
  • packages/core/src/external/ehr/lambdas/appointment/get-appointments/ehr-get-appointments-local.ts (1 hunks)
  • packages/core/src/external/ehr/lambdas/appointment/get-appointments/ehr-get-appointments.ts (1 hunks)
  • packages/lambdas/src/ehr-get-appointments.ts (1 hunks)
  • packages/lambdas/src/shared/ehr.ts (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/core/src/external/ehr/lambdas/appointment/get-appointments/ehr-get-appointments-factory.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/api/src/external/ehr/canvas/command/process-patients-from-appointments.ts
  • packages/core/src/external/aws/lambda.ts
  • packages/api/src/routes/internal/jwt-token/token.ts
  • packages/lambdas/src/shared/ehr.ts
  • packages/core/src/external/ehr/canvas/command/get-appointments.ts
  • packages/lambdas/src/ehr-get-appointments.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/lambdas/appointment/get-appointments/ehr-get-appointments-cloud.ts
  • packages/core/src/external/ehr/lambdas/appointment/get-appointments/ehr-get-appointments-local.ts
  • packages/core/src/external/ehr/lambdas/appointment/get-appointments/ehr-get-appointments.ts
🧬 Code Graph Analysis (2)
packages/core/src/external/ehr/lambdas/appointment/get-appointments/ehr-get-appointments-cloud.ts (3)
packages/core/src/external/ehr/lambdas/appointment/get-appointments/ehr-get-appointments.ts (2)
  • EhrGetAppointmentsHandler (17-19)
  • GetAppointmentsRequest (6-15)
packages/core/src/external/aws/lambda.ts (3)
  • LambdaClient (7-7)
  • makeLambdaClient (13-19)
  • defaultLambdaInvocationResponseHandler (26-35)
packages/shared/src/net/retry.ts (1)
  • executeWithNetworkRetries (108-135)
packages/core/src/external/ehr/lambdas/appointment/get-appointments/ehr-get-appointments.ts (3)
packages/shared/src/interface/external/ehr/source.ts (1)
  • EhrSource (8-8)
packages/lambdas/src/ehr-get-appointments.ts (1)
  • handler (19-39)
packages/shared/src/index.ts (1)
  • BadRequestError (39-39)
🔇 Additional comments (2)
packages/core/src/external/ehr/lambdas/appointment/get-appointments/ehr-get-appointments-cloud.ts (1)

11-20: Constructor implementation follows best practices

The constructor correctly initializes the Lambda client with either the provided client or creates a new one with appropriate region configuration.

packages/core/src/external/ehr/lambdas/appointment/get-appointments/ehr-get-appointments.ts (1)

6-21: Type definitions look good with clear interface contract

The request type definitions and handler interface are well-structured with appropriate typing.

Thomas Yopes added 2 commits May 20, 2025 13:06
Ref: ENG-199

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

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: 2

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a7390c1 and a64ac52.

📒 Files selected for processing (2)
  • packages/lambdas/src/ehr/get-appointments.ts (1 hunks)
  • packages/lambdas/src/shared/ehr.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/lambdas/src/shared/ehr.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/lambdas/src/ehr/get-appointments.ts
🧠 Learnings (1)
packages/lambdas/src/ehr/get-appointments.ts (1)
Learnt from: thomasyopes
PR: metriport/metriport#3873
File: packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments-cloud.ts:40-43
Timestamp: 2025-05-29T16:53:26.064Z
Learning: The EHR get-appointments Lambda function is designed to always return parseable JSON responses, so additional error handling for JSON.parse() is not required when processing its results.
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: location_services_cdk_stack / deploy
  • GitHub Check: ihe_stack / deploy
  • GitHub Check: infra-hl7-notification / deploy
  • GitHub Check: secrets_cdk_stack / deploy
  • GitHub Check: infra-api-lambdas / deploy
  • GitHub Check: mllp-server / deploy
  • GitHub Check: api / deploy
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (5)
packages/lambdas/src/ehr/get-appointments.ts (5)

1-6: LGTM!

The imports are well-organized and follow the coding guidelines. Good use of core types and shared utilities.


7-8: LGTM!

Good practice to initialize capture early for proper error handling and telemetry throughout the Lambda execution.


10-11: LGTM!

Correctly uses getEnvOrFail for required environment variables. The comment helpfully explains that AWS automatically sets this variable.


13-16: LGTM!

Good use of TypeScript utility types to create a Lambda-specific variant where dates are strings. This handles the common Lambda pattern where dates come serialized as strings.


20-34: LGTM!

The handler logic is well-structured with proper logging, timing, and error handling. Good use of destructuring for parameter extraction and meaningful variable names.

Thomas Yopes added 2 commits June 5, 2025 06:44
Ref: ENG-199

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

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
@thomasyopes thomasyopes added this pull request to the merge queue Jun 5, 2025
@thomasyopes thomasyopes removed this pull request from the merge queue due to a manual request Jun 5, 2025
@thomasyopes thomasyopes added this pull request to the merge queue Jun 5, 2025
Merged via the queue into develop with commit 498081a Jun 5, 2025
32 checks passed
@thomasyopes thomasyopes deleted the 199-elation-contribution-1.5 branch June 5, 2025 15:59
@coderabbitai coderabbitai bot mentioned this pull request Jul 26, 2025
5 tasks
@coderabbitai coderabbitai bot mentioned this pull request Aug 26, 2025
19 tasks
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.

2 participants