-
Notifications
You must be signed in to change notification settings - Fork 72
RELEASE data contribution 1.2 (refreshing from the lambda) #3973
New issue
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
Conversation
Ref: ENG-199 Ref: #1040 Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
…riport/metriport into 199-elation-contribution-1.5.1 Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
…riport/metriport into 199-elation-contribution-1.5.1 Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
…riport/metriport into 199-elation-contribution-1.5.1 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>
…riport/metriport into 199-elation-contribution-1.5.1 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>
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>
…riport/metriport into 199-elation-contribution-1.5.1 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>
…riport/metriport into 199-elation-contribution-1.5.1 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>
…riport/metriport into 199-elation-contribution-1.5.1 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>
…riport/metriport into 199-elation-contribution-1.5.1 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>
…riport/metriport into 199-elation-contribution-1.5.1 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>
…riport/metriport into 199-elation-contribution-1.5.1 Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
…riport/metriport into 199-elation-contribution-1.5.1 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>
…riport/metriport into 199-elation-contribution-1.5.1 Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
…riport/metriport into 199-elation-contribution-1.5.1 Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
…riport/metriport into 199-elation-contribution-1.5.1 Signed-off-by: Thomas Yopes <thomasyopes@Thomass-MBP.attlocal.net>
… 199-elation-contribution-1.5.1 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>
feat(ehr): adding athena / elation / healthie to lambda + canvas get resources
WalkthroughThis change refactors appointment and bundle retrieval logic for multiple EHR systems (AthenaHealth, Elation, Healthie, Canvas) to use handler-based abstractions and token-based client creation. It introduces new client creation utilities, updates type signatures, removes legacy refresh endpoints, and aligns types and schemas for improved consistency and token support across EHR integrations. Changes
Sequence Diagram(s)sequenceDiagram
participant Caller
participant HandlerFactory
participant TokenClientUtil
participant EHRClient
participant EHRApi
Caller->>HandlerFactory: buildEhrGetAppointmentsHandler()
HandlerFactory->>TokenClientUtil: createEhrClientWithClientCredentials()
TokenClientUtil->>EHRClient: instantiate client with tokenId, env
HandlerFactory-->>Caller: handler with getAppointments()
Caller->>HandlerFactory: handler.getAppointments(method, params)
HandlerFactory->>EHRApi: fetch appointments (using tokenId, practiceId, etc.)
EHRApi-->>HandlerFactory: appointments[]
HandlerFactory-->>Caller: appointments[]
sequenceDiagram
participant JobRunner
participant TwoLeggedClientUtil
participant EHRClient
participant BundleRetriever
JobRunner->>TwoLeggedClientUtil: getTwoLeggedClientWithTokenIdAndEnvironment(ehr, cxId, practiceId)
TwoLeggedClientUtil->>EHRClient: instantiate with tokenId, env
TwoLeggedClientUtil-->>JobRunner: { client, tokenId, environment }
JobRunner->>BundleRetriever: refreshEhrBundles({ ..., tokenId })
BundleRetriever->>EHRClient: getBundleByResourceType({ ..., tokenId })
EHRClient-->>BundleRetriever: Bundle
BundleRetriever-->>JobRunner: done
Possibly related PRs
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
npm error code ERR_SSL_WRONG_VERSION_NUMBER ✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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
📒 Files selected for processing (42)
packages/api/src/external/ehr/athenahealth/command/process-patients-from-appointments.ts
(5 hunks)packages/api/src/external/ehr/athenahealth/shared.ts
(1 hunks)packages/api/src/external/ehr/canvas/command/bundle/refresh-ehr-bundle.ts
(0 hunks)packages/api/src/external/ehr/canvas/shared.ts
(1 hunks)packages/api/src/external/ehr/elation/command/process-patients-from-appointments.ts
(4 hunks)packages/api/src/external/ehr/elation/shared.ts
(2 hunks)packages/api/src/external/ehr/healthie/command/process-patients-from-appointments.ts
(5 hunks)packages/api/src/external/ehr/shared/command/bundle/refresh-ehr-bundles.ts
(0 hunks)packages/api/src/external/ehr/shared/command/clients/get-two-legged-client.ts
(1 hunks)packages/api/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/start-job.ts
(3 hunks)packages/api/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/start-jobs-across-ehrs.ts
(2 hunks)packages/api/src/external/ehr/shared/utils/bundle/functions.ts
(2 hunks)packages/api/src/external/ehr/shared/utils/bundle/types.ts
(1 hunks)packages/api/src/external/ehr/shared/utils/client.ts
(3 hunks)packages/api/src/external/ehr/shared/utils/job.ts
(2 hunks)packages/api/src/routes/internal/ehr/patient.ts
(0 hunks)packages/api/src/routes/internal/ehr/practice.ts
(1 hunks)packages/core/src/external/ehr/api/bundle/refresh-ehr-bundle.ts
(0 hunks)packages/core/src/external/ehr/athenahealth/command/get-appointments-from-subscription-events.ts
(1 hunks)packages/core/src/external/ehr/athenahealth/command/get-appointments.ts
(1 hunks)packages/core/src/external/ehr/athenahealth/index.ts
(1 hunks)packages/core/src/external/ehr/athenahealth/shared.ts
(1 hunks)packages/core/src/external/ehr/canvas/command/get-bundle-by-resource-type.ts
(1 hunks)packages/core/src/external/ehr/canvas/index.ts
(2 hunks)packages/core/src/external/ehr/canvas/shared.ts
(1 hunks)packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments-direct.ts
(2 hunks)packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments.ts
(2 hunks)packages/core/src/external/ehr/command/get-bundle-by-resource-type.ts
(1 hunks)packages/core/src/external/ehr/command/get-secrets.ts
(3 hunks)packages/core/src/external/ehr/elation/command/get-appointments.ts
(1 hunks)packages/core/src/external/ehr/elation/index.ts
(1 hunks)packages/core/src/external/ehr/elation/shared.ts
(1 hunks)packages/core/src/external/ehr/environment.ts
(1 hunks)packages/core/src/external/ehr/healthie/command/get-appointments.ts
(1 hunks)packages/core/src/external/ehr/healthie/index.ts
(1 hunks)packages/core/src/external/ehr/healthie/shared.ts
(1 hunks)packages/core/src/external/ehr/job/create-resource-diff-bundles/create-resource-diff-bundle-shared.ts
(1 hunks)packages/core/src/external/ehr/job/create-resource-diff-bundles/steps/refresh/ehr-refresh-ehr-bundles-local.ts
(3 hunks)packages/core/src/external/ehr/secrets.ts
(1 hunks)packages/core/src/external/ehr/shared.ts
(1 hunks)packages/core/src/util/config.ts
(1 hunks)packages/lambdas/src/shared/ehr.ts
(1 hunks)
💤 Files with no reviewable changes (4)
- packages/api/src/routes/internal/ehr/patient.ts
- packages/api/src/external/ehr/canvas/command/bundle/refresh-ehr-bundle.ts
- packages/api/src/external/ehr/shared/command/bundle/refresh-ehr-bundles.ts
- packages/core/src/external/ehr/api/bundle/refresh-ehr-bundle.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
, preferisDisabled
- For numeric values, if the type doesn’t convey the unit, add the unit to the name
- Typescript
- Use types
- Prefer
const
instead oflet
- Avoid
any
and casting fromany
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 toundefined
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 (seeprocessAsyncError
andemptyFunction
depending on the case)- Date and Time
- Always use
buildDayjs()
to createdayjs
instances- Prefer
dayjs.duration(...)
to create duration consts and keep them asduration
- 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)
notif ('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
andconsole.error
in packages other than utils, infra and shared,
and try to useout().log
instead- Avoid multi-line logs
- don't send objects as a second parameter to
console.log()
orout().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/canvas/shared.ts
packages/core/src/util/config.ts
packages/lambdas/src/shared/ehr.ts
packages/core/src/external/ehr/elation/index.ts
packages/api/src/external/ehr/athenahealth/shared.ts
packages/api/src/external/ehr/canvas/shared.ts
packages/core/src/external/ehr/shared.ts
packages/core/src/external/ehr/athenahealth/index.ts
packages/core/src/external/ehr/canvas/index.ts
packages/core/src/external/ehr/job/create-resource-diff-bundles/create-resource-diff-bundle-shared.ts
packages/api/src/external/ehr/elation/shared.ts
packages/core/src/external/ehr/healthie/command/get-appointments.ts
packages/api/src/routes/internal/ehr/practice.ts
packages/core/src/external/ehr/athenahealth/shared.ts
packages/core/src/external/ehr/canvas/command/get-bundle-by-resource-type.ts
packages/core/src/external/ehr/elation/command/get-appointments.ts
packages/api/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/start-jobs-across-ehrs.ts
packages/api/src/external/ehr/shared/utils/job.ts
packages/core/src/external/ehr/healthie/shared.ts
packages/core/src/external/ehr/athenahealth/command/get-appointments-from-subscription-events.ts
packages/core/src/external/ehr/healthie/index.ts
packages/core/src/external/ehr/job/create-resource-diff-bundles/steps/refresh/ehr-refresh-ehr-bundles-local.ts
packages/core/src/external/ehr/athenahealth/command/get-appointments.ts
packages/api/src/external/ehr/shared/utils/bundle/types.ts
packages/core/src/external/ehr/command/get-bundle-by-resource-type.ts
packages/api/src/external/ehr/elation/command/process-patients-from-appointments.ts
packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments-direct.ts
packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments.ts
packages/core/src/external/ehr/elation/shared.ts
packages/api/src/external/ehr/shared/utils/bundle/functions.ts
packages/core/src/external/ehr/environment.ts
packages/api/src/external/ehr/shared/utils/client.ts
packages/api/src/external/ehr/athenahealth/command/process-patients-from-appointments.ts
packages/api/src/external/ehr/healthie/command/process-patients-from-appointments.ts
packages/core/src/external/ehr/command/get-secrets.ts
packages/api/src/external/ehr/shared/command/clients/get-two-legged-client.ts
packages/core/src/external/ehr/secrets.ts
packages/api/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/start-job.ts
🧠 Learnings (4)
packages/core/src/external/ehr/canvas/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/job/create-resource-diff-bundles/steps/refresh/ehr-refresh-ehr-bundles-local.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/command/get-bundle-by-resource-type.ts (3)
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#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.
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.
packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments-direct.ts (1)
Learnt from: thomasyopes
PR: metriport/metriport#3960
File: packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments-direct.ts:11-19
Timestamp: 2025-06-05T16:28:33.694Z
Learning: In the EhrGetAppointmentsDirect class in packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments-direct.ts, type casting `as T[]` for the handler return value is considered safe and doesn't require additional runtime validation by the user thomasyopes.
🧬 Code Graph Analysis (23)
packages/core/src/external/ehr/elation/index.ts (2)
packages/shared/src/index.ts (1)
sleep
(13-13)packages/core/src/external/ehr/shared.ts (1)
paginateWaitTime
(52-52)
packages/api/src/external/ehr/athenahealth/shared.ts (2)
packages/core/src/external/ehr/environment.ts (1)
EhrPerPracticeParams
(38-38)packages/core/src/external/ehr/athenahealth/index.ts (1)
AthenaEnv
(153-153)
packages/api/src/external/ehr/canvas/shared.ts (2)
packages/core/src/external/ehr/environment.ts (1)
EhrPerPracticeParams
(38-38)packages/core/src/external/ehr/canvas/index.ts (1)
CanvasEnv
(82-82)
packages/core/src/external/ehr/athenahealth/index.ts (2)
packages/shared/src/index.ts (1)
sleep
(13-13)packages/core/src/external/ehr/shared.ts (1)
paginateWaitTime
(52-52)
packages/core/src/external/ehr/canvas/index.ts (2)
packages/shared/src/index.ts (1)
sleep
(13-13)packages/core/src/external/ehr/shared.ts (1)
paginateWaitTime
(52-52)
packages/api/src/external/ehr/elation/shared.ts (2)
packages/core/src/external/ehr/elation/index.ts (1)
ElationEnv
(56-56)packages/core/src/external/ehr/environment.ts (1)
EhrPerPracticeParams
(38-38)
packages/core/src/external/ehr/healthie/command/get-appointments.ts (7)
packages/core/src/external/ehr/healthie/index.ts (1)
getAppointments
(221-303)packages/core/src/external/ehr/canvas/command/get-appointments.ts (1)
getAppointments
(7-28)packages/core/src/external/ehr/athenahealth/command/get-appointments.ts (1)
getAppointments
(6-32)packages/core/src/external/ehr/elation/command/get-appointments.ts (1)
getAppointments
(6-27)packages/shared/src/interface/external/ehr/healthie/appointment.ts (1)
AppointmentWithAttendee
(11-13)packages/shared/src/index.ts (1)
BadRequestError
(40-40)packages/core/src/external/ehr/healthie/shared.ts (1)
createHealthieClient
(7-32)
packages/core/src/external/ehr/canvas/command/get-bundle-by-resource-type.ts (1)
packages/core/src/external/ehr/command/get-bundle-by-resource-type.ts (1)
getBundleByResourceType
(19-25)
packages/api/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/start-jobs-across-ehrs.ts (1)
packages/shared/src/interface/external/ehr/source.ts (1)
EhrSource
(9-9)
packages/api/src/external/ehr/shared/utils/job.ts (1)
packages/shared/src/interface/external/ehr/source.ts (1)
EhrSource
(9-9)
packages/core/src/external/ehr/athenahealth/command/get-appointments-from-subscription-events.ts (2)
packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments.ts (1)
GetAppointmentsClientRequest
(16-16)packages/core/src/external/ehr/athenahealth/shared.ts (1)
createAthenaHealthClient
(8-38)
packages/core/src/external/ehr/healthie/index.ts (2)
packages/shared/src/index.ts (1)
sleep
(13-13)packages/core/src/external/ehr/shared.ts (1)
paginateWaitTime
(52-52)
packages/api/src/external/ehr/shared/utils/bundle/types.ts (1)
packages/shared/src/interface/external/ehr/source.ts (1)
EhrSource
(9-9)
packages/api/src/external/ehr/elation/command/process-patients-from-appointments.ts (3)
packages/api/src/external/ehr/elation/shared.ts (1)
createElationClientWithTokenIdAndEnvironment
(98-107)packages/api/src/external/ehr/shared/utils/appointment.ts (1)
getLookForwardTimeRange
(32-43)packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments-factory.ts (1)
buildEhrGetAppointmentsHandler
(6-12)
packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments-direct.ts (2)
packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments.ts (1)
GetAppointmentsClientRequest
(16-16)packages/shared/src/index.ts (1)
BadRequestError
(40-40)
packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments.ts (1)
packages/shared/src/interface/external/ehr/healthie/appointment.ts (1)
AppointmentWithAttendee
(11-13)
packages/api/src/external/ehr/shared/utils/bundle/functions.ts (1)
packages/shared/src/interface/external/ehr/source.ts (1)
EhrSource
(9-9)
packages/core/src/external/ehr/environment.ts (6)
packages/core/src/external/ehr/athenahealth/index.ts (1)
AthenaEnv
(153-153)packages/core/src/external/ehr/elation/index.ts (1)
ElationEnv
(56-56)packages/core/src/external/ehr/canvas/index.ts (1)
CanvasEnv
(82-82)packages/core/src/external/ehr/healthie/index.ts (1)
HealthieEnv
(46-46)packages/core/src/external/ehr/eclinicalworks/index.ts (1)
EClinicalWorksEnv
(15-15)packages/shared/src/domain/jwt-token.ts (1)
JwtTokenInfo
(1-5)
packages/api/src/external/ehr/shared/utils/client.ts (1)
packages/core/src/external/ehr/environment.ts (3)
EhrEnv
(9-9)EhrClientWithClientCredentials
(27-27)EhrClientWithClientCredentialsParams
(28-31)
packages/api/src/external/ehr/healthie/command/process-patients-from-appointments.ts (3)
packages/shared/src/interface/external/ehr/healthie/appointment.ts (1)
AppointmentWithAttendee
(11-13)packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments-factory.ts (1)
buildEhrGetAppointmentsHandler
(6-12)packages/api/src/external/ehr/shared/utils/appointment.ts (1)
getLookForwardTimeRange
(32-43)
packages/core/src/external/ehr/command/get-secrets.ts (3)
packages/core/src/external/ehr/environment.ts (3)
EhrPerPracticeParams
(38-38)EhrEnvAndClientCredentials
(22-26)EhrEnv
(9-9)packages/core/src/external/ehr/secrets.ts (1)
EhrSourceWithDynamicSecrets
(10-10)packages/shared/src/index.ts (1)
BadRequestError
(40-40)
packages/api/src/external/ehr/shared/command/clients/get-two-legged-client.ts (5)
packages/core/src/external/ehr/environment.ts (4)
EhrPerPracticeParams
(38-38)EhrSourceWithClientCredentials
(16-16)EhrClientWithClientCredentials
(27-27)EhrEnv
(9-9)packages/api/src/external/ehr/canvas/shared.ts (1)
createCanvasClientWithTokenIdAndEnvironment
(7-16)packages/api/src/external/ehr/athenahealth/shared.ts (1)
createAthenaClientWithTokenIdAndEnvironment
(7-16)packages/api/src/external/ehr/elation/shared.ts (1)
createElationClientWithTokenIdAndEnvironment
(98-107)packages/shared/src/index.ts (1)
BadRequestError
(40-40)
packages/core/src/external/ehr/secrets.ts (1)
packages/core/src/external/ehr/shared.ts (4)
getSecretsOauthSchema
(60-64)GetSecretsOauthResult
(65-65)getSecretsApiKeySchema
(67-70)GetSecretsApiKeyResult
(71-71)
⏰ Context from checks skipped due to timeout of 90000ms (11)
- 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)
- GitHub Check: ihe_stack / deploy
- GitHub Check: mllp-server / deploy
- GitHub Check: infra-api-lambdas / deploy
- GitHub Check: api / deploy
- GitHub Check: Analyze (javascript)
- GitHub Check: check-pr / lint-build-test
- GitHub Check: check-pr / lint-build-test
🔇 Additional comments (79)
packages/core/src/util/config.ts (1)
215-215
: No-op formatting change
The added blank line has no impact on functionality or style.packages/core/src/external/ehr/shared.ts (1)
52-52
:✅ Verification successful
Verify all consumers handle the Duration type correctly.
The change from milliseconds to
dayjs.Duration
improves type safety, but this is a breaking change. Ensure all EHR clients callingsleep(paginateWaitTime)
have been updated to usesleep(paginateWaitTime.asMilliseconds())
.
🏁 Script executed:
#!/bin/bash # Description: Verify all usages of paginateWaitTime call .asMilliseconds() # Expected: All sleep calls should use .asMilliseconds() echo "Searching for paginateWaitTime usage patterns..." rg -A 2 -B 2 "paginateWaitTime" --type ts echo -e "\nSearching for sleep calls with paginateWaitTime..." rg "sleep.*paginateWaitTime" --type tsLength of output: 4856
All EHR clients now use
.asMilliseconds()
All occurrences of
sleep(paginateWaitTime)
have been updated tosleep(paginateWaitTime.asMilliseconds())
in the Healthie, Elation, Canvas, and Athenahealth integrations. No further action is required.packages/core/src/external/ehr/secrets.ts (1)
4-13
: LGTM! Well-structured type definitions and type guard.The readonly tuple with const assertion and type guard function provide excellent type safety for identifying EHR sources requiring dynamic secrets.
packages/core/src/external/ehr/canvas/shared.ts (1)
5-5
: LGTM! Import path correctly updated for centralized secrets.The import path change aligns with the new
secrets.ts
module for centralized EHR secrets management.packages/lambdas/src/shared/ehr.ts (1)
83-83
: LGTM! Schema extension supports token-based authentication.The optional
tokenId
field properly supports the token-based authentication pattern being implemented across EHR integrations.packages/core/src/external/ehr/job/create-resource-diff-bundles/create-resource-diff-bundle-shared.ts (1)
5-5
: LGTM! Clean addition of optional tokenId property.The addition of the optional
tokenId
property follows TypeScript conventions and maintains backward compatibility while supporting the new token-based authentication flow.packages/api/src/external/ehr/athenahealth/shared.ts (2)
5-5
: LGTM! Import updated for client credentials authentication.The import change from
createEhrClient
tocreateEhrClientWithClientCredentials
aligns with the broader refactoring to support token-based authentication flows.
10-10
: LGTM! Function call updated consistently with import change.The function call is properly updated to use the new client credentials-based authentication method, maintaining the same interface and return type.
packages/core/src/external/ehr/healthie/index.ts (1)
273-273
: LGTM! Correct adaptation to Duration type change.The change from
sleep(paginateWaitTime)
tosleep(paginateWaitTime.asMilliseconds())
correctly adapts to the type change wherepaginateWaitTime
is now adayjs.Duration
object instead of a numeric millisecond value. This ensures thesleep
function receives the expected numeric millisecond value.packages/api/src/routes/internal/ehr/practice.ts (1)
1-2
: LGTM! Improved modularity with separated imports.The separation of
getSecrets
andisEhrSourceWithDynamicSecrets
into different modules reflects better code organization and separation of concerns. The imports are correctly updated to match the new module structure.packages/core/src/external/ehr/elation/index.ts (1)
266-266
: LGTM! Correct duration conversion for sleep function.This change properly converts the
dayjs.Duration
object to milliseconds before passing to thesleep
function, ensuring type compatibility and correct timing behavior.packages/core/src/external/ehr/athenahealth/index.ts (1)
1595-1595
: LGTM! Consistent duration conversion across EHR implementations.This change aligns with the standardization of sleep duration handling across all EHR integrations, properly converting the dayjs duration to milliseconds.
packages/core/src/external/ehr/canvas/index.ts (2)
777-777
: LGTM! Proper duration conversion in FHIR resource pagination.This change correctly converts the dayjs duration to milliseconds for the sleep function in the FHIR resource pagination helper.
883-883
: LGTM! Consistent duration handling in appointment pagination.This change mirrors the FHIR resource pagination fix, ensuring consistent sleep duration handling across all Canvas pagination helpers.
packages/api/src/external/ehr/canvas/shared.ts (2)
5-5
: LGTM! Updated import for client credentials authentication.This import change aligns with the architectural shift to client credentials-based authentication across EHR integrations.
10-10
: LGTM! Consistent client creation with credentials support.This function call update completes the transition to client credentials authentication while maintaining the same interface and return type.
packages/api/src/external/ehr/elation/shared.ts (2)
22-22
: LGTM: Import updated for client credentials authentication.The import change from
createEhrClient
tocreateEhrClientWithClientCredentials
aligns with the architectural shift to client credentials-based authentication flows.
101-106
: LGTM: Function call updated consistently with import change.The function call correctly uses the new
createEhrClientWithClientCredentials
function while maintaining the same interface and return type.packages/api/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/start-jobs-across-ehrs.ts (2)
4-4
: LGTM: Import updated to include both EhrSource and EhrSources.The addition of
EhrSource
to the existing import allows for more precise typing in the function parameter.
48-48
: LGTM: Type refinement from EhrSources to EhrSource.The parameter type change from
EhrSources
toEhrSource
is more semantically correct since the function operates on a single EHR source, not multiple sources.packages/api/src/external/ehr/shared/utils/job.ts (3)
2-2
: LGTM: Import updated to use EhrSource for better type precision.The change from
EhrSources
toEhrSource
aligns with the codebase-wide type refinement for representing single EHR sources.
7-7
: LGTM: Type definitions updated to use EhrSource.The type changes in
StartCreateResourceDiffBundlesJobParams
andGetResourceDiffBundlesJobPayloadParams
are semantically correct since these represent operations on individual EHR sources.Also applies to: 15-15
25-25
: LGTM: Function parameter type updated consistently.The parameter type change from
EhrSources
toEhrSource
maintains consistency with the updated type definitions and accurately reflects that the function operates on a single EHR source.packages/core/src/external/ehr/athenahealth/shared.ts (2)
17-29
: LGTM: OAuth secrets retrieval and environment validation.The implementation correctly fetches OAuth secrets and validates the environment with appropriate error handling.
30-38
: LGTM: Token handling and client creation.The optional token information retrieval and client creation logic is well-implemented, following the expected pattern for two-legged authentication.
packages/core/src/external/ehr/healthie/command/get-appointments.ts (1)
1-27
:✅ Verification successful
Well-structured appointment retrieval function following established patterns.
The implementation correctly follows the same validation and client creation pattern used by other EHR sources. However, I notice this implementation doesn't extract or use the
tokenId
parameter fromGetAppointmentsClientRequest
, unlike the Canvas, AthenaHealth, and Elation implementations.Please verify if Healthie intentionally doesn't support token-based authentication, as other EHR sources extract and use
tokenId
:
🏁 Script executed:
#!/bin/bash # Verify if Healthie should support tokenId like other EHR sources echo "Checking if other EHR appointment functions use tokenId..." rg -A 10 "const.*tokenId.*=.*params" packages/core/src/external/ehr/*/command/get-appointments.ts echo "Checking if createHealthieClient supports tokenId..." rg -A 5 "createHealthieClient" packages/core/src/external/ehr/healthie/shared.tsLength of output: 4095
No tokenId support required for Healthie client
The Healthie client’s
createHealthieClient
signature only acceptscxId
andpracticeId
—it does not take atokenId
, unlike the Canvas, AthenaHealth, and Elation clients. This indicates that Healthie’s API integration doesn’t rely on a per-request tokenId parameter. No changes are needed.packages/core/src/external/ehr/healthie/shared.ts (1)
7-32
: LGTM! Clean client creation function following established patterns.The implementation correctly follows the client creation pattern with proper environment validation and error handling. The use of
getSecretsApiKeySchema
instead of OAuth schema suggests Healthie uses API key authentication rather than OAuth, which explains why this implementation differs from other EHR sources that support token-based authentication.packages/api/src/external/ehr/shared/job/bundle/create-resource-diff-bundles/start-job.ts (2)
62-70
: Excellent token-based authentication implementation.The conditional token retrieval logic is well-implemented, properly checking if the EHR source requires client credentials before fetching the token. This follows the architecture pattern for supporting different authentication mechanisms across EHR sources.
75-75
: Clean conditional tokenId inclusion.The conditional spread syntax
...(tokenId ? { tokenId } : {})
is the correct approach for optionally including the tokenId parameter, following TypeScript best practices.packages/core/src/external/ehr/elation/shared.ts (1)
8-38
: Well-implemented client creation with token support.The function properly implements optional token-based authentication following the OAuth pattern. The conditional token info retrieval (
tokenId ? await getTokenInfo(tokenId) : undefined
) and proper environment validation demonstrate good defensive programming practices.packages/core/src/external/ehr/canvas/command/get-bundle-by-resource-type.ts (1)
1-31
: LGTM! Clean implementation following established patterns.The function correctly implements the Canvas bundle retrieval handler with proper parameter destructuring, optional token support, and appropriate client usage. The mapping of
ehrPatientId
tocanvasPatientId
is clear and the return type aligns with the expected interface.packages/core/src/external/ehr/elation/command/get-appointments.ts (1)
6-27
: Excellent input validation and error handling.The function implements thorough validation for required date parameters with comprehensive error context. The BadRequestError includes helpful debugging information including method name, EHR source, and all relevant parameters. The optional
tokenId
handling is consistent with the broader refactor pattern.packages/core/src/external/ehr/job/create-resource-diff-bundles/steps/refresh/ehr-refresh-ehr-bundles-local.ts (3)
6-6
: Appropriate import update for the refactor.The import correctly reflects the migration from the deprecated
refreshEhrBundle
to the newgetBundleByResourceType
function.
18-18
: Good addition of tokenId parameter.The tokenId parameter addition supports the new token-based authentication pattern across EHR integrations.
36-45
: Migration preserves intended refresh behavior.The replacement of
refreshEhrBundle
withgetBundleByResourceType
correctly maintains the refresh functionality by explicitly settinguseCachedBundle: false
. The addition ofmetriportPatientId
and conditionaltokenId
parameters aligns with the new handler interface.packages/api/src/external/ehr/elation/command/process-patients-from-appointments.ts (3)
2-3
: Good imports for the handler-based refactor.The new imports correctly support the migration to handler-based appointment fetching with proper method enumeration and typed appointment interfaces.
Also applies to: 13-13
27-27
: Appropriate client creation function update.The change to
createElationClientWithTokenIdAndEnvironment
aligns with the token-based authentication pattern established across EHR integrations.
149-166
: Clean handler-based appointment fetching implementation.The refactor from direct client usage to the handler pattern is well-executed. The explicit method specification (
AppointmentMethods.elationGetAppointments
) provides clear intent, and the tokenId integration follows the established authentication pattern. The function maintains the same error handling and date range logic while improving architectural consistency.packages/core/src/external/ehr/athenahealth/command/get-appointments.ts (1)
6-32
: LGTM! Well-implemented appointment retrieval function.The function follows good practices with comprehensive parameter validation, meaningful error messages with context, and clean conditional parameter passing using spread operators.
packages/core/src/external/ehr/athenahealth/command/get-appointments-from-subscription-events.ts (1)
5-21
: Verify if parameter validation is intentionally omitted.This function lacks the
fromDate
/toDate
validation present in the regulargetAppointments
function. While this might be intentional for subscription events, please confirm this is the expected behavior to ensure consistency across the API.packages/api/src/external/ehr/shared/utils/bundle/types.ts (1)
2-2
: LGTM! Appropriate type refinement.The change from
EhrSources
toEhrSource
improves type precision and aligns with the broader refactoring to use singular EHR source types.Also applies to: 5-5
packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments.ts (3)
1-1
: LGTM! Well-organized imports with clear aliasing.The import aliases (
BookedAppointmentAthena
,BookedAppointmentElation
) effectively prevent naming conflicts while maintaining clarity about the source EHR system.Also applies to: 3-4
22-26
: LGTM! Appropriate union type expansion.The
Appointment
union type correctly includes all supported EHR appointment types, enabling type-safe handling across different systems.
28-35
: LGTM! Comprehensive appointment method enumeration.The enum expansion appropriately covers all EHR systems including multiple methods for AthenaHealth (regular and subscription events), following a consistent naming convention.
packages/api/src/external/ehr/shared/utils/bundle/functions.ts (4)
6-6
: LGTM: Type import updated correctly.The change from
EhrSources
toEhrSource
aligns with the refactoring from plural to singular type naming, which is more semantically correct for representing individual EHR sources.
9-9
: LGTM: Removed unused type import.The removal of
RefreshEhrBundleParamsForClient
is consistent with the broader refactoring that eliminates the refresh bundle functionality in favor of the new handler-based approach.
38-46
: LGTM: Type consistency maintained.The change from
EhrSources
toEhrSource
for the record type is consistent with the import changes and provides better type safety for individual EHR source lookups.
48-48
: LGTM: Function signature updated consistently.The parameter type change from
EhrSources
toEhrSource
maintains consistency with the type refactoring throughout the file.packages/core/src/external/ehr/command/get-bundle-by-resource-type.ts (4)
6-15
: LGTM: Well-structured request types.The request types are properly defined with clear separation between the main request and client request types. The optional
tokenId
parameter supports the new token-based authentication flows.
19-25
: LGTM: Clean handler delegation pattern.The function properly delegates to the appropriate handler based on the EHR source, following the established pattern used in other EHR command modules.
39-47
: LGTM: Proper error handling with informative messages.The error handling follows the coding guidelines with static error messages and dynamic data in the
additionalInfo
property.
31-37
:❓ Verification inconclusive
Verify implementation timeline for remaining EHR sources.
Currently only Canvas has an implementation while other EHR sources are undefined. This is acceptable for the current refactoring phase.
🏁 Script executed:
#!/bin/bash # Description: Check if other EHR sources have corresponding get-bundle-by-resource-type implementations echo "Searching for get-bundle-by-resource-type implementations across EHR sources..." # Search for Canvas implementation (should exist) echo "Canvas implementation:" fd "get-bundle-by-resource-type" packages/core/src/external/ehr/canvas # Search for other EHR implementations echo -e "\nAthena implementation:" fd "get-bundle-by-resource-type" packages/core/src/external/ehr/athenahealth echo -e "\nElation implementation:" fd "get-bundle-by-resource-type" packages/core/src/external/ehr/elation echo -e "\nHealthie implementation:" fd "get-bundle-by-resource-type" packages/core/src/external/ehr/healthieLength of output: 782
Confirm implementation plan for remaining EHR sources
Only Canvas currently has a
getBundleByResourceType
implementation. The other sources are still mapped toundefined
in:
- packages/core/src/external/ehr/command/get-bundle-by-resource-type.ts (lines 31–37)
Please outline your timeline or approach for adding these implementations (Athena, Elation, Healthie, eClinicalWorks), or update this map when they become available.
packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments-direct.ts (5)
2-6
: LGTM: New EHR handler imports added consistently.The imports for AthenaHealth, Elation, and Healthie appointment handlers are properly added and follow the established naming conventions with aliasing to avoid conflicts.
25-27
: LGTM: Type renames improve clarity.The renames from
GetAppointments
toGetAppointmentsFn
andAppointmentMethodsMap
toAppointmentFnMap
make the types more descriptive and follow theFn
suffix convention for function types.
29-37
: LGTM: Handler mapping expanded appropriately.The mapping now includes handlers for AthenaHealth (both variants), Elation, and Healthie, with proper undefined placeholders for eClinicalWorks. This maintains consistency with the supported EHR sources.
39-39
: LGTM: Function visibility appropriately restricted.Removing the
export
keyword makes this function internal to the module, which is appropriate since it's only used within this file and doesn't need to be part of the public API.
42-42
: LGTM: Error message improved for clarity.The error message is more specific and follows the coding guidelines with a static message and dynamic data in the
additionalInfo
property.packages/core/src/external/ehr/environment.ts (3)
11-21
: LGTM: Well-designed client credentials support.The constant array, type alias, and type guard function provide a clean way to identify EHR sources that require client credentials. The type guard follows TypeScript best practices for runtime type checking.
27-31
: LGTM: Improved type definitions for client credentials.The new types
EhrClientWithClientCredentials
andEhrClientWithClientCredentialsParams
are more specific than the previousEhrClientTwoLeggedAuth
types and properly include token information and practice ID requirements.
2-2
:❓ Verification inconclusive
Verify consistency of EhrSources import.
The import uses
EhrSources
(plural) while other files in this PR are transitioning toEhrSource
(singular). Ensure this is intentional.
🏁 Script executed:
#!/bin/bash # Description: Check usage of EhrSources vs EhrSource in this file and related files echo "Checking EhrSources usage in environment.ts:" rg "EhrSources\." packages/core/src/external/ehr/environment.ts echo -e "\nChecking if EhrSource should be used instead:" rg "EhrSource[^s]" packages/core/src/external/ehr/environment.ts echo -e "\nChecking usage in ehrSourcesWithClientCredentials definition:" rg -A 5 "ehrSourcesWithClientCredentials" packages/core/src/external/ehr/environment.tsLength of output: 1447
Please confirm EhrSources vs. EhrSource consistency
It looks like
environment.ts
intentionally uses theEhrSources
enum (e.g.EhrSources.canvas
) to build theehrSourcesWithClientCredentials
array, and then defines the singularEhrSourceWithClientCredentials
type from its values. If other files have switched to importing a singularEhrSource
type, double-check that:•
environment.ts
should indeed import the enum asEhrSources
• All references elsewhere expecting a single source use theEhrSource
type (or the new enum member type)Once you verify that files importing the singular form aren’t broken by this plural import here, you can resolve this.
packages/api/src/external/ehr/shared/utils/client.ts (2)
2-3
: LGTM! Clear type naming improvement.The rename from
EhrClientTwoLeggedAuth
toEhrClientWithClientCredentials
is more descriptive and aligns with the authentication pattern being used.
38-52
: Function rename improves clarity.The function rename from
createEhrClient
tocreateEhrClientWithClientCredentials
makes the authentication method explicit and aligns with the broader refactoring to client credentials authentication.packages/api/src/external/ehr/shared/command/clients/get-two-legged-client.ts (4)
1-11
: Well-structured imports and dependencies.The imports are organized logically and include all necessary types and functions for the unified client creation interface.
20-31
: Excellent abstraction with proper documentation.The function provides a clean, unified interface for obtaining EHR clients with token IDs. The JSDoc documentation clearly explains the parameters and purpose.
45-49
: Consider adding explicit type annotation for better clarity.While the mapping works correctly, consider adding an explicit type annotation to improve readability:
-const clientWithTokenIdAndEnvironmentMethodsBy: GetClientWithTokenIdAndEnvironmentFnMap = { +const clientWithTokenIdAndEnvironmentMethodsBy: GetClientWithTokenIdAndEnvironmentFnMap = {Actually, the type annotation is already present. The implementation looks good.
55-59
: Proper error handling with context.The error handling follows best practices by using
BadRequestError
with contextual information about the unsupported EHR source.packages/api/src/external/ehr/athenahealth/command/process-patients-from-appointments.ts (3)
1-2
: Good adoption of handler-based appointment retrieval.The new imports align with the standardized handler pattern for appointment operations across EHR sources.
153-156
:❓ Verification inconclusive
Verify token-based client creation integration.
The client creation now returns both
tokenId
andclient
, which aligns with the new authentication flow. Ensure this change is consistent across all EHR sources and that the token is properly utilized downstream.
🏁 Script executed:
#!/bin/bash # Description: Verify that other EHR sources use similar token-based client creation pattern # Search for similar client creation patterns in other EHR sources rg -A 5 "createElationClientWithTokenIdAndEnvironment|createCanvasClientWithTokenIdAndEnvironment"Length of output: 6822
Ensure Consistent Token & Client Handling Across EHR Integrations
I’ve confirmed that all
create*ClientWithTokenIdAndEnvironment
helpers now return{ client, tokenId, environment }
. In the Athena flow you’re correctly capturing both:
- packages/api/src/external/ehr/athenahealth/command/process-patients-from-appointments.ts
const { tokenId, client } = await createAthenaClientWithTokenIdAndEnvironment({ cxId, practiceId });However, the Elation and Canvas process scripts only destruct the token:
- packages/api/src/external/ehr/elation/command/process-patients-from-appointments.ts
const { tokenId } = await createElationClientWithTokenIdAndEnvironment({ cxId, practiceId });
- packages/api/src/external/ehr/canvas/command/process-patients-from-appointments.ts
const { tokenId } = await createCanvasClientWithTokenIdAndEnvironment({ cxId, practiceId });
Please verify that downstream operations in those modules don’t require the
client
instance. If they do (e.g., fetching appointments or syncing patients), update them to destruct and use bothclient
andtokenId
consistently.
193-240
:✅ Verification successful
Significant refactoring to handler pattern - verify handler implementation.
The
getAppointmentsFromApi
function has been completely refactored to use the handler pattern withbuildEhrGetAppointmentsHandler
. While this standardizes the approach across EHR sources, it's a significant change that needs verification.Key changes:
- Uses
handler.getAppointments
instead of direct API calls- Parameters renamed from
startRange
/endRange
tofromDate
/toDate
- Token ID passed to handler
🏁 Script executed:
#!/bin/bash # Description: Verify the handler implementation supports the expected methods and parameters # Check if buildEhrGetAppointmentsHandler is properly implemented ast-grep --pattern 'export function buildEhrGetAppointmentsHandler' # Check AppointmentMethods enum definition ast-grep --pattern 'export enum AppointmentMethods' # Verify athena-specific appointment methods exist rg "athenaGetAppointments|athenaGetAppointmentFromSubscriptionEvents" -A 3Length of output: 3146
🏁 Script executed:
#!/bin/bash # Locate the buildEhrGetAppointmentsHandler implementation and verify it exposes getAppointments rg -n "buildEhrGetAppointmentsHandler" -C 5Length of output: 12196
🏁 Script executed:
#!/bin/bash # Inspect the handler interface for getAppointments signature rg -n "export (interface|type) EhrGetAppointmentsHandler" -A5 packages/core/src/external/ehr/command/get-appointments # Inspect the Direct implementation to ensure it defines getAppointments<T> rg -n "class EhrGetAppointmentsDirect" -A10 packages/core/src/external/ehr/command/get-appointments # Inspect the Cloud implementation to ensure it defines getAppointments<T> rg -n "class EhrGetAppointmentsCloud" -A10 packages/core/src/external/ehr/command/get-appointmentsLength of output: 3736
🏁 Script executed:
#!/bin/bash # Inspect the GetAppointmentsRequest type to verify expected parameters rg -n "export interface GetAppointmentsRequest" -A10 packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments.tsLength of output: 134
🏁 Script executed:
#!/bin/bash # Search for the GetAppointmentsRequest type across the repository rg -n "GetAppointmentsRequest" -A5 . # If not found, print the top of the handler file to inspect its contents head -n 80 packages/core/src/external/ehr/command/get-appointments/ehr-get-appointments.tsLength of output: 9099
getAppointmentsFromApi refactoring validated
I’ve confirmed that:
buildEhrGetAppointmentsHandler()
returns anEhrGetAppointmentsHandler
with agetAppointments<T>(GetAppointmentsRequest)
signature.GetAppointmentsRequest
includesfromDate
/toDate
,tokenId
,cxId
,practiceId
, anddepartmentIds
.AppointmentMethods
enum defines bothathenaGetAppointments
andathenaGetAppointmentFromSubscriptionEvents
.- Both
EhrGetAppointmentsDirect
andEhrGetAppointmentsCloud
correctly implementgetAppointments
and honor the provided parameters.No further changes required. Approving this refactoring.
packages/core/src/external/ehr/command/get-secrets.ts (4)
2-2
: Good addition of explicit EhrSources import.Adding the explicit import for
EhrSources
improves clarity and follows good import practices.
13-13
: Improved type organization with centralized import.Moving
EhrSourceWithDynamicSecrets
to a dedicated secrets module improves code organization and makes type definitions more discoverable.
26-26
: Consistent type naming with singular form.The change from
EhrSourcesWithDynamicSecrets
toEhrSourceWithDynamicSecrets
(plural to singular) is more consistent with TypeScript naming conventions where types typically represent a single entity.Also applies to: 33-33, 36-36, 38-38, 40-40, 42-42, 49-49
52-52
: Minor improvement in error message clarity.The error message change to "Could not find handler to get secrets" is slightly clearer than the previous version.
packages/api/src/external/ehr/healthie/command/process-patients-from-appointments.ts (6)
1-2
: LGTM: Proper handler abstractions imported.The new imports correctly introduce the handler-based approach for appointment processing, replacing direct API client usage.
28-28
: LGTM: Proper import organization.Moving shared types to the shared module improves modularity and follows good organizational practices.
182-182
: LGTM: Explicit parameter passing improves clarity.Making the
practiceId
parameter explicit enhances readability and follows the coding guidelines for clear function signatures.Also applies to: 204-204
198-200
: LGTM: Proper type extension maintains type safety.The type definition correctly extends the base parameters while adding the log function parameter.
208-236
: LGTM: Handler implementation follows the refactored pattern correctly.The handler-based approach is implemented properly:
- Uses the factory to create handlers
- Maintains existing conditional logic for lookup modes
- Passes correct parameters including the Healthie-specific appointment method
- Preserves the same error handling boundaries
The handler creation on each call follows the factory pattern and maintains isolation between requests.
1-268
: Excellent refactor: Successfully implements handler-based pattern.This refactor successfully transitions from direct API client usage to the handler-based approach while:
- Maintaining all existing business logic and error handling
- Following TypeScript coding guidelines consistently
- Improving modularity through proper abstraction layers
- Supporting the new token-based authentication pattern
- Preserving functional programming principles
The changes align well with the broader EHR integration standardization effort mentioned in the AI summary.
🧰 Tools
🪛 Biome (1.9.4)
[error] 60-60: Avoid the use of spread (
...
) syntax on accumulators.Spread syntax should be avoided on accumulators (like those in
.reduce
) because it causes a time complexity ofO(n^2)
.
Consider methods such as .splice or .push instead.(lint/performance/noAccumulatingSpread)
export const getSecretsOauthSchema = z.object({ | ||
environment: z.string(), | ||
clientKey: z.string(), | ||
clientSecret: z.string(), | ||
}); | ||
export type GetSecretsOauthResult = z.infer<typeof getSecretsOauthSchema>; | ||
|
||
export const getSecretsApiKeySchema = z.object({ | ||
environment: z.string(), | ||
apiKey: z.string(), | ||
}); | ||
export type GetSecretsApiKeyResult = z.infer<typeof getSecretsApiKeySchema>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove schema duplication between files.
These schemas duplicate the definitions in shared.ts
(lines 60-71). Consider removing the schemas from shared.ts
and importing them from this centralized secrets module to maintain single source of truth.
# In shared.ts, remove the duplicate schemas and import from secrets:
+import { getSecretsOauthSchema, getSecretsApiKeySchema } from "./secrets";
-export const getSecretsOauthSchema = z.object({
- environment: z.string(),
- clientKey: z.string(),
- clientSecret: z.string(),
-});
-export type GetSecretsOauthResult = z.infer<typeof getSecretsOauthSchema>;
-
-export const getSecretsApiKeySchema = z.object({
- environment: z.string(),
- apiKey: z.string(),
-});
-export type GetSecretsApiKeyResult = z.infer<typeof getSecretsApiKeySchema>;
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In packages/core/src/external/ehr/secrets.ts around lines 15 to 26, the OAuth
and API key schemas are duplicated in shared.ts (lines 60-71). To fix this,
remove the schema definitions from shared.ts and instead import the existing
schemas from this secrets.ts module wherever needed. This centralizes the schema
definitions and avoids duplication.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in downstream PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@thomasyopes Perfect! Thanks for addressing the schema duplication concern in the downstream PR. That keeps the codebase cleaner and maintains a single source of truth for the schema definitions.
export async function createAthenaHealthClient({ | ||
cxId, | ||
practiceId, | ||
tokenId, | ||
}: { | ||
cxId: string; | ||
practiceId: string; | ||
tokenId?: string; | ||
}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add input validation for required parameters.
The function should validate that cxId
and practiceId
are non-empty strings to fail fast with clear error messages.
export async function createAthenaHealthClient({
cxId,
practiceId,
tokenId,
}: {
cxId: string;
practiceId: string;
tokenId?: string;
}) {
+ if (!cxId?.trim()) {
+ throw new BadRequestError("cxId is required");
+ }
+ if (!practiceId?.trim()) {
+ throw new BadRequestError("practiceId is required");
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export async function createAthenaHealthClient({ | |
cxId, | |
practiceId, | |
tokenId, | |
}: { | |
cxId: string; | |
practiceId: string; | |
tokenId?: string; | |
}) { | |
export async function createAthenaHealthClient({ | |
cxId, | |
practiceId, | |
tokenId, | |
}: { | |
cxId: string; | |
practiceId: string; | |
tokenId?: string; | |
}) { | |
if (!cxId?.trim()) { | |
throw new BadRequestError("cxId is required"); | |
} | |
if (!practiceId?.trim()) { | |
throw new BadRequestError("practiceId is required"); | |
} | |
// …rest of implementation… | |
} |
🤖 Prompt for AI Agents
In packages/core/src/external/ehr/athenahealth/shared.ts around lines 8 to 16,
the createAthenaHealthClient function lacks validation for required parameters
cxId and practiceId. Add checks to ensure cxId and practiceId are non-empty
strings at the start of the function, and throw clear errors if either is
missing or empty to enable fail-fast behavior with informative messages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We control these inputs, we don't need this validation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@thomasyopes Understood! That makes sense for internal functions where you control the inputs. I'll keep that in mind for future reviews.
(\ /)
( ._.)
o_(")(")
✏️ Learnings added
Learnt from: thomasyopes
PR: metriport/metriport#3973
File: packages/core/src/external/ehr/athenahealth/shared.ts:8-16
Timestamp: 2025-06-06T18:17:23.549Z
Learning: For internal functions in the Metriport codebase where inputs are controlled by the development team, input validation for required parameters like cxId and practiceId is not needed and should be avoided to keep functions simple.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
Issues:
Description
Testing
Check each PR.
Release Plan
master
Summary by CodeRabbit
New Features
Bug Fixes
Refactor
Chores