From 3fd1cbcc428829c2735734244558ac41564054d6 Mon Sep 17 00:00:00 2001 From: Jorge Orta <36802074+Orta21@users.noreply.github.com> Date: Mon, 15 May 2023 12:23:36 -0400 Subject: [PATCH] Metriport initial integration (#119) --- .gitignore | 2 +- .pnp.cjs | 26 ++++ ...rt-api-npm-3.1.4-cf49b64e5d-39f6623d3d.zip | Bin 0 -> 100559 bytes ...idator-npm-3.1.1-40c37f982a-7d7ca31990.zip | Bin 0 -> 9589 bytes .yarnrc.yml | 2 +- extensions/index.ts | 32 ++--- extensions/metriport/CHANGELOG.md | 1 + extensions/metriport/README.md | 3 + .../metriport/actions/document/fields.ts | 28 +++++ extensions/metriport/actions/document/list.ts | 46 ++++++++ .../metriport/actions/document/query.ts | 46 ++++++++ .../metriport/actions/document/validation.ts | 10 ++ .../metriport/actions/facility/create.ts | 54 +++++++++ .../metriport/actions/facility/dataPoints.ts | 29 +++++ .../metriport/actions/facility/fields.ts | 57 +++++++++ extensions/metriport/actions/facility/get.ts | 47 ++++++++ .../metriport/actions/facility/update.ts | 48 ++++++++ .../metriport/actions/facility/validation.ts | 17 +++ extensions/metriport/actions/index.ts | 15 +++ extensions/metriport/actions/link/create.ts | 46 ++++++++ extensions/metriport/actions/link/fields.ts | 42 +++++++ extensions/metriport/actions/link/getAll.ts | 46 ++++++++ extensions/metriport/actions/link/remove.ts | 47 ++++++++ .../metriport/actions/link/validation.ts | 25 ++++ .../metriport/actions/organization/create.ts | 52 ++++++++ .../actions/organization/dataPoints.ts | 21 ++++ .../metriport/actions/organization/fields.ts | 34 ++++++ .../metriport/actions/organization/get.ts | 46 ++++++++ .../metriport/actions/organization/update.ts | 46 ++++++++ .../actions/organization/validation.ts | 15 +++ .../metriport/actions/patient/create.ts | 111 ++++++++++++++++++ .../metriport/actions/patient/dataPoints.ts | 45 +++++++ .../metriport/actions/patient/delete.ts | 29 +++++ .../metriport/actions/patient/fields.ts | 97 +++++++++++++++ extensions/metriport/actions/patient/get.ts | 68 +++++++++++ .../metriport/actions/patient/update.ts | 38 ++++++ .../metriport/actions/patient/validation.ts | 23 ++++ extensions/metriport/client.ts | 13 ++ extensions/metriport/index.ts | 24 ++++ extensions/metriport/settings.ts | 18 +++ extensions/metriport/shared/dataPoints.ts | 28 +++++ extensions/metriport/shared/errorHandler.ts | 56 +++++++++ extensions/metriport/shared/fields.ts | 45 +++++++ .../metriport/validation/generic.zod.ts | 9 ++ .../metriport/validation/settings.zod.ts | 9 ++ package.json | 3 + yarn.lock | 24 +++- 47 files changed, 1504 insertions(+), 19 deletions(-) create mode 100644 .yarn/cache/@metriport-api-npm-3.1.4-cf49b64e5d-39f6623d3d.zip create mode 100644 .yarn/cache/driver-license-validator-npm-3.1.1-40c37f982a-7d7ca31990.zip create mode 100644 extensions/metriport/CHANGELOG.md create mode 100644 extensions/metriport/README.md create mode 100644 extensions/metriport/actions/document/fields.ts create mode 100644 extensions/metriport/actions/document/list.ts create mode 100644 extensions/metriport/actions/document/query.ts create mode 100644 extensions/metriport/actions/document/validation.ts create mode 100644 extensions/metriport/actions/facility/create.ts create mode 100644 extensions/metriport/actions/facility/dataPoints.ts create mode 100644 extensions/metriport/actions/facility/fields.ts create mode 100644 extensions/metriport/actions/facility/get.ts create mode 100644 extensions/metriport/actions/facility/update.ts create mode 100644 extensions/metriport/actions/facility/validation.ts create mode 100644 extensions/metriport/actions/index.ts create mode 100644 extensions/metriport/actions/link/create.ts create mode 100644 extensions/metriport/actions/link/fields.ts create mode 100644 extensions/metriport/actions/link/getAll.ts create mode 100644 extensions/metriport/actions/link/remove.ts create mode 100644 extensions/metriport/actions/link/validation.ts create mode 100644 extensions/metriport/actions/organization/create.ts create mode 100644 extensions/metriport/actions/organization/dataPoints.ts create mode 100644 extensions/metriport/actions/organization/fields.ts create mode 100644 extensions/metriport/actions/organization/get.ts create mode 100644 extensions/metriport/actions/organization/update.ts create mode 100644 extensions/metriport/actions/organization/validation.ts create mode 100644 extensions/metriport/actions/patient/create.ts create mode 100644 extensions/metriport/actions/patient/dataPoints.ts create mode 100644 extensions/metriport/actions/patient/delete.ts create mode 100644 extensions/metriport/actions/patient/fields.ts create mode 100644 extensions/metriport/actions/patient/get.ts create mode 100644 extensions/metriport/actions/patient/update.ts create mode 100644 extensions/metriport/actions/patient/validation.ts create mode 100644 extensions/metriport/client.ts create mode 100644 extensions/metriport/index.ts create mode 100644 extensions/metriport/settings.ts create mode 100644 extensions/metriport/shared/dataPoints.ts create mode 100644 extensions/metriport/shared/errorHandler.ts create mode 100644 extensions/metriport/shared/fields.ts create mode 100644 extensions/metriport/validation/generic.zod.ts create mode 100644 extensions/metriport/validation/settings.zod.ts diff --git a/.gitignore b/.gitignore index c69cf0909..36c542824 100644 --- a/.gitignore +++ b/.gitignore @@ -21,5 +21,5 @@ dist/* # projections file to easily jump to/from test file .projections.json -# Jetbrains ide +# Jetbrains ide .idea \ No newline at end of file diff --git a/.pnp.cjs b/.pnp.cjs index 09136ec06..8f9e6ce74 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -38,6 +38,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@graphql-tools/apollo-engine-loader", "virtual:d0a76b174a54d973a27fb74b962aa2f26df55e18a188b76dd96ab65ec80e4a0b001d63550589d8a4f2f542a16ec7ef50e51453b019e1529d4f43d2542f174b98#npm:7.3.26"],\ ["@graphql-typed-document-node/core", "virtual:d0a76b174a54d973a27fb74b962aa2f26df55e18a188b76dd96ab65ec80e4a0b001d63550589d8a4f2f542a16ec7ef50e51453b019e1529d4f43d2542f174b98#npm:3.1.2"],\ ["@mailchimp/mailchimp_transactional", "npm:1.0.50"],\ + ["@metriport/api", "npm:3.1.4"],\ ["@types/jest", "npm:29.4.0"],\ ["@types/lodash", "npm:4.14.191"],\ ["@types/mailchimp__mailchimp_transactional", "npm:1.0.5"],\ @@ -47,6 +48,8 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@typescript-eslint/eslint-plugin", "virtual:d0a76b174a54d973a27fb74b962aa2f26df55e18a188b76dd96ab65ec80e4a0b001d63550589d8a4f2f542a16ec7ef50e51453b019e1529d4f43d2542f174b98#npm:5.52.0"],\ ["axios", "npm:1.3.4"],\ ["date-fns", "npm:2.29.3"],\ + ["dayjs", "npm:1.11.7"],\ + ["driver-license-validator", "npm:3.1.1"],\ ["eslint", "npm:8.34.0"],\ ["eslint-config-prettier", "virtual:d0a76b174a54d973a27fb74b962aa2f26df55e18a188b76dd96ab65ec80e4a0b001d63550589d8a4f2f542a16ec7ef50e51453b019e1529d4f43d2542f174b98#npm:8.6.0"],\ ["eslint-config-standard-with-typescript", "virtual:d0a76b174a54d973a27fb74b962aa2f26df55e18a188b76dd96ab65ec80e4a0b001d63550589d8a4f2f542a16ec7ef50e51453b019e1529d4f43d2542f174b98#npm:34.0.0"],\ @@ -157,6 +160,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@graphql-tools/apollo-engine-loader", "virtual:d0a76b174a54d973a27fb74b962aa2f26df55e18a188b76dd96ab65ec80e4a0b001d63550589d8a4f2f542a16ec7ef50e51453b019e1529d4f43d2542f174b98#npm:7.3.26"],\ ["@graphql-typed-document-node/core", "virtual:d0a76b174a54d973a27fb74b962aa2f26df55e18a188b76dd96ab65ec80e4a0b001d63550589d8a4f2f542a16ec7ef50e51453b019e1529d4f43d2542f174b98#npm:3.1.2"],\ ["@mailchimp/mailchimp_transactional", "npm:1.0.50"],\ + ["@metriport/api", "npm:3.1.4"],\ ["@types/jest", "npm:29.4.0"],\ ["@types/lodash", "npm:4.14.191"],\ ["@types/mailchimp__mailchimp_transactional", "npm:1.0.5"],\ @@ -166,6 +170,8 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@typescript-eslint/eslint-plugin", "virtual:d0a76b174a54d973a27fb74b962aa2f26df55e18a188b76dd96ab65ec80e4a0b001d63550589d8a4f2f542a16ec7ef50e51453b019e1529d4f43d2542f174b98#npm:5.52.0"],\ ["axios", "npm:1.3.4"],\ ["date-fns", "npm:2.29.3"],\ + ["dayjs", "npm:1.11.7"],\ + ["driver-license-validator", "npm:3.1.1"],\ ["eslint", "npm:8.34.0"],\ ["eslint-config-prettier", "virtual:d0a76b174a54d973a27fb74b962aa2f26df55e18a188b76dd96ab65ec80e4a0b001d63550589d8a4f2f542a16ec7ef50e51453b019e1529d4f43d2542f174b98#npm:8.6.0"],\ ["eslint-config-standard-with-typescript", "virtual:d0a76b174a54d973a27fb74b962aa2f26df55e18a188b76dd96ab65ec80e4a0b001d63550589d8a4f2f542a16ec7ef50e51453b019e1529d4f43d2542f174b98#npm:34.0.0"],\ @@ -3405,6 +3411,17 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["@metriport/api", [\ + ["npm:3.1.4", {\ + "packageLocation": "./.yarn/cache/@metriport-api-npm-3.1.4-cf49b64e5d-39f6623d3d.zip/node_modules/@metriport/api/",\ + "packageDependencies": [\ + ["@metriport/api", "npm:3.1.4"],\ + ["axios", "npm:1.3.4"],\ + ["zod", "npm:3.21.4"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["@nodelib/fs.scandir", [\ ["npm:2.1.5", {\ "packageLocation": "./.yarn/cache/@nodelib-fs.scandir-npm-2.1.5-89c67370dd-a970d595bd.zip/node_modules/@nodelib/fs.scandir/",\ @@ -6113,6 +6130,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["driver-license-validator", [\ + ["npm:3.1.1", {\ + "packageLocation": "./.yarn/cache/driver-license-validator-npm-3.1.1-40c37f982a-7d7ca31990.zip/node_modules/driver-license-validator/",\ + "packageDependencies": [\ + ["driver-license-validator", "npm:3.1.1"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["dset", [\ ["npm:3.1.2", {\ "packageLocation": "./.yarn/cache/dset-npm-3.1.2-c711fbe49b-4f8066f517.zip/node_modules/dset/",\ diff --git a/.yarn/cache/@metriport-api-npm-3.1.4-cf49b64e5d-39f6623d3d.zip b/.yarn/cache/@metriport-api-npm-3.1.4-cf49b64e5d-39f6623d3d.zip new file mode 100644 index 0000000000000000000000000000000000000000..ff56eba1fe0b2ea41da5d7d187e499d64f0220af GIT binary patch literal 100559 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 literal 0 HcmV?d00001 diff --git a/.yarn/cache/driver-license-validator-npm-3.1.1-40c37f982a-7d7ca31990.zip b/.yarn/cache/driver-license-validator-npm-3.1.1-40c37f982a-7d7ca31990.zip new file mode 100644 index 0000000000000000000000000000000000000000..d081c42918938aad24b10c5773895135b82003bd GIT binary patch literal 9589 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 + +export const getUrlFields = { + fileName: { + id: 'fileName', + label: 'File Name', + description: 'The file name of the document', + type: FieldType.STRING, + required: true, + }, +} satisfies Record diff --git a/extensions/metriport/actions/document/list.ts b/extensions/metriport/actions/document/list.ts new file mode 100644 index 000000000..457c2aa42 --- /dev/null +++ b/extensions/metriport/actions/document/list.ts @@ -0,0 +1,46 @@ +import { type Action, type DataPointDefinition } from '../../../../lib/types' +import { Category } from '../../../../lib/types/marketplace' +import { type settings } from '../../settings' +import { createMetriportApi } from '../../client' +import { handleErrorMessage } from '../../shared/errorHandler' +import { listFields } from './fields' +import { startQuerySchema } from './validation' + +const dataPoints = { + patientId: { + key: 'patientId', + valueType: 'string', + }, +} satisfies Record + +export const queryDocs: Action< + typeof listFields, + typeof settings, + keyof typeof dataPoints +> = { + key: 'listDocs', + category: Category.EHR_INTEGRATIONS, + title: 'List Documents', + description: + 'Triggers a document query for the specified patient across HIEs.', + fields: listFields, + previewable: true, + dataPoints, + onActivityCreated: async (payload, onComplete, onError): Promise => { + try { + const { patientId, facilityId } = startQuerySchema.parse(payload.fields) + + const api = createMetriportApi(payload.settings) + + await api.startDocumentQuery(patientId, facilityId) + + await onComplete({ + data_points: { + patientId: String(patientId), + }, + }) + } catch (err) { + await handleErrorMessage(err, onError) + } + }, +} diff --git a/extensions/metriport/actions/document/query.ts b/extensions/metriport/actions/document/query.ts new file mode 100644 index 000000000..4c5281815 --- /dev/null +++ b/extensions/metriport/actions/document/query.ts @@ -0,0 +1,46 @@ +import { type Action, type DataPointDefinition } from '../../../../lib/types' +import { Category } from '../../../../lib/types/marketplace' +import { type settings } from '../../settings' +import { createMetriportApi } from '../../client' +import { handleErrorMessage } from '../../shared/errorHandler' +import { listFields } from './fields' +import { startQuerySchema } from './validation' + +const dataPoints = { + patientId: { + key: 'patientId', + valueType: 'string', + }, +} satisfies Record + +export const queryDocs: Action< + typeof listFields, + typeof settings, + keyof typeof dataPoints +> = { + key: 'queryDocs', + category: Category.EHR_INTEGRATIONS, + title: 'Start Document Query', + description: + 'Triggers a document query for the specified patient across HIEs.', + fields: listFields, + previewable: true, + dataPoints, + onActivityCreated: async (payload, onComplete, onError): Promise => { + try { + const { patientId, facilityId } = startQuerySchema.parse(payload.fields) + + const api = createMetriportApi(payload.settings) + + await api.startDocumentQuery(patientId, facilityId) + + await onComplete({ + data_points: { + patientId: String(patientId), + }, + }) + } catch (err) { + await handleErrorMessage(err, onError) + } + }, +} diff --git a/extensions/metriport/actions/document/validation.ts b/extensions/metriport/actions/document/validation.ts new file mode 100644 index 000000000..09bbbf5ff --- /dev/null +++ b/extensions/metriport/actions/document/validation.ts @@ -0,0 +1,10 @@ +import { z } from 'zod' + +export const startQuerySchema = z.object({ + patientId: z + .string({ errorMap: () => ({ message: 'Missing patientId' }) }) + .min(1), + facilityId: z + .string({ errorMap: () => ({ message: 'Missing facilityId' }) }) + .min(1), +}) diff --git a/extensions/metriport/actions/facility/create.ts b/extensions/metriport/actions/facility/create.ts new file mode 100644 index 000000000..bebcf0cf6 --- /dev/null +++ b/extensions/metriport/actions/facility/create.ts @@ -0,0 +1,54 @@ +import { type Action } from '../../../../lib/types' +import { Category } from '../../../../lib/types/marketplace' +import { type settings } from '../../settings' +import { createMetriportApi } from '../../client' +import { handleErrorMessage } from '../../shared/errorHandler' +import { facilityFields } from './fields' +import { facilityIdDataPoint } from './dataPoints' +import { facilityCreateSchema } from './validation' + +export const createFacility: Action< + typeof facilityFields, + typeof settings, + keyof typeof facilityIdDataPoint +> = { + key: 'createFacility', + category: Category.EHR_INTEGRATIONS, + title: 'Create Facility', + description: + 'Creates a Facility in Metriport where your patients receive care.', + fields: facilityFields, + previewable: true, + dataPoints: facilityIdDataPoint, + onActivityCreated: async (payload, onComplete, onError): Promise => { + try { + const facility = facilityCreateSchema.parse(payload.fields) + + const metriportFacility = { + name: facility.name, + npi: facility.npi, + tin: facility.tin, + address: { + addressLine1: facility.addressLine1, + addressLine2: facility.addressLine2, + city: facility.city, + state: facility.state, + zip: facility.zip, + country: facility.country, + }, + } + + const api = createMetriportApi(payload.settings) + + const { id } = await api.createFacility(metriportFacility) + + await onComplete({ + data_points: { + facilityId: String(id), + }, + }) + } catch (err) { + await handleErrorMessage(err, onError) + } + }, +} diff --git a/extensions/metriport/actions/facility/dataPoints.ts b/extensions/metriport/actions/facility/dataPoints.ts new file mode 100644 index 000000000..b9ea75927 --- /dev/null +++ b/extensions/metriport/actions/facility/dataPoints.ts @@ -0,0 +1,29 @@ +import { type DataPointDefinition } from '../../../../lib/types' +import { address } from '../../shared/dataPoints' + +export const facilityIdDataPoint = { + facilityId: { + key: 'facilityId', + valueType: 'string', + }, +} satisfies Record + +export const facilityDataPoints = { + name: { + key: 'name', + valueType: 'string', + }, + npi: { + key: 'npi', + valueType: 'string', + }, + active: { + key: 'active', + valueType: 'boolean', + }, + tin: { + key: 'tin', + valueType: 'string', + }, + ...address, +} satisfies Record diff --git a/extensions/metriport/actions/facility/fields.ts b/extensions/metriport/actions/facility/fields.ts new file mode 100644 index 000000000..372d3b150 --- /dev/null +++ b/extensions/metriport/actions/facility/fields.ts @@ -0,0 +1,57 @@ +import { FieldType, type Field } from '../../../../lib/types' +import { address } from '../../shared/fields' + +export const facilityFields = { + name: { + id: 'name', + label: 'Name', + description: 'The name of your organization', + type: FieldType.STRING, + required: true, + }, + npi: { + id: 'npi', + label: 'National Provider Identifier (NPI)', + description: + 'The 10 digit National Provider Identifier (NPI) that will be used to make requests on behalf of the Facility', + type: FieldType.NUMERIC, + required: true, + }, + tin: { + id: 'tin', + label: 'Tax Identification Number (TIN)', + description: + 'The 10 digit National Provider Identifier (NPI) that will be used to make requests on behalf of the Facility', + type: FieldType.NUMERIC, + required: true, + }, + active: { + id: 'active', + label: 'Active', + description: + 'Whether or not this Facility is currently active - this is usually true.', + type: FieldType.BOOLEAN, + }, + ...address, +} satisfies Record + +export const facilityWithIdFields = { + id: { + id: 'id', + label: 'Facility ID', + description: 'The ID of the facility to update', + type: FieldType.STRING, + required: true, + }, + ...facilityFields, +} satisfies Record + +export const getFields = { + facilityId: { + id: 'facilityId', + label: 'Facility ID', + description: 'The facility ID', + type: FieldType.STRING, + required: true, + }, +} satisfies Record diff --git a/extensions/metriport/actions/facility/get.ts b/extensions/metriport/actions/facility/get.ts new file mode 100644 index 000000000..45816185c --- /dev/null +++ b/extensions/metriport/actions/facility/get.ts @@ -0,0 +1,47 @@ +import { type Action } from '../../../../lib/types' +import { Category } from '../../../../lib/types/marketplace' +import { type settings } from '../../settings' +import { createMetriportApi } from '../../client' +import { handleErrorMessage } from '../../shared/errorHandler' +import { getFields } from './fields' +import { stringId } from '../../validation/generic.zod' +import { facilityDataPoints } from './dataPoints' + +export const getFacility: Action< + typeof getFields, + typeof settings, + keyof typeof facilityDataPoints +> = { + key: 'getFacility', + category: Category.EHR_INTEGRATIONS, + title: 'Get Facility', + description: 'Gets a Facility', + fields: getFields, + previewable: true, + dataPoints: facilityDataPoints, + onActivityCreated: async (payload, onComplete, onError): Promise => { + try { + const facilityId = stringId.parse(payload.fields.facilityId) + + const api = createMetriportApi(payload.settings) + const facility = await api.getFacility(facilityId) + + await onComplete({ + data_points: { + name: facility.name, + tin: facility.tin, + npi: facility.npi, + active: String(facility.active), + addressLine1: facility.address.addressLine1, + addressLine2: facility.address.addressLine2, + city: facility.address.city, + state: facility.address.state, + zip: facility.address.zip, + country: facility.address.country, + }, + }) + } catch (err) { + await handleErrorMessage(err, onError) + } + }, +} diff --git a/extensions/metriport/actions/facility/update.ts b/extensions/metriport/actions/facility/update.ts new file mode 100644 index 000000000..1131f94ed --- /dev/null +++ b/extensions/metriport/actions/facility/update.ts @@ -0,0 +1,48 @@ +import { type Action } from '../../../../lib/types' +import { Category } from '../../../../lib/types/marketplace' +import { type settings } from '../../settings' +import { createMetriportApi } from '../../client' +import { handleErrorMessage } from '../../shared/errorHandler' +import { facilityWithIdFields } from './fields' +import { facilityUpdateSchema } from './validation' + +export const updateFacility: Action< + typeof facilityWithIdFields, + typeof settings +> = { + key: 'updateFacility', + category: Category.EHR_INTEGRATIONS, + title: 'Update Facility', + description: + 'Updates a Facility in Metriport where your patients receive care.', + fields: facilityWithIdFields, + previewable: true, + onActivityCreated: async (payload, onComplete, onError): Promise => { + try { + const facility = facilityUpdateSchema.parse(payload.fields) + + const metriportFacility = { + id: facility.id, + name: facility.name, + tin: facility.tin, + npi: facility.npi, + address: { + addressLine1: facility.addressLine1, + addressLine2: facility.addressLine2, + city: facility.city, + state: facility.state, + zip: facility.zip, + country: facility.country, + }, + } + + const api = createMetriportApi(payload.settings) + + await api.updateFacility(metriportFacility) + + await onComplete() + } catch (err) { + await handleErrorMessage(err, onError) + } + }, +} diff --git a/extensions/metriport/actions/facility/validation.ts b/extensions/metriport/actions/facility/validation.ts new file mode 100644 index 000000000..edd3e2f7e --- /dev/null +++ b/extensions/metriport/actions/facility/validation.ts @@ -0,0 +1,17 @@ +import { z } from 'zod' +import { addressSchema } from '@metriport/api' + +export const facilityCreateSchema = z + .object({ + name: z.string().min(1), + npi: z.string().min(1), + tin: z.string().optional().nullable(), + active: z.boolean().optional().nullable(), + }) + .merge(addressSchema) + +export const facilityUpdateSchema = z + .object({ + id: z.string().min(1), + }) + .merge(facilityCreateSchema) diff --git a/extensions/metriport/actions/index.ts b/extensions/metriport/actions/index.ts new file mode 100644 index 000000000..e18e15fb5 --- /dev/null +++ b/extensions/metriport/actions/index.ts @@ -0,0 +1,15 @@ +import { createOrganization } from './organization/create' +import { updateOrganization } from './organization/update' +import { getOrganization } from './organization/get' +import { createFacility } from './facility/create' +import { updateFacility } from './facility/update' +import { getFacility } from './facility/get' + +export const actions = { + createOrganization, + updateOrganization, + getOrganization, + createFacility, + updateFacility, + getFacility, +} diff --git a/extensions/metriport/actions/link/create.ts b/extensions/metriport/actions/link/create.ts new file mode 100644 index 000000000..40b8096d8 --- /dev/null +++ b/extensions/metriport/actions/link/create.ts @@ -0,0 +1,46 @@ +import { type Action, type DataPointDefinition } from '../../../../lib/types' +import { Category } from '../../../../lib/types/marketplace' +import { type settings } from '../../settings' +import { createMetriportApi } from '../../client' +import { handleErrorMessage } from '../../shared/errorHandler' +import { createFields } from './fields' +import { linkCreateSchema } from './validation' + +const dataPoints = { + patientId: { + key: 'patientId', + valueType: 'string', + }, +} satisfies Record + +export const createLink: Action< + typeof createFields, + typeof settings, + keyof typeof dataPoints +> = { + key: 'createLink', + category: Category.EHR_INTEGRATIONS, + title: 'Create Link', + description: "Create an link using Metriport's API.", + fields: createFields, + previewable: true, + dataPoints, + onActivityCreated: async (payload, onComplete, onError): Promise => { + try { + const { patientId, linkSource, facilityId, entityId } = + linkCreateSchema.parse(payload.fields) + + const api = createMetriportApi(payload.settings) + + await api.createLink(patientId, facilityId, entityId, linkSource) + + await onComplete({ + data_points: { + patientId: String(patientId), + }, + }) + } catch (err) { + await handleErrorMessage(err, onError) + } + }, +} diff --git a/extensions/metriport/actions/link/fields.ts b/extensions/metriport/actions/link/fields.ts new file mode 100644 index 000000000..d15807d7a --- /dev/null +++ b/extensions/metriport/actions/link/fields.ts @@ -0,0 +1,42 @@ +import { FieldType, type Field } from '../../../../lib/types' + +export const getAllFields = { + patientId: { + id: 'patientId', + label: 'Patient ID', + description: + 'The ID of the Patient that will be linked to the entity in the HIE', + type: FieldType.STRING, + required: true, + }, + facilityId: { + id: 'facilityId', + label: 'Facility ID', + description: + 'The ID of the Facility that is currently providing the Patient care', + type: FieldType.STRING, + required: true, + }, +} satisfies Record + +export const removeFields = { + ...getAllFields, + linkSource: { + id: 'linkSource', + label: 'Link Source', + description: 'The HIE to link to - currently COMMONWELL is supported', + type: FieldType.STRING, + required: true, + }, +} satisfies Record + +export const createFields = { + ...removeFields, + entityId: { + id: 'entityId', + label: 'Entity ID', + description: 'The ID of the entity in the HIE to link the Patient to', + type: FieldType.STRING, + required: true, + }, +} satisfies Record diff --git a/extensions/metriport/actions/link/getAll.ts b/extensions/metriport/actions/link/getAll.ts new file mode 100644 index 000000000..c5f42e4b8 --- /dev/null +++ b/extensions/metriport/actions/link/getAll.ts @@ -0,0 +1,46 @@ +import { type Action, type DataPointDefinition } from '../../../../lib/types' +import { Category } from '../../../../lib/types/marketplace' +import { type settings } from '../../settings' +import { createMetriportApi } from '../../client' +import { handleErrorMessage } from '../../shared/errorHandler' +import { getAllFields } from './fields' +import { getAllLinksSchema } from './validation' + +// NEED TO SEE WHILE TESTING +const dataPoints = { + patientId: { + key: 'patientId', + valueType: 'string', + }, +} satisfies Record + +export const getAllLinks: Action< + typeof getAllFields, + typeof settings, + keyof typeof dataPoints +> = { + key: 'getAllLinks', + category: Category.EHR_INTEGRATIONS, + title: 'Get All Links', + description: "Get all links using Metriport's API.", + fields: getAllFields, + previewable: true, + dataPoints, + onActivityCreated: async (payload, onComplete, onError): Promise => { + try { + const { patientId, facilityId } = getAllLinksSchema.parse(payload.fields) + + const api = createMetriportApi(payload.settings) + + await api.listLinks(patientId, facilityId) + + await onComplete({ + data_points: { + patientId: String(patientId), + }, + }) + } catch (err) { + await handleErrorMessage(err, onError) + } + }, +} diff --git a/extensions/metriport/actions/link/remove.ts b/extensions/metriport/actions/link/remove.ts new file mode 100644 index 000000000..9158e6728 --- /dev/null +++ b/extensions/metriport/actions/link/remove.ts @@ -0,0 +1,47 @@ +import { type Action, type DataPointDefinition } from '../../../../lib/types' +import { Category } from '../../../../lib/types/marketplace' +import { type settings } from '../../settings' +import { createMetriportApi } from '../../client' +import { handleErrorMessage } from '../../shared/errorHandler' +import { removeFields } from './fields' +import { linkRemoveSchema } from './validation' + +const dataPoints = { + patientId: { + key: 'patientId', + valueType: 'string', + }, +} satisfies Record + +export const removeLink: Action< + typeof removeFields, + typeof settings, + keyof typeof dataPoints +> = { + key: 'removeLink', + category: Category.EHR_INTEGRATIONS, + title: 'Remove Link', + description: "Remove an link using Metriport's API.", + fields: removeFields, + previewable: true, + dataPoints, + onActivityCreated: async (payload, onComplete, onError): Promise => { + try { + const { patientId, linkSource, facilityId } = linkRemoveSchema.parse( + payload.fields + ) + + const api = createMetriportApi(payload.settings) + + await api.removeLink(patientId, facilityId, linkSource) + + await onComplete({ + data_points: { + patientId: String(patientId), + }, + }) + } catch (err) { + await handleErrorMessage(err, onError) + } + }, +} diff --git a/extensions/metriport/actions/link/validation.ts b/extensions/metriport/actions/link/validation.ts new file mode 100644 index 000000000..725693926 --- /dev/null +++ b/extensions/metriport/actions/link/validation.ts @@ -0,0 +1,25 @@ +import { z } from 'zod' +import { MedicalDataSource } from '@metriport/api' + +export const getAllLinksSchema = z.object({ + patientId: z + .string({ errorMap: () => ({ message: 'Missing patientId' }) }) + .min(1), + facilityId: z + .string({ errorMap: () => ({ message: 'Missing facilityId' }) }) + .min(1), +}) + +export const linkRemoveSchema = z + .object({ + linkSource: z.nativeEnum(MedicalDataSource), + }) + .merge(getAllLinksSchema) + +export const linkCreateSchema = z + .object({ + entityId: z + .string({ errorMap: () => ({ message: 'Missing entityId' }) }) + .min(1), + }) + .merge(linkRemoveSchema) diff --git a/extensions/metriport/actions/organization/create.ts b/extensions/metriport/actions/organization/create.ts new file mode 100644 index 000000000..45b49901a --- /dev/null +++ b/extensions/metriport/actions/organization/create.ts @@ -0,0 +1,52 @@ +import { type Action } from '../../../../lib/types' +import { Category } from '../../../../lib/types/marketplace' +import { type settings } from '../../settings' +import { createMetriportApi } from '../../client' +import { handleErrorMessage } from '../../shared/errorHandler' +import { orgFields } from './fields' +import { orgIdDataPoint } from './dataPoints' +import { orgCreateSchema } from './validation' + +export const createOrganization: Action< + typeof orgFields, + typeof settings, + keyof typeof orgIdDataPoint +> = { + key: 'createOrganization', + category: Category.EHR_INTEGRATIONS, + title: 'Create Organization', + description: 'Registers your Organization in Metriport.', + fields: orgFields, + previewable: true, + dataPoints: orgIdDataPoint, + onActivityCreated: async (payload, onComplete, onError): Promise => { + try { + const organization = orgCreateSchema.parse(payload.fields) + + const metriportOrg = { + name: organization.name, + type: organization.type, + location: { + addressLine1: organization.addressLine1, + addressLine2: organization.addressLine2, + city: organization.city, + state: organization.state, + zip: organization.zip, + country: organization.country, + }, + } + + const api = createMetriportApi(payload.settings) + + const { id } = await api.createOrganization(metriportOrg) + + await onComplete({ + data_points: { + organizationId: String(id), + }, + }) + } catch (err) { + await handleErrorMessage(err, onError) + } + }, +} diff --git a/extensions/metriport/actions/organization/dataPoints.ts b/extensions/metriport/actions/organization/dataPoints.ts new file mode 100644 index 000000000..e787f8257 --- /dev/null +++ b/extensions/metriport/actions/organization/dataPoints.ts @@ -0,0 +1,21 @@ +import { type DataPointDefinition } from '../../../../lib/types' +import { address } from '../../shared/dataPoints' + +export const orgIdDataPoint = { + organizationId: { + key: 'organizationId', + valueType: 'string', + }, +} satisfies Record + +export const orgDataPoints = { + type: { + key: 'type', + valueType: 'string', + }, + name: { + key: 'name', + valueType: 'string', + }, + ...address, +} satisfies Record diff --git a/extensions/metriport/actions/organization/fields.ts b/extensions/metriport/actions/organization/fields.ts new file mode 100644 index 000000000..4df9360b8 --- /dev/null +++ b/extensions/metriport/actions/organization/fields.ts @@ -0,0 +1,34 @@ +import { FieldType, type Field } from '../../../../lib/types' +import { address } from '../../shared/fields' + +export const orgFields = { + name: { + id: 'name', + label: 'Name', + description: 'The name of your organization', + type: FieldType.STRING, + required: true, + }, + type: { + id: 'type', + label: 'Type', + description: + 'The type of your organization, can be one of: acuteCare, ambulatory, hospital, labSystems, pharmacy, postAcuteCare', + type: FieldType.STRING, + required: true, + }, + ...address, +} satisfies Record + +export const orgWithIdFields = { + id: { + id: 'id', + label: 'Organization ID', + description: 'The ID of the organization to update', + type: FieldType.STRING, + required: true, + }, + ...orgFields, +} satisfies Record + +export const getFields = {} satisfies Record diff --git a/extensions/metriport/actions/organization/get.ts b/extensions/metriport/actions/organization/get.ts new file mode 100644 index 000000000..78aeaacb4 --- /dev/null +++ b/extensions/metriport/actions/organization/get.ts @@ -0,0 +1,46 @@ +import { type Action } from '../../../../lib/types' +import { Category } from '../../../../lib/types/marketplace' +import { type settings } from '../../settings' +import { createMetriportApi } from '../../client' +import { handleErrorMessage } from '../../shared/errorHandler' +import { type getFields } from './fields' +import { orgDataPoints } from './dataPoints' + +export const getOrganization: Action< + typeof getFields, + typeof settings, + keyof typeof orgDataPoints +> = { + key: 'getOrganization', + category: Category.EHR_INTEGRATIONS, + title: 'Get Organization', + description: 'Gets the Organization representing your legal corporate entity', + fields: {}, + previewable: true, + dataPoints: orgDataPoints, + onActivityCreated: async (payload, onComplete, onError): Promise => { + try { + const api = createMetriportApi(payload.settings) + const organization = await api.getOrganization() + + if (organization !== undefined) { + await onComplete({ + data_points: { + type: organization.type, + name: organization.name, + addressLine1: organization.location.addressLine1, + addressLine2: organization.location.addressLine2, + city: organization.location.city, + state: organization.location.state, + zip: organization.location.zip, + country: organization.location.country, + }, + }) + } + + throw new Error('Organization not found') + } catch (err) { + await handleErrorMessage(err, onError) + } + }, +} diff --git a/extensions/metriport/actions/organization/update.ts b/extensions/metriport/actions/organization/update.ts new file mode 100644 index 000000000..a6316c8a9 --- /dev/null +++ b/extensions/metriport/actions/organization/update.ts @@ -0,0 +1,46 @@ +import { type Action } from '../../../../lib/types' +import { Category } from '../../../../lib/types/marketplace' +import { type settings } from '../../settings' +import { createMetriportApi } from '../../client' +import { handleErrorMessage } from '../../shared/errorHandler' +import { orgWithIdFields } from './fields' +import { orgUpdateSchema } from './validation' + +export const updateOrganization: Action< + typeof orgWithIdFields, + typeof settings +> = { + key: 'updateOrganization', + category: Category.EHR_INTEGRATIONS, + title: 'Update Organization', + description: "Updates your Organization's details.", + fields: orgWithIdFields, + previewable: true, + onActivityCreated: async (payload, onComplete, onError): Promise => { + try { + const organization = orgUpdateSchema.parse(payload.fields) + + const metriportOrg = { + id: organization.id, + name: organization.name, + type: organization.type, + location: { + addressLine1: organization.addressLine1, + addressLine2: organization.addressLine2, + city: organization.city, + state: organization.state, + zip: organization.zip, + country: organization.country, + }, + } + + const api = createMetriportApi(payload.settings) + + await api.updateOrganization(metriportOrg) + + await onComplete() + } catch (err) { + await handleErrorMessage(err, onError) + } + }, +} diff --git a/extensions/metriport/actions/organization/validation.ts b/extensions/metriport/actions/organization/validation.ts new file mode 100644 index 000000000..09a9b769b --- /dev/null +++ b/extensions/metriport/actions/organization/validation.ts @@ -0,0 +1,15 @@ +import { z } from 'zod' +import { addressSchema, orgTypeSchema } from '@metriport/api' + +export const orgCreateSchema = z + .object({ + name: z.string().min(1), + type: orgTypeSchema, + }) + .merge(addressSchema) + +export const orgUpdateSchema = z + .object({ + id: z.string().min(1), + }) + .merge(orgCreateSchema) diff --git a/extensions/metriport/actions/patient/create.ts b/extensions/metriport/actions/patient/create.ts new file mode 100644 index 000000000..2778dfc2b --- /dev/null +++ b/extensions/metriport/actions/patient/create.ts @@ -0,0 +1,111 @@ +import { + type PatientCreate as MetriportPatientCreate, + usStateSchema, +} from '@metriport/api' +import { isValid } from 'driver-license-validator' +import { type Action } from '../../../../lib/types' +import { Category } from '../../../../lib/types/marketplace' +import { type settings } from '../../settings' +import { createMetriportApi } from '../../client' +import { handleErrorMessage } from '../../shared/errorHandler' +import { createFields } from './fields' +import { stringId } from '../../validation/generic.zod' +import { type PatientCreate, patientCreateSchema } from './validation' +import { patientIdDataPoint } from './dataPoints' + +export const createPatient: Action< + typeof createFields, + typeof settings, + keyof typeof patientIdDataPoint +> = { + key: 'createPatient', + category: Category.EHR_INTEGRATIONS, + title: 'Create Patient', + description: + 'Creates a Patient in Metriport for the specified Facility where the patient is receiving care.', + fields: createFields, + previewable: true, + dataPoints: patientIdDataPoint, + onActivityCreated: async (payload, onComplete, onError): Promise => { + try { + const patient = patientCreateSchema.parse(payload.fields) + + const facilityId = stringId.parse(payload.fields.facilityId) + + const patientMetriport = convertToMetriportPatient(patient) + + const api = createMetriportApi(payload.settings) + + const { id } = await api.createPatient(patientMetriport, facilityId) + + await onComplete({ + data_points: { + patientId: String(id), + }, + }) + } catch (err) { + await handleErrorMessage(err, onError) + } + }, +} + +export const convertToMetriportPatient = ( + patient: PatientCreate +): MetriportPatientCreate => { + const patientMetriport: MetriportPatientCreate = { + firstName: patient.firstName, + lastName: patient.lastName, + dob: patient.dob, + genderAtBirth: patient.genderAtBirth, + address: { + addressLine1: patient.addressLine1, + addressLine2: patient.addressLine2, + city: patient.city, + state: patient.state, + zip: patient.zip, + country: patient.country, + }, + personalIdentifiers: [], + contact: { + phone: patient.phone, + email: patient.email, + }, + } + + if ( + patient.driversLicenseState !== undefined && + patient.driversLicenseValue === undefined + ) { + throw new Error( + 'Drivers license value is required when drivers license state is provided' + ) + } else if ( + patient.driversLicenseState === undefined && + patient.driversLicenseValue !== undefined + ) { + throw new Error( + 'Drivers license state is required when drivers license value is provided' + ) + } + + if ( + patient.driversLicenseState !== undefined && + patient.driversLicenseState.length > 0 && + patient.driversLicenseValue !== undefined && + patient.driversLicenseValue.length > 0 + ) { + const valid = isValid(patient.driversLicenseValue, { + states: patient.driversLicenseState, + }) + + if (valid) { + ;(patientMetriport.personalIdentifiers ?? []).push({ + type: 'driversLicense', + value: patient.driversLicenseValue, + state: usStateSchema.parse(patient.driversLicenseState), + }) + } + } + + return patientMetriport +} diff --git a/extensions/metriport/actions/patient/dataPoints.ts b/extensions/metriport/actions/patient/dataPoints.ts new file mode 100644 index 000000000..91dfafb4c --- /dev/null +++ b/extensions/metriport/actions/patient/dataPoints.ts @@ -0,0 +1,45 @@ +import { type DataPointDefinition } from '../../../../lib/types' +import { address } from '../../shared/dataPoints' + +export const patientIdDataPoint = { + patientId: { + key: 'patientId', + valueType: 'string', + }, +} satisfies Record + +export const patientDataPoints = { + firstName: { + key: 'firstName', + valueType: 'string', + }, + lastName: { + key: 'lastName', + valueType: 'string', + }, + dob: { + key: 'dob', + valueType: 'date', + }, + genderAtBirth: { + key: 'genderAtBirth', + valueType: 'string', + }, + driversLicenseValue: { + key: 'driversLicenseValue', + valueType: 'string', + }, + driversLicenseState: { + key: 'driversLicenseState', + valueType: 'string', + }, + ...address, + phone: { + key: 'phone', + valueType: 'string', + }, + email: { + key: 'email', + valueType: 'string', + }, +} satisfies Record diff --git a/extensions/metriport/actions/patient/delete.ts b/extensions/metriport/actions/patient/delete.ts new file mode 100644 index 000000000..94cf5a4e5 --- /dev/null +++ b/extensions/metriport/actions/patient/delete.ts @@ -0,0 +1,29 @@ +import { type Action } from '../../../../lib/types' +import { Category } from '../../../../lib/types/marketplace' +import { type settings } from '../../settings' +import { createMetriportApi } from '../../client' +import { handleErrorMessage } from '../../shared/errorHandler' +import { stringId } from '../../validation/generic.zod' +import { deleteFields } from './fields' + +export const deletePatient: Action = { + key: 'deletePatient', + category: Category.EHR_INTEGRATIONS, + title: 'Delete Patient', + description: 'Removes the specified Patient.', + fields: deleteFields, + previewable: true, + onActivityCreated: async (payload, onComplete, onError): Promise => { + try { + const patientId = stringId.parse(payload.fields.patientId) + const facilityId = stringId.parse(payload.fields.facilityId) + + const api = createMetriportApi(payload.settings) + await api.deletePatient(patientId, facilityId) + + await onComplete() + } catch (err) { + await handleErrorMessage(err, onError) + } + }, +} diff --git a/extensions/metriport/actions/patient/fields.ts b/extensions/metriport/actions/patient/fields.ts new file mode 100644 index 000000000..ce90c3aa2 --- /dev/null +++ b/extensions/metriport/actions/patient/fields.ts @@ -0,0 +1,97 @@ +import { FieldType, type Field } from '../../../../lib/types' +import { address } from '../../shared/fields' + +export const createFields = { + facilityId: { + id: 'facilityId', + label: 'Facility ID', + description: `The ID of the facility to create the Patient in`, + type: FieldType.STRING, + required: true, + }, + firstName: { + id: 'firstName', + label: 'First Name', + description: `The Patient's first name`, + type: FieldType.STRING, + required: true, + }, + lastName: { + id: 'lastName', + label: 'Last Name', + description: `The Patient's last name`, + type: FieldType.STRING, + required: true, + }, + dob: { + id: 'dob', + label: 'Date of Birth', + description: `The Patient's date of birth (DOB), formatted YYYY-MM-DD`, + type: FieldType.STRING, + required: true, + }, + genderAtBirth: { + id: 'genderAtBirth', + label: 'Gender at Birth', + description: `The Patient's gender at birth, can be one of M or F`, + type: FieldType.STRING, + required: true, + }, + driversLicenseValue: { + id: 'driversLicenseValue', + label: 'Drivers License Value', + description: `The Patient's driver's license number`, + type: FieldType.STRING, + }, + driversLicenseState: { + id: 'driversLicenseState', + label: 'Drivers License State', + description: `The 2 letter state acronym where this ID was issued, for example: CA`, + type: FieldType.STRING, + }, + ...address, + phone: { + id: 'phone', + label: 'Phone', + description: `The Patient's 10 digit phone number, formatted 1234567899`, + type: FieldType.NUMERIC, + }, + email: { + id: 'email', + label: 'Email', + description: `The Patient's email address`, + type: FieldType.STRING, + }, +} satisfies Record + +export const updateFields = { + id: { + id: 'id', + label: 'Patient ID', + description: 'The ID of the patient to update', + type: FieldType.STRING, + required: true, + }, + ...createFields, +} satisfies Record + +export const getFields = { + patientId: { + id: 'patientId', + label: 'Patient ID', + description: 'The patient ID', + type: FieldType.STRING, + required: true, + }, +} satisfies Record + +export const deleteFields = { + ...getFields, + facilityId: { + id: 'facilityId', + label: 'Facility ID', + description: 'The facility ID', + type: FieldType.STRING, + required: true, + }, +} diff --git a/extensions/metriport/actions/patient/get.ts b/extensions/metriport/actions/patient/get.ts new file mode 100644 index 000000000..fcd1fd34e --- /dev/null +++ b/extensions/metriport/actions/patient/get.ts @@ -0,0 +1,68 @@ +import { type Action } from '../../../../lib/types' +import { Category } from '../../../../lib/types/marketplace' +import { type settings } from '../../settings' +import { createMetriportApi } from '../../client' +import { handleErrorMessage } from '../../shared/errorHandler' +import { getFields } from './fields' +import { stringId } from '../../validation/generic.zod' +import { patientDataPoints } from './dataPoints' +import { isNil } from 'lodash' + +export const getPatient: Action< + typeof getFields, + typeof settings, + keyof typeof patientDataPoints +> = { + key: 'getPatient', + category: Category.EHR_INTEGRATIONS, + title: 'Get Patient', + description: 'Gets a Patient.', + fields: getFields, + previewable: true, + dataPoints: patientDataPoints, + onActivityCreated: async (payload, onComplete, onError): Promise => { + try { + const patientId = stringId.parse(payload.fields.patientId) + + const api = createMetriportApi(payload.settings) + const patient = await api.getPatient(patientId) + + if (isNil(patient.personalIdentifiers)) { + throw new Error('Patient does not have any personal identifiers.') + } + + if (Array.isArray(patient.address)) { + patient.address = patient.address[0] + } + + if (Array.isArray(patient.contact)) { + patient.contact = patient.contact[0] + } + + const driversLicense = patient.personalIdentifiers.find( + (id) => id.type === 'driversLicense' + ) + + await onComplete({ + data_points: { + firstName: patient.firstName, + lastName: patient.lastName, + dob: patient.dob, + genderAtBirth: patient.genderAtBirth, + driversLicenseValue: driversLicense?.value, + driversLicenseState: driversLicense?.state, + addressLine1: patient.address.addressLine1, + addressLine2: patient.address.addressLine2, + city: patient.address.city, + state: patient.address.state, + zip: patient.address.zip, + country: patient.address.country, + phone: patient.contact?.phone, + email: patient.contact?.email, + }, + }) + } catch (err) { + await handleErrorMessage(err, onError) + } + }, +} diff --git a/extensions/metriport/actions/patient/update.ts b/extensions/metriport/actions/patient/update.ts new file mode 100644 index 000000000..dcd21fb33 --- /dev/null +++ b/extensions/metriport/actions/patient/update.ts @@ -0,0 +1,38 @@ +import { type Action } from '../../../../lib/types' +import { Category } from '../../../../lib/types/marketplace' +import { type settings } from '../../settings' +import { createMetriportApi } from '../../client' +import { handleErrorMessage } from '../../shared/errorHandler' +import { updateFields } from './fields' +import { stringId } from '../../validation/generic.zod' +import { patientUpdateSchema } from './validation' +import { convertToMetriportPatient } from './create' + +export const updatePatient: Action = { + key: 'updatePatient', + category: Category.EHR_INTEGRATIONS, + title: 'Update Patient', + description: 'Updates the specified Patient.', + fields: updateFields, + previewable: true, + onActivityCreated: async (payload, onComplete, onError): Promise => { + try { + const patient = patientUpdateSchema.parse(payload.fields) + + const facilityId = stringId.parse(payload.fields.facilityId) + + const metriportPatient = convertToMetriportPatient(patient) + + const api = createMetriportApi(payload.settings) + + await api.updatePatient( + { id: patient.id, ...metriportPatient }, + facilityId + ) + + await onComplete() + } catch (err) { + await handleErrorMessage(err, onError) + } + }, +} diff --git a/extensions/metriport/actions/patient/validation.ts b/extensions/metriport/actions/patient/validation.ts new file mode 100644 index 000000000..07a7a6d83 --- /dev/null +++ b/extensions/metriport/actions/patient/validation.ts @@ -0,0 +1,23 @@ +import * as z from 'zod' +import { addressSchema, genderAtBirthSchema } from '@metriport/api' + +export const patientCreateSchema = z + .object({ + firstName: z.string().min(1), + lastName: z.string().min(1), + dob: z.string().length(10), // YYYY-MM-DD + genderAtBirth: genderAtBirthSchema, + driversLicenseState: z.string().optional(), + driversLicenseValue: z.string().optional(), + phone: z.string().optional(), + email: z.string().email().optional(), + }) + .merge(addressSchema) + +export type PatientCreate = z.infer + +export const patientUpdateSchema = z + .object({ + id: z.string().min(1), + }) + .merge(patientCreateSchema) diff --git a/extensions/metriport/client.ts b/extensions/metriport/client.ts new file mode 100644 index 000000000..380bd431d --- /dev/null +++ b/extensions/metriport/client.ts @@ -0,0 +1,13 @@ +import { MetriportMedicalApi } from '@metriport/api' +import { type settings } from './settings' +import { settingsSchema } from './validation/settings.zod' + +export const createMetriportApi = ( + payloadSettings: Record +): MetriportMedicalApi => { + const { apiKey, baseUrl } = settingsSchema.parse(payloadSettings) + + return new MetriportMedicalApi(apiKey, { + baseAddress: baseUrl, + }) +} diff --git a/extensions/metriport/index.ts b/extensions/metriport/index.ts new file mode 100644 index 000000000..d1bbb8837 --- /dev/null +++ b/extensions/metriport/index.ts @@ -0,0 +1,24 @@ +import { actions } from './actions' +import { type Extension } from '../../lib/types' +import { settings } from './settings' +import { AuthorType, Category } from '../../lib/types/marketplace' +// import { webhooks } from './webhooks' + +export const Metriport: Extension = { + key: 'metriport', + title: 'Metriport', + description: + 'Metriport helps digital health companies access and manage health and medical data, through a single universal API.', + + // TODO: WE NEED JUST THE ICON NO NAME 60X60 + icon_url: + 'https://uploads-ssl.webflow.com/63e8455460afc21f779ddb79/63e845b3e00682d54cfd92fc_metriport-logo-p-500.png', + category: Category.EHR_INTEGRATIONS, + author: { + authorType: AuthorType.EXTERNAL, + authorName: 'Metriport', + }, + settings, + actions, + // webhooks, +} diff --git a/extensions/metriport/settings.ts b/extensions/metriport/settings.ts new file mode 100644 index 000000000..d143b72a6 --- /dev/null +++ b/extensions/metriport/settings.ts @@ -0,0 +1,18 @@ +import { type Setting } from '../../lib/types' + +export const settings = { + apiKey: { + key: 'api_key', + label: 'API Key', + obfuscated: true, + description: 'The API Key for the Metriport Medical API.', + required: true, + }, + baseUrl: { + key: 'base_url', + label: 'Base URL', + obfuscated: false, + description: 'The base URL of the Metriport Medical API.', + required: false, + }, +} satisfies Record diff --git a/extensions/metriport/shared/dataPoints.ts b/extensions/metriport/shared/dataPoints.ts new file mode 100644 index 000000000..348590813 --- /dev/null +++ b/extensions/metriport/shared/dataPoints.ts @@ -0,0 +1,28 @@ +import { type DataPointDefinition } from '../../../lib/types' + +export const address = { + addressLine1: { + key: 'addressLine1', + valueType: 'string', + }, + addressLine2: { + key: 'addressLine2', + valueType: 'string', + }, + city: { + key: 'city', + valueType: 'string', + }, + state: { + key: 'state', + valueType: 'string', + }, + zip: { + key: 'zip', + valueType: 'string', + }, + country: { + key: 'country', + valueType: 'string', + }, +} satisfies Record diff --git a/extensions/metriport/shared/errorHandler.ts b/extensions/metriport/shared/errorHandler.ts new file mode 100644 index 000000000..28d1124f8 --- /dev/null +++ b/extensions/metriport/shared/errorHandler.ts @@ -0,0 +1,56 @@ +import { fromZodError } from 'zod-validation-error' +import { ZodError } from 'zod' +import { AxiosError } from 'axios' +import { type OnErrorCallback } from '../../../lib/types' + +export const handleErrorMessage = async ( + err: any, + onError: OnErrorCallback +): Promise => { + if (err instanceof ZodError) { + const error = fromZodError(err) + await onError({ + events: [ + { + date: new Date().toISOString(), + text: { en: error.message }, + error: { + category: 'WRONG_INPUT', + message: error.message, + }, + }, + ], + }) + } else if (err instanceof AxiosError) { + await onError({ + events: [ + { + date: new Date().toISOString(), + text: { + en: `${err.status ?? '(no status code)'} Error: ${err.message}`, + }, + error: { + category: 'SERVER_ERROR', + message: `${err.status ?? '(no status code)'} Error: ${ + err.message + }`, + }, + }, + ], + }) + } else { + const message = (err as Error).message + await onError({ + events: [ + { + date: new Date().toISOString(), + text: { en: message }, + error: { + category: 'SERVER_ERROR', + message, + }, + }, + ], + }) + } +} diff --git a/extensions/metriport/shared/fields.ts b/extensions/metriport/shared/fields.ts new file mode 100644 index 000000000..aa9ff5231 --- /dev/null +++ b/extensions/metriport/shared/fields.ts @@ -0,0 +1,45 @@ +import { FieldType, type Field } from '../../../lib/types' + +export const address = { + addressLine1: { + id: 'addressLine1', + label: 'Address Line 1', + description: 'The address', + type: FieldType.STRING, + required: true, + }, + addressLine2: { + id: 'addressLine2', + label: 'Address Line 2', + description: 'The address details', + type: FieldType.STRING, + }, + city: { + id: 'city', + label: 'City', + description: 'The city', + type: FieldType.STRING, + required: true, + }, + state: { + id: 'state', + label: 'State', + description: 'The 2 letter state acronym, for example: CA', + type: FieldType.STRING, + required: true, + }, + zip: { + id: 'zip', + label: 'Zip', + description: '5 digit zip code', + type: FieldType.STRING, + required: true, + }, + country: { + id: 'country', + label: 'Country', + description: 'Must be “USA”', + type: FieldType.STRING, + required: true, + }, +} satisfies Record diff --git a/extensions/metriport/validation/generic.zod.ts b/extensions/metriport/validation/generic.zod.ts new file mode 100644 index 000000000..4965eac8d --- /dev/null +++ b/extensions/metriport/validation/generic.zod.ts @@ -0,0 +1,9 @@ +import { z } from 'zod' + +/** + * stringId is a REQUIRED field, so please use a z.coerce...optional() for non- + * required numbers + */ +export const stringId = z.coerce.string({ + invalid_type_error: 'Requires a valid ID', +}) diff --git a/extensions/metriport/validation/settings.zod.ts b/extensions/metriport/validation/settings.zod.ts new file mode 100644 index 000000000..ca0522965 --- /dev/null +++ b/extensions/metriport/validation/settings.zod.ts @@ -0,0 +1,9 @@ +import { z } from 'zod' + +export const settingsSchema = z.object({ + baseUrl: z + .string({ errorMap: () => ({ message: 'Missing baseUrl' }) }) + .min(1) + .optional(), + apiKey: z.string({ errorMap: () => ({ message: 'Missing apiKey' }) }).min(1), +}) diff --git a/package.json b/package.json index a080c025f..6e56fbf97 100644 --- a/package.json +++ b/package.json @@ -46,8 +46,11 @@ "@fastify/cors": "^8.2.1", "@google-cloud/pubsub": "^3.4.1", "@mailchimp/mailchimp_transactional": "^1.0.50", + "@metriport/api": "^3.1.4", "axios": "^1.3.4", "date-fns": "^2.29.3", + "dayjs": "^1.11.7", + "driver-license-validator": "^3.1.1", "fastify": "^4.13.0", "form-data": "^4.0.0", "graphql": "^16.6.0", diff --git a/yarn.lock b/yarn.lock index 32de378d3..2cad5d5f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -67,6 +67,7 @@ __metadata: "@graphql-tools/apollo-engine-loader": ^7.3.26 "@graphql-typed-document-node/core": ^3.1.2 "@mailchimp/mailchimp_transactional": ^1.0.50 + "@metriport/api": ^3.1.4 "@types/jest": ^29.4.0 "@types/lodash": ^4.14.191 "@types/mailchimp__mailchimp_transactional": ^1.0.5 @@ -76,6 +77,8 @@ __metadata: "@typescript-eslint/eslint-plugin": ^5.52.0 axios: ^1.3.4 date-fns: ^2.29.3 + dayjs: ^1.11.7 + driver-license-validator: ^3.1.1 eslint: ^8.34.0 eslint-config-prettier: ^8.6.0 eslint-config-standard-with-typescript: ^34.0.0 @@ -2099,6 +2102,16 @@ __metadata: languageName: node linkType: hard +"@metriport/api@npm:^3.1.4": + version: 3.1.4 + resolution: "@metriport/api@npm:3.1.4" + dependencies: + axios: ^1.3.4 + zod: ^3.20.2 + checksum: 39f6623d3d8561a0177f89f4b3e31b50bc2d9eb37818d04db49686b9258bc1227219a6a3739bd0a670beb7bdeaf49f0b0d31225477d7ecd9425eb42a4bc53d1d + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -4119,7 +4132,7 @@ __metadata: languageName: node linkType: hard -"dayjs@npm:^1.8.29": +"dayjs@npm:^1.11.7, dayjs@npm:^1.8.29": version: 1.11.7 resolution: "dayjs@npm:1.11.7" checksum: 5003a7c1dd9ed51385beb658231c3548700b82d3548c0cfbe549d85f2d08e90e972510282b7506941452c58d32136d6362f009c77ca55381a09c704e9f177ebb @@ -4308,6 +4321,13 @@ __metadata: languageName: node linkType: hard +"driver-license-validator@npm:^3.1.1": + version: 3.1.1 + resolution: "driver-license-validator@npm:3.1.1" + checksum: 7d7ca31990a5608d9c759075b912f5b40e822090ddb8fa7b9889a782374291e071f028b84a5e22107c902beba42a89f95768336a599c51512516baa01a01ed9c + languageName: node + linkType: hard + "dset@npm:^3.1.2": version: 3.1.2 resolution: "dset@npm:3.1.2" @@ -10383,7 +10403,7 @@ __metadata: languageName: node linkType: hard -"zod@npm:^3.21.4": +"zod@npm:^3.20.2, zod@npm:^3.21.4": version: 3.21.4 resolution: "zod@npm:3.21.4" checksum: f185ba87342ff16f7a06686767c2b2a7af41110c7edf7c1974095d8db7a73792696bcb4a00853de0d2edeb34a5b2ea6a55871bc864227dace682a0a28de33e1f