8000 feat(ehr): elation contribution by thomasyopes · Pull Request #4082 · metriport/metriport · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat(ehr): elation contribution #4082

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 42 commits into from
Jun 25, 2025
Merged

Conversation

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

Ref: ENG-139

Issues:

Dependencies

Description

  • adding getBundleByResourceType and getBundleByResourceTypes methods for elation
  • hooking elation into the create resource diff flow

Testing

  • Local
    • create resource diff flow runs correctly for elation
  • Staging
    • create resource diff flow runs correctly for elation
  • Sandbox
    • N/A
  • Production
    • create resource diff flow runs correctly for elation

Release Plan

  • Upstream dependencies are met/released
  • Merge this

Summary by CodeRabbit

  • New Features

    • Added comprehensive support for Elation EHR, including FHIR resource bundle retrieval, CCDA document handling, and caching.
    • Introduced new FHIR conversion workflows supporting both cloud-based (AWS Lambda) and direct server conversion methods.
    • Enabled generation and storage of CCDA files with utilities for constructing structured S3 paths and keys.
    • Added new API handlers for retrieving bundles and resources by type and ID from Elation.
    • Added utilities for partitioning and saving intermediate FHIR conversion payloads and results.
    • Included a new function to create batch FHIR bundles from resource arrays.
  • Improvements

    • Enhanced deduplication of FHIR bundles during CCD generation.
    • Improved configuration management with new environment variable accessors.
    • Expanded support for resource diff bundles to include Elation EHR.
    • Updated error messages for better context in reference bundle saving.
    • Upgraded Lambda permissions and environment variables to support FHIR conversion integration.
  • Chores

    • Removed deprecated internal API route for setting resource diff entry status.
    • Refactored infrastructure stacks to include FHIR converter Lambda and bucket references.

Ref: ENG-139

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Copy link
linear bot commented Jun 23, 2025

Copy link
coderabbitai bot commented Jun 23, 2025

Walkthrough

This change introduces comprehensive support for integrating the Elation EHR source into FHIR and CCDA workflows. It adds new handler functions, resource type mappings, and utility methods for CCDA and FHIR bundle management. The update also implements a flexible FHIR conversion handler system with cloud and direct modes, infrastructure enhancements for Lambda and S3 integration, and new utilities for file key/path generation and S3 operations.

Changes

File(s) / Path(s) Change Summary
packages/core/src/external/ehr/elation/index.ts, elation/command/*.ts, elation/ccda.ts Adds Elation EHR support: resource type guards, CCDA document handling, FHIR bundle retrieval, and schema definitions.
packages/core/src/external/ehr/command/get-bundle-by-resource-type.ts, get-resource-bundle-by-resource-id.ts Registers Elation-specific handlers for resource/bundle retrieval in handler maps.
packages/core/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/start-jobs-across-ehrs.ts, job/create-resource-diff-bundles/shared.ts Enables Elation in resource diff bundle creation and job startup logic.
packages/core/src/external/ehr/bundle/bundle-shared.ts Adds CCDA-specific prefix/key utilities, extends supported resource logic for Elation.
packages/core/src/external/ehr/bundle/command/create-or-replace-ccda.ts Adds function to store CCDA documents in S3 with error handling and retry.
packages/core/src/external/ehr/shared.ts Improves error/context reporting for reference bundle saving.
packages/core/src/command/conversion-fhir/conversion-fhir.ts, conversion-fhir-cloud.ts, conversion-fhir-direct.ts, conversion-fhir-factory.ts, utils.ts Introduces abstract FHIR conversion handler, cloud/direct implementations, factory, and S3 utility functions for conversion.
packages/core/src/domain/conversion/filename.ts Adds utilities for building conversion FHIR file paths and keys.
packages/core/src/util/config.ts Adds config accessors for FHIR converter Lambda, server URL, and bucket.
packages/api/src/external/cda/generate-ccd.ts Invokes deduplication on FHIR bundles after construction.
packages/api/src/routes/internal/ehr/patient.ts Removes deprecated resource diff entry status route and related imports.
packages/core/src/external/ehr/canvas/index.ts Updates import paths for FHIR resource bundle types.
packages/infra/lib/api-stack.ts, ehr-nested-stack.ts Passes FHIR converter Lambda/bucket to EHR stack, updates stack props, environment, and permissions for Lambda/S3 integration.
packages/shared/src/interface/external/ehr/elation/ccda.ts, index.ts Adds CCDA schema/type and re-exports for Elation interface.

Sequence Diagram(s)

sequenceDiagram
    participant API
    participant ElationApi
    participant S3
    participant FHIRConverter

    API->>ElationApi: getBundleByResourceType(params)
    ElationApi->>ElationApi: getCcdaDocument()
    ElationApi->>S3: Upload CCDA payload
    ElationApi->>FHIRConverter: callConverter({payload, params})
    FHIRConverter-->>ElationApi: FHIR Bundle
    ElationApi->>API: Return validated FHIR Bundle
Loading
sequenceDiagram
    participant HandlerFactory
    participant ConversionFhirDirect
    participant ConversionFhirCloud
    participant Lambda
    participant FHIRServer

    HandlerFactory->>ConversionFhirDirect: if dev, instantiate
    HandlerFactory->>ConversionFhirCloud: else, instantiate

    ConversionFhirCloud->>Lambda: Invoke Lambda with payload
    Lambda-->>ConversionFhirCloud: Return FHIR Bundle

    ConversionFhirDirect->>FHIRServer: POST /api/convert/cda/ccd.hbs
    FHIRServer-->>ConversionFhirDirect: Return FHIR Bundle
Loading

Possibly related PRs

  • metriport/metriport#3712: Adds a medical documents bucket to EhrNestedStack and adjusts Lambda timeouts, related by modifying the same infrastructure stack and environment variables for resource integration.

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: C0BCC00B1E7F0000: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-25T01_05_14_617Z-debug-0.log

✨ 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 result 8000 s, 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 7 commits June 23, 2025 10:52
Ref: ENG-139

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

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

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

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

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

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

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
@thomasyopes thomasyopes marked this pull request as ready for review June 23, 2025 23:32
Ref: ENG-139

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

🔭 Outside diff range comments (1)
packages/shared/src/domain/converison-fhir.ts (1)

1-19: Fix filename typo

The filename has a typo: converison-fhir.ts should be conversion-fhir.ts. This typo will affect imports in other files and should be corrected.

The enums and constant definitions are well-structured and follow the coding guidelines appropriately.

🧹 Nitpick comments (1)
packages/core/src/external/ehr/elation/command/get-resource-bundle-by-resource-id.ts (1)

19-21: Improve error message for better user experience

The error message could be more descriptive to help users understand why this operation is not supported and what alternatives they have.

-    throw new BadRequestError("useCachedBundle false is not supported");
+    throw new BadRequestError("Non-cached bundle retrieval is not supported for Elation. Please use cached bundles only.");
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1cedab9 and 85a1552.

📒 Files selected for processing (19)
  • packages/api/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/start-jobs-across-ehrs.ts (1 hunks)
  • packages/core/src/command/conversion-fhir/conversion-fhir-cloud.ts (1 hunks)
  • packages/core/src/command/conversion-fhir/conversion-fhir-direct.ts (1 hunks)
  • packages/core/src/command/conversion-fhir/conversion-fhir-factory.ts (1 hunks)
  • packages/core/src/command/conversion-fhir/conversion-fhir.ts (1 hunks)
  • packages/core/src/command/conversion-fhir/shared.ts (1 hunks)
  • packages/core/src/external/ehr/bundle/bundle-shared.ts (2 hunks)
  • packages/core/src/external/ehr/canvas/index.ts (1 hunks)
  • packages/core/src/external/ehr/command/get-bundle-by-resource-type.ts (2 hunks)
  • packages/core/src/external/ehr/command/get-resource-bundle-by-resource-id.ts (2 hunks)
  • packages/core/src/external/ehr/elation/command/get-bundle-by-resource-type.ts (1 hunks)
  • packages/core/src/external/ehr/elation/command/get-resource-bundle-by-resource-id.ts (1 hunks)
  • packages/core/src/external/ehr/elation/index.ts (6 hunks)
  • packages/core/src/external/ehr/job/create-resource-diff-bundles/shared.ts (1 hunks)
  • packages/core/src/external/ehr/shared.ts (4 hunks)
  • packages/core/src/util/config.ts (1 hunks)
  • packages/shared/src/domain/converison-fhir.ts (1 hunks)
  • packages/shared/src/interface/external/ehr/elation/ccda.ts (1 hunks)
  • packages/shared/src/interface/external/ehr/elation/index.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
`**/*`: Use eslint to enforce code style Use prettier to format code Max column length is 100 chars Multi-line comments use /** */ Top-level comments go after the import (save pre-...

**/*: Use eslint to enforce code style
Use prettier to format code
Max column length is 100 chars
Multi-line comments use /** */
Top-level comments go after the import (save pre-import to basic file header, like license)
Move literals to constants declared after imports when possible
File names: kebab-case

  • packages/shared/src/interface/external/ehr/elation/index.ts
  • packages/core/src/external/ehr/command/get-resource-bundle-by-resource-id.ts
  • packages/core/src/external/ehr/bundle/bundle-shared.ts
  • packages/core/src/external/ehr/command/get-bundle-by-resource-type.ts
  • packages/core/src/external/ehr/job/create-resource-diff-bundles/shared.ts
  • packages/core/src/util/config.ts
  • packages/core/src/external/ehr/elation/command/get-resource-bundle-by-resource-id.ts
  • packages/core/src/command/conversion-fhir/conversion-fhir-factory.ts
  • packages/core/src/external/ehr/elation/command/get-bundle-by-resource-type.ts
  • packages/shared/src/interface/external/ehr/elation/ccda.ts
  • packages/shared/src/domain/converison-fhir.ts
  • packages/core/src/command/conversion-fhir/conversion-fhir.ts
  • packages/core/src/command/conversion-fhir/conversion-fhir-cloud.ts
  • packages/core/src/command/conversion-fhir/conversion-fhir-direct.ts
  • packages/api/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/start-jobs-across-ehrs.ts
  • packages/core/src/external/ehr/canvas/index.ts
  • packages/core/src/command/conversion-fhir/shared.ts
  • packages/core/src/external/ehr/elation/index.ts
  • packages/core/src/external/ehr/shared.ts
`**/*.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/shared/src/interface/external/ehr/elation/index.ts
  • packages/core/src/external/ehr/command/get-resource-bundle-by-resource-id.ts
  • packages/core/src/external/ehr/bundle/bundle-shared.ts
  • packages/core/src/external/ehr/command/get-bundle-by-resource-type.ts
  • packages/core/src/external/ehr/job/create-resource-diff-bundles/shared.ts
  • packages/core/src/util/config.ts
  • packages/core/src/external/ehr/elation/command/get-resource-bundle-by-resource-id.ts
  • packages/core/src/command/conversion-fhir/conversion-fhir-factory.ts
  • packages/core/src/external/ehr/elation/command/get-bundle-by-resource-type.ts
  • packages/shared/src/interface/external/ehr/elation/ccda.ts
  • packages/shared/src/domain/converison-fhir.ts
  • packages/core/src/command/conversion-fhir/conversion-fhir.ts
  • packages/core/src/command/conversion-fhir/conversion-fhir-cloud.ts
  • packages/core/src/command/conversion-fhir/conversion-fhir-direct.ts
  • packages/api/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/start-jobs-across-ehrs.ts
  • packages/core/src/external/ehr/canvas/index.ts
  • packages/core/src/command/conversion-fhir/shared.ts
  • packages/core/src/external/ehr/elation/index.ts
  • packages/core/src/external/ehr/shared.ts
🧠 Learnings (4)
packages/core/src/external/ehr/command/get-bundle-by-resource-type.ts (1)
Learnt from: thomasyopes
PR: metriport/metriport#3608
File: packages/core/src/external/ehr/canvas/index.ts:451-469
Timestamp: 2025-04-21T17:07:30.574Z
Learning: The `getMetriportOnlyBundleByResourceType` method in CanvasApi returns `Promise<Bundle | undefined>` because the Metriport-only bundle is a computed artifact that may not exist yet. In contrast, `getBundleByResourceType` returns `Promise<Bundle>` because it can always fetch from the EHR API if the S3 cached bundle doesn't exist.
packages/core/src/external/ehr/elation/command/get-resource-bundle-by-resource-id.ts (1)
Learnt from: thomasyopes
PR: metriport/metriport#3608
File: packages/core/src/external/ehr/canvas/index.ts:451-469
Timestamp: 2025-04-21T17:07:30.574Z
Learning: The `getMetriportOnlyBundleByResourceType` method in CanvasApi returns `Promise<Bundle | undefined>` because the Metriport-only bundle is a computed artifact that may not exist yet. In contrast, `getBundleByResourceType` returns `Promise<Bundle>` because it can always fetch from the EHR API if the S3 cached bundle doesn't exist.
packages/core/src/external/ehr/elation/command/get-bundle-by-resource-type.ts (1)
Learnt from: thomasyopes
PR: metriport/metriport#3608
File: packages/core/src/external/ehr/canvas/index.ts:451-469
Timestamp: 2025-04-21T17:07:30.574Z
Learning: The `getMetriportOnlyBundleByResourceType` method in CanvasApi returns `Promise<Bundle | undefined>` because the Metriport-only bundle is a computed artifact that may not exist yet. In contrast, `getBundleByResourceType` returns `Promise<Bundle>` because it can always fetch from the EHR API if the S3 cached bundle doesn't exist.
packages/core/src/external/ehr/elation/index.ts (1)
Learnt from: thomasyopes
PR: metriport/metriport#3608
File: packages/core/src/external/ehr/canvas/index.ts:451-469
Timestamp: 2025-04-21T17:07:30.574Z
Learning: The `getMetriportOnlyBundleByResourceType` method in CanvasApi returns `Promise<Bundle | undefined>` because the Metriport-only bundle is a computed artifact that may not exist yet. In contrast, `getBundleByResourceType` returns `Promise<Bundle>` because it can always fetch from the EHR API if the S3 cached bundle doesn't exist.
🔇 Additional comments (17)
packages/core/src/util/config.ts (1)

318-325: LGTM! Clean configuration method additions.

The new methods follow the established patterns in the Config class, use appropriate error handling with getEnvVarOrFail(), and maintain consistent naming conventions.

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

36-39: LGTM! Improved import specificity.

The import path refinement from @metriport/shared to @metriport/shared/interface/external/ehr/fhir-resource enhances modularity and aligns with stricter type definitions.

packages/shared/src/interface/external/ehr/elation/index.ts (1)

8-8: LGTM! Consistent export pattern.

The CCDA module export follows the established pattern in the file and properly extends the public interface.

packages/core/src/external/ehr/bundle/bundle-shared.ts (2)

6-6: LGTM! Consistent EHR module import.

The import follows the established pattern for other EHR sources and maintains consistency across the codebase.


64-64: LGTM! Proper EHR source integration.

The Elation conditional branch follows the exact same pattern as Canvas and Athena implementations, ensuring consistent behavior across all EHR sources.

packages/core/src/external/ehr/command/get-resource-bundle-by-resource-id.ts (2)

6-6: LGTM! Consistent handler import pattern.

The import follows the established naming convention and pattern used for other EHR source handlers.


45-45: LGTM! Proper handler registration.

The Elation handler assignment replaces the previous undefined value and follows the same pattern as Canvas and Athena implementations, enabling resource bundle retrieval for Elation.

packages/core/src/external/ehr/command/get-bundle-by-resource-type.ts (2)

6-6: LGTM: Import follows established pattern.

The import statement correctly follows the same naming convention as existing EHR sources (Canvas and Athena).


36-36: LGTM: Consistent EHR source mapping.

The addition of Elation to the ehrGetBundleByResourceTypeMap correctly follows the established pattern and maintains consistency with other EHR sources.

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

48-54: LGTM: Consistent job orchestration for Elation.

The implementation correctly follows the established pattern for Canvas (without practiceId) and includes proper error handling with processAsyncError. The conditional branch maintains consistency with existing EHR source handling.

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

19-23: LGTM: Proper addition to supported EHR sources.

The addition of EhrSources.elation to the array is consistent with the existing pattern and maintains proper type safety with the const assertion.

packages/shared/src/interface/external/ehr/elation/ccda.ts (1)

1-7: LGTM: Well-structured Zod schema for CCDA validation.

The schema definition follows best practices:

  • z.coerce.string() for the id field appropriately handles potential numeric inputs
  • Clear naming convention for the base64_ccda field
  • Proper type inference with TypeScript
  • Consistent with Zod validation patterns
packages/core/src/command/conversion-fhir/conversion-fhir-factory.ts (1)

6-13: LGTM: Clean factory pattern implementation.

The factory function follows good design principles:

  • Environment-based handler selection is appropriate
  • Clean separation between development and production configurations
  • Proper use of configuration methods
  • Follows TypeScript and coding guidelines correctly
packages/core/src/external/ehr/elation/command/get-bundle-by-resource-type.ts (1)

6-39: Consider adding error handling for external service calls

The function lacks error handling for potential failures from the Elation client or FHIR conversion handler. Consider wrapping the external calls in try-catch blocks to provide more meaningful error messages to callers.

 export async function getBundleByResourceType(
   params: GetBundleByResourceTypeClientRequest
 ): Promise<Bundle> {
+  try {
     const {
       tokenId,
       cxId,
       practiceId,
       metriportPatientId,
       ehrPatientId,
       resourceType,
       useCachedBundle,
     } = params;
     const client = await createElationHealthClient({
       cxId,
       practiceId,
       ...(tokenId && { tokenId }),
     });
     const handler = buildConversionFhirHandler();
     const bundle = await client.getBundleByResourceType({
       cxId,
       metriportPatientId,
       elationPatientId: ehrPatientId,
       resourceType,
       fhirConverter: async (payload: string) => {
         return await handler.convertToFhir({
           cxId,
           patientId: metriportPatientId,
           rawData: payload,
         });
       },
       useCachedBundle,
     });
     return bundle;
+  } catch (error) {
+    throw new Error(`Failed to get bundle by resource type for Elation: ${error.message}`, {
+      cause: error,
+    });
+  }
 }
⛔ Skipped due to learnings
Learnt from: thomasyopes
PR: metriport/metriport#3882
File: packages/core/src/external/ehr/lambdas/get-bundle-by-resource-type/ehr-get-bundle-by-resource-type-cloud.ts:0-0
Timestamp: 2025-05-28T19:20:47.442Z
Learning: In packages/core/src/external/ehr/lambdas/get-bundle-by-resource-type/ehr-get-bundle-by-resource-type-cloud.ts, the EHR get bundle by resource type Lambda endpoint is guaranteed to return valid JSON, so JSON.parse() error handling is not necessary for this specific endpoint.
Learnt from: thomasyopes
PR: metriport/metriport#3882
File: packages/core/src/external/ehr/command/get-bundle-by-resource-type/ehr-get-bundle-by-resource-type-cloud.ts:27-49
Timestamp: 2025-05-28T19:22:09.281Z
Learning: In packages/core/src/external/ehr/command/get-bundle-by-resource-type/ehr-get-bundle-by-resource-type-cloud.ts, the EHR get bundle by resource type Lambda endpoint is guaranteed to return valid JSON, so JSON.parse() error handling is not necessary for this specific endpoint.
Learnt from: thomasyopes
PR: metriport/metriport#3788
File: packages/api/src/external/ehr/shared/utils/bundle.ts:83-93
Timestamp: 2025-05-08T19:41:36.533Z
Learning: In the Metriport codebase, the team prefers to let errors bubble up naturally in some cases rather than adding explicit error handling at every layer, as demonstrated in the refreshEhrBundle function in the bundle.ts file.
Learnt from: thomasyopes
PR: metriport/metriport#3608
File: packages/core/src/external/ehr/canvas/index.ts:451-469
Timestamp: 2025-04-21T17:07:30.574Z
Learning: The `getMetriportOnlyBundleByResourceType` method in CanvasApi returns `Promise<Bundle | undefined>` because the Metriport-only bundle is a computed artifact that may not exist yet. In contrast, `getBundleByResourceType` returns `Promise<Bundle>` because it can always fetch from the EHR API if the S3 cached bundle doesn't exist.
packages/core/src/command/conversion-fhir/conversion-fhir.ts (1)

4-19: Well-designed type definitions and interface

The ConversionFhirRequest type and ConversionFhirHandler interface are well-structured with appropriate optional fields and clear method signatures. They follow TypeScript best practices and maintain good separation of concerns.

packages/core/src/command/conversion-fhir/conversion-fhir-cloud.ts (1)

11-16: Remove redundant assignment in constructor.

The assignment this.lambdaClient = lambdaClient; on line 15 is redundant because the parameter is already assigned to the instance property through the private modifier on line 13.

  constructor(
    private readonly nodejsFhirConvertLambdaName: string,
    lambdaClient: LambdaClient = makeLambdaClient(Config.getAWSRegion())
  ) {
-   this.lambdaClient = lambdaClient;
  }

Likely an incorrect or invalid review comment.

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

674-710: Well-structured bundle creation and partitioning logic.

The function properly separates target and reference resources, validates them against schemas, and returns a clear structure with both bundles.

Thomas Yopes added 2 commits June 23, 2025 17:52
…metriport/metriport into eng-139-elation-contribution

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

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

♻️ Duplicate comments (3)
packages/core/src/command/conversion-fhir/conversion-fhir.ts (1)

2-2: Fix import path to match corrected filename.

The import references the misspelled filename. This should be corrected to match the fixed filename from the previous file.

-import { FHIRConverterCDATemplate } from "@metriport/shared/domain/converison-fhir";
+import { FHIRConverterCDATemplate } from "@metriport/shared/domain/conversion-fhir";
packages/core/src/external/ehr/elation/index.ts (2)

341-360: Fix typo in parameter name.

The parameter name has a typo: "elationPatinetId" should be "elationPatientId" (note the "n" and "t" are swapped).

  async getBundleByResourceType({
    cxId,
    metriportPatientId,
-   elationPatinetId,
+   elationPatientId,
    resourceType,
-   fhirConverterToEhrBundle,
+   fhirConverterToEhrBundle,
    useCachedBundle = true,
  }: {
    cxId: string;
    metriportPatientId: string;
-   elationPatinetId: string;
+   elationPatientId: string;
    resourceType: string;
-   fhirConverterToEhrBundle: (params: {
+   fhirConverterToEhrBundle: (params: {
      inputS3Key: string;
      inputS3BucketName: string;
      outputS3Key: string;
      outputS3BucketName: string;
    }) => Promise<EhrFhirResourceBundle>;
    useCachedBundle?: boolean;
  }): Promise<Bundle> {

366-410: Update all references to the corrected parameter name.

After fixing the parameter name typo, update all references within the method.

-   const xml = await this.getCcdaDocument({ cxId, patientId: elationPatinetId, resourceType });
+   const xml = await this.getCcdaDocument({ cxId, patientId: elationPatientId, resourceType });
-     ehrPatientId: elationPatinetId,
+     ehrPatientId: elationPatientId,
-             ehrPatientId: elationPatinetId,
+             ehrPatientId: elationPatientId,
🧹 Nitpick comments (2)
packages/core/src/external/ehr/bundle/command/create-or-replace-ccda.ts (1)

13-25: Fix JSDoc documentation inconsistency.

The JSDoc mentions an s3FileName parameter that doesn't exist in the function signature. Remove this line from the documentation.

- * @param s3FileName - The S3 file name of the CCDA file.
packages/core/src/command/conversion-fhir/shared.ts (1)

30-79: Fix typo in parameter name.

There's a typo in the parameter name on line 34: "parmas" should be "params".

-  convertToFhir: (payload: string, parmas: FhirConverterParams) => Promise<Bundle<Resource>>;
+  convertToFhir: (payload: string, params: FhirConverterParams) => Promise<Bundle<Resource>>;

Otherwise, the function implementation is well-structured with proper logging, deduplication using Set, and appropriate async handling.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between cd90530 and 77e3df7.

📒 Files selected for processing (10)
  • packages/core/src/command/conversion-fhir/conversion-fhir.ts (1 hunks)
  • packages/core/src/command/conversion-fhir/shared.ts (1 hunks)
  • packages/core/src/domain/conversion/filename.ts (1 hunks)
  • packages/core/src/external/ehr/bundle/bundle-shared.ts (5 hunks)
  • packages/core/src/external/ehr/bundle/command/create-or-replace-bundle.ts (1 hunks)
  • packages/core/src/external/ehr/bundle/command/create-or-replace-ccda.ts (1 hunks)
  • packages/core/src/external/ehr/bundle/command/fetch-bundle.ts (2 hunks)
  • packages/core/src/external/ehr/elation/command/get-bundle-by-resource-type.ts (1 hunks)
  • packages/core/src/external/ehr/elation/index.ts (6 hunks)
  • packages/core/src/util/config.ts (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • packages/core/src/external/ehr/bundle/command/create-or-replace-bundle.ts
  • packages/core/src/external/ehr/bundle/command/fetch-bundle.ts
  • packages/core/src/domain/conversion/filename.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/core/src/external/ehr/bundle/bundle-shared.ts
  • packages/core/src/util/config.ts
  • packages/core/src/external/ehr/elation/command/get-bundle-by-resource-type.ts
🧰 Additional context used
📓 Path-based instructions (2)
`**/*`: Use eslint to enforce code style Use prettier to format code Max column length is 100 chars Multi-line comments use /** */ Top-level comments go after the import (save pre-...

**/*: Use eslint to enforce code style
Use prettier to format code
Max column length is 100 chars
Multi-line comments use /** */
Top-level comments go after the import (save pre-import to basic file header, like license)
Move literals to constants declared after imports when possible
File names: kebab-case

  • packages/core/src/external/ehr/bundle/command/create-or-replace-ccda.ts
  • packages/core/src/command/conversion-fhir/conversion-fhir.ts
  • packages/core/src/command/conversion-fhir/shared.ts
  • packages/core/src/external/ehr/elation/index.ts
`**/*.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/bundle/command/create-or-replace-ccda.ts
  • packages/core/src/command/conversion-fhir/conversion-fhir.ts
  • packages/core/src/command/conversion-fhir/shared.ts
  • packages/core/src/external/ehr/elation/index.ts
🧠 Learnings (1)
packages/core/src/external/ehr/elation/index.ts (1)
Learnt from: thomasyopes
PR: metriport/metriport#3608
File: packages/core/src/external/ehr/canvas/index.ts:451-469
Timestamp: 2025-04-21T17:07:30.574Z
Learning: The `getMetriportOnlyBundleByResourceType` method in CanvasApi returns `Promise<Bundle | undefined>` because the Metriport-only bundle is a computed artifact that may not exist yet. In contrast, `getBundleByResourceType` returns `Promise<Bundle>` because it can always fetch from the EHR API if the S3 cached bundle doesn't exist.
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
🔇 Additional comments (13)
packages/core/src/external/ehr/bundle/command/create-or-replace-ccda.ts (4)

1-4: LGTM! Well-organized imports.

The imports are properly structured and all appear to be used in the implementation.


6-11: LGTM! Well-defined type using proper TypeScript patterns.

The CreateOrReplaceCcdaParams type correctly omits unnecessary properties from the base type and adds the required payload property.


26-38: LGTM! Well-designed function signature.

The function signature properly uses destructuring, has appropriate default values, and returns a well-typed object.


39-74: LGTM! Solid implementation with proper error handling.

The implementation correctly:

  • Uses proper logging with context
  • Implements network retries for resilience
  • Sets appropriate content type for XML
  • Provides detailed error context
  • Follows the established patterns for S3 operations
packages/core/src/command/conversion-fhir/conversion-fhir.ts (2)

4-15: LGTM! Well-structured type definition.

The ConversionFhirRequest type is well-designed with:

  • Clear property names following camelCase convention
  • Appropriate use of optional properties
  • Proper separation of input/output S3 configurations
  • Meaningful conversion control flags

17-21: LGTM! Clean interface design.

The ConversionFhirHandler interface follows good design principles with a single, focused method that has a clear contract.

packages/core/src/command/conversion-fhir/shared.ts (3)

1-28: LGTM! Well-organized imports and constants.

The imports are properly structured and the LARGE_CHUNK_SIZE_IN_BYTES constant follows the guideline of moving literals to constants. The getS3Utils helper function provides good encapsulation.


119-155: LGTM! Well-implemented intermediate step saving.

The function properly handles both string and Bundle types, uses appropriate error handling without re-throwing (since step saving is optional), and provides good logging for debugging purposes.


157-171: LGTM! Clean and focused output saving function.

The function is appropriately simple and focused on its single responsibility of saving the final conversion result to the output bucket.

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

78-127: LGTM! Well-structured constants and type definitions.

The new constants and type guards are well-implemented:

  • Clear mapping between FHIR resource types and Elation CCDA sections
  • Proper TypeScript type guards with runtime validation
  • Good separation between main resources and reference resources
  • Currently conservative approach with only AllergyIntolerance enabled

217-259: LGTM! Well-implemented CCDA document fetching.

The getCcdaDocument method is properly implemented with:

  • Input validation for supported resource types
  • Proper URL parameter handling for section filtering
  • Correct base64 decoding
  • Integration with the CCDA storage utility
  • Good error handling and logging

361-410: LGTM! Sound implementation logic once parameter typos are fixed.

The method implementation follows good patterns:

  • Proper resource type validation
  • Correct FHIR conversion workflow
  • Bundle partitioning for target vs reference resources
  • Appropriate caching strategy
  • Async reference bundle saving

The return type Promise<Bundle> is correct per established patterns where this method can always return a bundle by fetching from the EHR if needed.


412-446: LGTM! Well-implemented resource bundle retrieval by ID.

The getResourceBundleByResourceId method is correctly implemented:

  • Validates both main and reference resource types
  • Appropriately uses cache-only strategy with empty fetch function
  • Proper error handling with meaningful context
  • Correct usage pattern for retrieving specific resources by ID

Thomas Yopes added 2 commits June 24, 2025 06:12
Ref: ENG-139

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

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

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 77e3df7 and f1dbf35.

📒 Files selected for processing (1)
  • packages/api/src/external/cda/generate-ccd.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
`**/*`: Use eslint to enforce code style Use prettier to format code Max column length is 100 chars Multi-line comments use /** */ Top-level comments go after the import (save pre-...

**/*: Use eslint to enforce code style
Use prettier to format code
Max column length is 100 chars
Multi-line comments use /** */
Top-level comments go after the import (save pre-import to basic file header, like license)
Move literals to constants declared after imports when possible
File names: kebab-case

  • packages/api/src/external/cda/generate-ccd.ts
`**/*.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/api/src/external/cda/generate-ccd.ts
🪛 Biome (1.9.4)
packages/api/src/external/cda/generate-ccd.ts

[error] 55-55: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
  • GitHub Check: check-pr / lint-build-test
🔇 Additional comments (1)
packages/api/src/external/cda/generate-ccd.ts (1)

23-23: LGTM!

The import statement is correctly structured and follows the project's import organization conventions.

Thomas Yopes added 6 commits June 24, 2025 06:22
Ref: ENG-139

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
…metriport/metriport into eng-139-elation-contribution

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

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
…metriport/metriport into eng-139-elation-contribution

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

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
…metriport/metriport into eng-139-elation-contribution

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

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

♻️ Duplicate comments (1)
packages/core/src/command/conversion-fhir/convert-payload-to-fhir.ts (1)

70-73: Consider using a dedicated batch bundle builder function.

Based on the past review comment, there's a preference to deprecate buildBundleFromResources and create dedicated functions like buildBatchBundle for each bundle type, since each has its own logic (e.g., searchset bundles have total).

Consider creating a dedicated buildBatchBundle function instead:

-  const bundle = buildBundleFromResources({
-    type: "batch",
-    resources: [...resources.values()],
-  });
+  const bundle = buildBatchBundle([...resources.values()]);
🧹 Nitpick comments (3)
packages/core/src/command/conversion-fhir/convert-payload-to-fhir.ts (3)

101-105: Strengthen the nonXMLBody validation.

The current string inclusion check for "nonXMLBody" might be fragile and could produce false positives if this text appears in legitimate XML content.

Consider a more robust check:

-  if (payloadRaw.includes("nonXMLBody")) {
+  if (payloadRaw.includes("<nonXMLBody>")) {
     throw new BadRequestError("XML document is unstructured CDA with nonXMLBody", undefined, {
       ...additionalInfo,
     });
   }

115-118: Address the B64 attachments TODO.

The extraction of base64 PDF attachments is logged but not processed, leaving functionality incomplete.

This TODO comment indicates missing B64 attachment processing. The extracted attachments are currently ignored, which might result in incomplete FHIR conversion.

Would you like me to help implement the B64 attachment processing or open a new issue to track this requirement?


141-143: Address the TODO for optional parameters.

The converter parameters unusedSegments and invalidAccess are hardcoded as "false" with a TODO indicating they should be optional.

The hardcoded converter parameters suggest incomplete flexibility in the conversion process.

Would you like me to help implement optional parameter handling or open a new issue to track this enhancement?

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ac52bef and 9872d51.

📒 Files selected for processing (1)
  • packages/core/src/command/conversion-fhir/convert-payload-to-fhir.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
`**/*`: Use eslint to enforce code style Use prettier to format code Max column length is 100 chars Multi-line comments use /** */ Top-level comments go after the import (save pre-...

**/*: Use eslint to enforce code style
Use prettier to format code
Max column length is 100 chars
Multi-line comments use /** */
Top-level comments go after the import (save pre-import to basic file header, like license)
Move literals to constants declared after imports when possible
File names: kebab-case

  • packages/core/src/command/conversion-fhir/convert-payload-to-fhir.ts
`**/*.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/conversion-fhir/convert-payload-to-fhir.ts
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • 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 (3)
packages/core/src/command/conversion-fhir/convert-payload-to-fhir.ts (3)

149-194: Good error handling strategy for intermediate steps.

The saveConverterStep function correctly implements the error handling strategy mentioned in past reviews - throwing errors for result files but not for intermediate steps via the throwError parameter.


32-82: Well-structured main conversion function.

The main function follows good practices with proper error handling, resource deduplication using Set, chunk size monitoring, and comprehensive logging. The return type is well-defined and the function signature is clear.


50-63: Effective chunk size monitoring.

The implementation correctly monitors chunk sizes and uses structured logging with capture.message for large chunks, providing good observability for performance issues.

Ref: ENG-139

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
Thomas Yopes added 2 commits June 24, 2025 17:45
Ref: ENG-139

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

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

🧹 Nitpick comments (5)
packages/core/src/command/conversion-fhir/utils.ts (3)

44-48: Consider more robust validation for unstructured CDA detection.

The current string search for "nonXMLBody" could potentially produce false positives if this text appears in legitimate XML content or comments. Consider using XML parsing or more specific pattern matching to detect unstructured CDA documents.

-  if (payloadRaw.includes("nonXMLBody")) {
+  if (payloadRaw.includes("<nonXMLBody>") || payloadRaw.includes("nonXMLBody")) {

84-86: Address the TODO comment for converter parameters.

The hardcoded unusedSegments and invalidAccess parameters should be made configurable or removed if not needed. This reduces technical debt and improves maintainability.

Would you like me to help generate a solution that makes these parameters optional or configurable based on the conversion requirements?


119-124: Optimize file upload for large payloads.

Creating a Buffer from the entire result string could consume significant memory for large FHIR bundles. Consider using streaming uploads for better memory efficiency.

-      file: Buffer.from(typeof result === "string" ? result : JSON.stringify(result), "utf8"),
+      file: typeof result === "string" ? result : JSON.stringify(result),

Note: This assumes S3Utils.uploadFile can accept string input directly. If not, consider implementing streaming for large payloads.

packages/core/src/command/conversion-fhir/conversion-fhir.ts (2)

40-52: Optimize chunk size calculation and improve telemetry threshold.

Using new Blob([payload]).size to calculate chunk size creates an unnecessary object. Consider using Buffer.byteLength() for better performance. Also, the 50MB threshold seems arbitrary - consider making it configurable.

-      const chunkSize = new Blob([payload]).size;
+      const chunkSize = Buffer.byteLength(payload, 'utf8');

Consider adding a configuration constant:

-const LARGE_CHUNK_SIZE_IN_BYTES = 50_000_000;
+const LARGE_CHUNK_SIZE_IN_BYTES = Config.getFhirConversionChunkSizeThreshold() ?? 50_000_000;

54-58: Enhance validation logic for conversion results.

The current validation could be more explicit about what constitutes a valid conversion result. Consider adding more descriptive logging for skipped results.

-      if (!conversionResult || !conversionResult.entry || conversionResult.entry.length < 1)
+      if (!conversionResult || !conversionResult.entry || conversionResult.entry.length < 1) {
+        log(`Skipping chunk ${index} - no valid conversion result or entries`);
         continue;
+      }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6eb115e and 106df5a.

📒 Files selected for processing (2)
  • packages/core/src/command/conversion-fhir/conversion-fhir.ts (1 hunks)
  • packages/core/src/command/conversion-fhir/utils.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
`**/*`: Use eslint to enforce code style Use prettier to format code Max column length is 100 chars Multi-line comments use /** */ Top-level comments go after the import (save pre-...

**/*: Use eslint to enforce code style
Use prettier to format code
Max column length is 100 chars
Multi-line comments use /** */
Top-level comments go after the import (save pre-import to basic file header, like license)
Move literals to constants declared after imports when possible
File names: kebab-case

  • packages/core/src/command/conversion-fhir/utils.ts
  • packages/core/src/command/conversion-fhir/conversion-fhir.ts
`**/*.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/conversion-fhir/utils.ts
  • packages/core/src/command/conversion-fhir/conversion-fhir.ts
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • 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 (3)
packages/core/src/command/conversion-fhir/utils.ts (1)

92-106: Well-designed function signature with good error handling options.

The saveConverterStep function provides flexible error handling with the throwError parameter and comprehensive logging. The type union for result parameter appropriately handles both string and Bundle types.

packages/core/src/command/conversion-fhir/conversion-fhir.ts (2)

38-39: Good use of Set for resource deduplication.

Using a Set to collect resources effectively prevents duplicates across chunks while maintaining good performance characteristics. This is a solid design choice for handling potentially overlapping conversion results.


26-31: Well-designed async method signature with comprehensive return type.

The convertToFhir method provides a clean interface that returns both the converted bundle and S3 storage details, which is essential for tracking conversion results. The return type clearly communicates what consumers can expect.

Ref: ENG-139

Ref: #1040
Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
@thomasyopes thomasyopes requested a review from leite08 June 25, 2025 00:59
Copy link
Member
@leite08 leite08 left a comment

Choose a reason for hiding this comment

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

Approving w/ this comment open so we can ship it. Hopefully we can at least mark the buildBundleFromResources function as deprecated.

Ref: ENG-139

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

🧹 Nitpick comments (3)
packages/core/src/command/conversion-fhir/utils.ts (1)

25-76: Well-structured payload processing with proper validation.

The function correctly handles the conversion pipeline: S3 retrieval, validation, cleaning, and partitioning. The error handling is comprehensive with proper context information.

One minor observation: The TODO comment at line 57 indicates incomplete work for B64 attachments processing. Consider tracking this as a separate issue.

packages/core/src/command/conversion-fhir/conversion-fhir.ts (2)

29-82: Consider more accurate byte size calculation for chunk monitoring.

The chunk size calculation using new Blob([payload]).size may not accurately represent the UTF-8 byte size of the string. Consider using Buffer.byteLength(payload, 'utf8') for more precise measurement.

-      const chunkSize = new Blob([payload]).size;
+      const chunkSize = Buffer.byteLength(payload, 'utf8');

Otherwise, the conversion orchestration logic is well-implemented with proper resource deduplication using Set and comprehensive logging.


47-50: Address the TODO comment for converter parameters.

The TODO comment at line 47 indicates that unusedSegments and invalidAccess parameters should be made optional. Consider tracking this as a separate issue to improve the API design.

Do you want me to generate a solution that makes these parameters optional or open a new issue to track this task?

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 106df5a and 28bdec7.

📒 Files selected for processing (3)
  • packages/core/src/command/conversion-fhir/conversion-fhir.ts (1 hunks)
  • packages/core/src/command/conversion-fhir/utils.ts (1 hunks)
  • packages/core/src/external/fhir/bundle/bundle.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
`**/*`: Use eslint to enforce code style Use prettier to format code Max column length is 100 chars Multi-line comments use /** */ Top-level comments go after the import (save pre-...

**/*: Use eslint to enforce code style
Use prettier to format code
Max column length is 100 chars
Multi-line comments use /** */
Top-level comments go after the import (save pre-import to basic file header, like license)
Move literals to constants declared after imports when possible
File names: kebab-case

  • packages/core/src/external/fhir/bundle/bundle.ts
  • packages/core/src/command/conversion-fhir/utils.ts
  • packages/core/src/command/conversion-fhir/conversion-fhir.ts
`**/*.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/fhir/bundle/bundle.ts
  • packages/core/src/command/conversion-fhir/utils.ts
  • packages/core/src/command/conversion-fhir/conversion-fhir.ts
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • 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 (5)
packages/core/src/external/fhir/bundle/bundle.ts (2)

177-183: Well-implemented batch bundle builder function.

The function correctly creates a batch-type FHIR bundle from resources, following the same pattern as buildBundleFromResources. The implementation is consistent with the existing codebase style.


323-327: Function refactoring maintains correctness.

The change from arrow function to function declaration preserves the original logic and behavior. This refactoring aligns with the coding guidelines preference to avoid creating arrow functions.

packages/core/src/command/conversion-fhir/utils.ts (1)

78-123: Robust file saving with appropriate error handling.

The function properly handles S3 uploads with comprehensive error handling and optional error throwing. The use of Buffer.from() for both string and object serialization is correct.

packages/core/src/command/conversion-fhir/conversion-fhir.ts (2)

16-27: Well-defined types for the conversion workflow.

The type definitions are clear and properly structured. ConversionFhirRequest and ConverterRequest provide good separation of concerns between input parameters and converter-specific data.


84-84: Abstract method design follows good patterns.

The abstract callConverter method provides a clean interface for subclasses to implement environment-specific conversion logic while maintaining consistent input/output contracts.

@thomasyopes thomasyopes added this pull request to the merge queue Jun 25, 2025
Merged via the queue into develop with commit 250302e Jun 25, 2025
21 checks passed
@thomasyopes thomasyopes deleted the eng-139-elation-contribution branch June 25, 2025 01:14
@thomasyopes thomasyopes mentioned this pull request Jun 25, 2025
1 task
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
0