From 5c3b7d07c4e3a976f7343ec41218c67d96b75ccc Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Thu, 13 Jan 2022 17:50:22 -0800 Subject: [PATCH 1/4] hacky draft improvements. --- client/package.json | 3 +- client/src/components/header.tsx | 2 +- client/src/components/patientData.tsx | 2 +- client/src/components/records.tsx | 243 +++++++++++++++++++++----- client/src/setupProxy.js | 2 +- docker-compose.yml | 1 + server/Dockerfile | 2 +- server/package.json | 1 + server/src/index.ts | 1 - server/src/routes/Authorize.ts | 62 ++++--- server/src/routes/Data.ts | 77 ++++---- server/src/utils/bb2.ts | 7 +- server/src/utils/db.ts | 2 + server/src/utils/request.ts | 105 +++++++++++ server/src/utils/user.ts | 2 + 15 files changed, 396 insertions(+), 116 deletions(-) create mode 100755 server/src/utils/request.ts diff --git a/client/package.json b/client/package.json index 64a4824..2f61327 100644 --- a/client/package.json +++ b/client/package.json @@ -23,7 +23,8 @@ "web-vitals": "^1.0.1" }, "scripts": { - "start": "react-scripts start", + "start": "REACT_APP_CTX=docker react-scripts start", + "start-native": "REACT_APP_CTX=native react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" diff --git a/client/src/components/header.tsx b/client/src/components/header.tsx index 8d6285e..4b1df78 100644 --- a/client/src/components/header.tsx +++ b/client/src/components/header.tsx @@ -1,7 +1,7 @@ import { Badge } from '@cmsgov/design-system'; import { Link as RouterLink } from 'react-router-dom'; -export default function Header({ }) { +export default function Header() { return (

diff --git a/client/src/components/patientData.tsx b/client/src/components/patientData.tsx index c90b3bf..2f11b92 100644 --- a/client/src/components/patientData.tsx +++ b/client/src/components/patientData.tsx @@ -4,7 +4,7 @@ import chart from '../images/who-charted.png' import { SettingsType } from '../types/settings'; import { useState } from 'react'; -export default function PatientData({ }) { +export default function PatientData() { const [header] = useState('Add your Medicare Prescription Drug data'); const [settingsState] = useState({ pkce: true, diff --git a/client/src/components/records.tsx b/client/src/components/records.tsx index 122640d..b11d790 100644 --- a/client/src/components/records.tsx +++ b/client/src/components/records.tsx @@ -8,8 +8,33 @@ export type EOBRecord = { amount: number } -export default function Records({ }) { - const [records, setRecords] = useState([]); +export type PatientRecord = { + id: string, + name: string, + gender: string, + dateOfBirth: string, + addressState: string, + addressZip: string +} + +export type CoverageRecord = { + id: string, + subscriberId: string, + status: string, + beneRef: string, + payor: string +} + +export type ErrorResponse = { + type: string, + content: string, +} + +export default function Records() { + const [eobs, setRecords] = useState([]); + const [patients, setPatients] = useState([]); + const [coverages, setCoverages] = useState([]); + const [message, setMessage] = useState(); /* * DEVELOPER NOTES: * Here we are parsing through the different PDE Claim records @@ -30,50 +55,182 @@ export default function Records({ }) { fetch('/api/data/benefit') .then(res => { return res.json(); - }).then(eobData => { - const records: EOBRecord[] = eobData.entry.map((resourceData: any) => { - const resource = resourceData.resource; - return { - id: resource.id, - code: resource.item[0]?.productOrService?.coding[0]?.code || 'Unknown', - display: resource.item[0]?.productOrService?.coding[0]?.display || 'Unknown Prescription Drug', - amount: resource.item[0]?.adjudication[7]?.amount?.value || '0' - } - }); - setRecords(records); + }).then(beneData => { + if (beneData.eobData && beneData.eobData.entry) { + const records: EOBRecord[] = beneData.eobData.entry.map((resourceData: any) => { + const resource = resourceData.resource; + return { + id: resource.id, + code: resource.item[0]?.productOrService?.coding[0]?.code || 'Unknown', + display: resource.item[0]?.productOrService?.coding[0]?.display || 'Unknown Prescription Drug', + amount: resource.item[0]?.adjudication[7]?.amount?.value || '0' + } + }); + setRecords(records); + } + + if (beneData.patient && beneData.patient.entry) { + const records: PatientRecord[] = beneData.patient.entry.map((resourceData: any) => { + const resource = resourceData.resource; + return { + id: resource.id, + name: resource?.name[0]?.family || 'Unknown', + gender: resource?.gender || 'Unknown Gender', + dateOfBirth: resource?.birthDate || 'Unknown DOB', + addressState: resource?.address[0]?.state || 'Unknown State', + addressZip: resource?.address[0]?.postalCode || 'Unknown ZIP', + } + }); + setPatients(records); + } + + if (beneData.coverage && beneData.coverage.entry) { + const records: CoverageRecord[] = beneData.coverage.entry.map((resourceData: any) => { + const resource = resourceData.resource; + return { + id: resource.id, + subscriberId: resource?.subscriberId || 'Unknown', + status: resource?.status || 'Unknown Status', + beneRef: resource?.beneficiary?.reference || 'Unknown Beneficiary', + payor: resource?.payor[0]?.identifier?.value || 'Unknown Payor', + } + }); + setCoverages(records); + } + + if (beneData.eobData && beneData.eobData.message) { + setMessage({"type": "error", "content": beneData.eobData.message || "Unknown"}) + } }); }, []) - return ( -
- - Medicare Prescription Drug Claims Data - - - NDC Code - Prescription Drug Name - Cost - - - - - {records.map(record => { - return ( - - - {record.code} - - - {record.display} - - - ${record.amount}.00 - - - ) - })} - -
+ if (message) { + return ( +
+ + Error Response + + + Type + Content + + + + + + {message.type} + + + {message.content} + + + +
+
+ ); + } else { + return ( +
+
+ + Medicare Patient Info + + + Name + Gender + Birth Date + Address State + Address ZIP + + + + {patients.map(record => { + return ( + + + {record.name} + + + {record.gender} + + + {record.dateOfBirth} + + + {record.addressState} + + + {record.addressZip} + + + ) + })} + +
+
+
+ + Medicare Coverage Info + + + Subscriber ID + Status + Beneficiary Reference + Payor + + + + {coverages.map(record => { + return ( + + + {record.subscriberId} + + + {record.status} + + + {record.beneRef} + + + {record.payor} + + + ) + })} + +
+
+
+ + Medicare Prescription Drug Claims Data + + + NDC Code + Prescription Drug Name + Cost + + + + {eobs.map(record => { + return ( + + + {record.code} + + + {record.display} + + + ${record.amount}.00 + + + ) + })} + +
+
); + } }; \ No newline at end of file diff --git a/client/src/setupProxy.js b/client/src/setupProxy.js index 128eb71..d0a50b4 100644 --- a/client/src/setupProxy.js +++ b/client/src/setupProxy.js @@ -4,7 +4,7 @@ module.exports = function(app) { app.use( '/api', createProxyMiddleware({ - target: 'http://server:3001', + target: (process.env.REACT_APP_CTX === 'docker' ? 'http://server:3001' : 'http://localhost:3001'), changeOrigin: true, }) ); diff --git a/docker-compose.yml b/docker-compose.yml index 952b09b..36d0110 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,7 @@ services: dockerfile: ./Dockerfile ports: - "3001:3001" + - "9229:9229" client: build: context: ./client diff --git a/server/Dockerfile b/server/Dockerfile index ab15b2d..da25f04 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -14,4 +14,4 @@ RUN yarn install EXPOSE 3001 -CMD ["yarn","start:dev"] \ No newline at end of file +CMD ["yarn","start:debug"] \ No newline at end of file diff --git a/server/package.json b/server/package.json index 20f94f9..e163c42 100644 --- a/server/package.json +++ b/server/package.json @@ -5,6 +5,7 @@ "build": "./node_modules/.bin/ts-node build.ts", "lint": "eslint . --ext .ts", "start": "node -r module-alias/register ./dist --env=production", + "start:debug": "node --inspect=0.0.0.0:9229 ./node_modules/.bin/ts-node -r tsconfig-paths/register ./src", "start:dev": "nodemon", "test": "nodemon --config ./spec/nodemon.json" }, diff --git a/server/src/index.ts b/server/src/index.ts index f7f4ea8..e690a52 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -2,7 +2,6 @@ import './pre-start'; // Must be the first import import app from '@server'; import logger from '@shared/Logger'; - // Start the server /* DEVELOPER NOTE: * Default values are hard coded here, but you may choose to store these values in a diff --git a/server/src/routes/Authorize.ts b/server/src/routes/Authorize.ts index a436296..3b3c34f 100644 --- a/server/src/routes/Authorize.ts +++ b/server/src/routes/Authorize.ts @@ -1,10 +1,10 @@ -import StatusCodes from 'http-status-codes'; import { Router, Request, Response } from 'express'; import AuthorizationToken from '../entities/AuthorizationToken'; import Settings from '../entities/Settings'; import db from '../utils/db'; import { getAccessToken, generateAuthorizeUrl } from '../utils/bb2'; -import { getBenefitData } from './Data'; +import { getBenefitData, getPatientData, getCoverageData, getPatientJSON, getCoverageJSON } from './Data'; +import logger from '@shared/Logger'; import { clearBB2Data, getLoggedInUser } from 'src/utils/user'; const BENE_DENIED_ACCESS = 'access_denied'; @@ -30,31 +30,42 @@ export async function authorizationCallback(req: Request, res: Response) { // this gets the token from Medicare.gov once the 'user' authenticates their Medicare.gov account const response = await getAccessToken(req.query.code?.toString(), req.query.state?.toString()); - const authToken = new AuthorizationToken(response.data); - - /* DEVELOPER NOTES: - * This is where you would most likely place some type of - * persistence service/functionality to store the token along with - * the application user identifiers - */ - - // Here we are grabbing the mocked 'user' for our application - // to be able to store the access token for that user - // thereby linking the 'user' of our sample applicaiton with their Medicare.gov account - // providing access to their Medicare data to our sample application - const loggedInUser = getLoggedInUser(db); - loggedInUser.authToken = authToken; + const loggedInUser = getLoggedInUser(db); - /* DEVELOPER NOTES: - * Here we will use the token to get the EoB data for the mocked 'user' of the sample application - * then to save trips to the BB2 API we will store it in the mocked db with the mocked 'user' - * - * You could also request data for the Patient endpoint and/or the Coverage endpoint here - * using similar functionality - */ - const eobData = await getBenefitData( req, res); - loggedInUser.eobData = eobData; + if (response.status === 200) { + const authToken = new AuthorizationToken(response.data); + + /* DEVELOPER NOTES: + * This is where you would most likely place some type of + * persistence service/functionality to store the token along with + * the application user identifiers + */ + + // Here we are grabbing the mocked 'user' for our application + // to be able to store the access token for that user + // thereby linking the 'user' of our sample applicaiton with their Medicare.gov account + // providing access to their Medicare data to our sample application + loggedInUser.authToken = authToken; + + + /* DEVELOPER NOTES: + * Here we will use the token to get the EoB data for the mocked 'user' of the sample application + * then to save trips to the BB2 API we will store it in the mocked db with the mocked 'user' + * + * You could also request data for the Patient endpoint and/or the Coverage endpoint here + * using similar functionality + */ + loggedInUser.eobData = await getBenefitData( req, res); + loggedInUser.patient = await getPatientJSON( req, res); + loggedInUser.coverage = await getCoverageJSON( req, res); + } + else { + // send generic error message to FE + loggedInUser.eobData = JSON.parse('{"message": "Unable to load EOB Data - authorization failed."}'); + loggedInUser.patient = JSON.parse('{"message": "Unable to load Patient Data - authorization failed."}'); + loggedInUser.coverage = JSON.parse('{"message": "Unable to load Coverage Data - authorization failed."}'); + } } catch (e) { /* DEVELOPER NOTES: @@ -62,6 +73,7 @@ export async function authorizationCallback(req: Request, res: Response) { * to display or store the error */ console.log(e); + logger.info(e) } /* DEVELOPER NOTE: * This is a hardcoded redirect, but this should be used from settings stored in a conf file diff --git a/server/src/routes/Data.ts b/server/src/routes/Data.ts index f9518dc..1a69ed7 100644 --- a/server/src/routes/Data.ts +++ b/server/src/routes/Data.ts @@ -1,8 +1,8 @@ import { Router, Request, Response } from 'express'; -import axios from 'axios'; import config from '../configs/config'; import db from '../utils/db'; import { getLoggedInUser } from 'src/utils/user'; +import { get } from '../utils/request' import moment from 'moment'; import { refreshAccessToken } from 'src/utils/bb2'; @@ -31,13 +31,15 @@ export async function getBenefitData(req: Request, res: Response) { loggedInUser.authToken = newAuthToken; } - const response = await axios.get(BB2_BENEFIT_URL, { - params: req.query, - headers: { - 'Authorization': `Bearer ${loggedInUser.authToken.access_token}` - } - }); - return response.data; + const response = await get(BB2_BENEFIT_URL, req.query, `${loggedInUser.authToken?.access_token}`); + + if (response.status === 200) { + return response.data; + } + else { + // send generic error to client + return JSON.parse('{"message": "Unable to load EOB Data - fetch FHIR resource error."}'); + } } /* @@ -49,52 +51,49 @@ export async function getBenefitData(req: Request, res: Response) { */ export async function getBenefitDataEndPoint(req: Request, res: Response) { const loggedInUser = getLoggedInUser(db); - const data = loggedInUser.eobData; - res.json(data); + const beneData = {'eobData': loggedInUser.eobData, 'patient': loggedInUser.patient, 'coverage': loggedInUser.coverage}; + if ( beneData ) { + res.json(beneData); + } } export async function getPatientData(req: Request, res: Response) { + // const loggedInUser = getLoggedInUser(db); + // const envConfig = config[db.settings.env]; + // get Patient end point + const data = await getPatientJSON(req, res); + res.json(data); +} + +export async function getPatientJSON(req: Request, res: Response) { const loggedInUser = getLoggedInUser(db); const envConfig = config[db.settings.env]; - const BB2_PATIENT_URL = envConfig.bb2BaseUrl + '/' + db.settings.version + '/fhir/Patient/'; - - const response = await axios.get(BB2_PATIENT_URL, { - params: req.query, - headers: { - 'Authorization': `Bearer ${loggedInUser.authToken?.access_token}` - } - }); - - res.json(response.data); + // get Patient end point + const response = await get(`${envConfig.bb2BaseUrl}/${db.settings.version}/fhir/Patient/`, req.query, `${loggedInUser.authToken?.access_token}`); + return response.data; } export async function getCoverageData(req: Request, res: Response) { + // const loggedInUser = getLoggedInUser(db); + // const envConfig = config[db.settings.env]; + // get Coverage end point + const data = await getCoverageJSON(req, res); + res.json(data); +} + +export async function getCoverageJSON(req: Request, res: Response) { const loggedInUser = getLoggedInUser(db); const envConfig = config[db.settings.env]; - const BB2_COVERAGE_URL = envConfig.bb2BaseUrl + '/' + db.settings.version + '/fhir/Coverage/'; - - const response = await axios.get(BB2_COVERAGE_URL, { - params: req.query, - headers: { - 'Authorization': `Bearer ${loggedInUser.authToken?.access_token}` - } - }); - - res.json(response.data); + // get Coverage end point + const response = await get(`${envConfig.bb2BaseUrl}/${db.settings.version}/fhir/Coverage/`, req.query, `${loggedInUser.authToken?.access_token}`); + return response.data; } export async function getUserProfileData(req: Request, res: Response) { const loggedInUser = getLoggedInUser(db); const envConfig = config[db.settings.env]; - const BB2_BENEFIT_URL = envConfig.bb2BaseUrl + '/' + db.settings.version + '/connect/userinfo'; - - const response = await axios.get(BB2_BENEFIT_URL, { - params: req.query, - headers: { - 'Authorization': `Bearer ${loggedInUser.authToken?.access_token}` - } - }); - + // get usrinfo end point + const response = await get(`${envConfig.bb2BaseUrl}/${db.settings.version}/connect/userinfo`, req.query, `${loggedInUser.authToken?.access_token}`); res.json(response.data); } diff --git a/server/src/utils/bb2.ts b/server/src/utils/bb2.ts index bad4554..0309c36 100644 --- a/server/src/utils/bb2.ts +++ b/server/src/utils/bb2.ts @@ -4,6 +4,7 @@ import FormData from 'form-data'; import db from './db'; import config from '../configs/config'; import { generateCodeChallenge, generateRandomState } from './generatePKCE'; +import { post, post_w_config } from './request' import AuthorizationToken from '@entities/AuthorizationToken'; export function generateAuthorizeUrl(): string { @@ -34,7 +35,7 @@ export async function getAccessToken(code: string, state: string | undefined) { const envConfig = config[db.settings.env]; const BB2_ACCESS_TOKEN_URL = envConfig.bb2BaseUrl + '/' + db.settings.version + '/o/token/'; - + const form = new FormData(); form.append('client_id', envConfig.bb2ClientId); form.append('client_secret', envConfig.bb2ClientSecret); @@ -47,7 +48,7 @@ export async function getAccessToken(code: string, state: string | undefined) { form.append('code_verifier', codeChallenge.verifier); form.append('code_challenge', codeChallenge.codeChallenge); } - return await axios.post(BB2_ACCESS_TOKEN_URL, form, { headers: form.getHeaders() }); + return await post(BB2_ACCESS_TOKEN_URL, form, form.getHeaders()); } export async function refreshAccessToken(refreshToken: string) { @@ -55,7 +56,7 @@ export async function refreshAccessToken(refreshToken: string) { const BB2_ACCESS_TOKEN_URL = envConfig.bb2BaseUrl + '/' + db.settings.version + '/o/token/'; - const tokenResponse = await axios({ + const tokenResponse = await post_w_config({ method: 'post', url: BB2_ACCESS_TOKEN_URL, auth: { diff --git a/server/src/utils/db.ts b/server/src/utils/db.ts index 1f78ea5..734937a 100644 --- a/server/src/utils/db.ts +++ b/server/src/utils/db.ts @@ -17,6 +17,8 @@ export interface User { authToken?: AuthorizationToken, userInfo: UserInfo, eobData?: any, + patient?: any, + coverage?: any, errors: string[] } export interface DB { diff --git a/server/src/utils/request.ts b/server/src/utils/request.ts new file mode 100755 index 0000000..e433b41 --- /dev/null +++ b/server/src/utils/request.ts @@ -0,0 +1,105 @@ +import axios from 'axios'; +import FormData from 'form-data'; + +export async function post(endpoint_url: string, data: FormData, headers: any) { + return await request({ + method: 'post', + url: endpoint_url, + data: data, + headers: headers}, true); +} + +export async function post_w_config(config: any) { + return await request(config, false); +} + +export async function get(endpointUrl: string, params: any, authToken: string) { + return await request({ + method: 'get', + url: endpointUrl, + params: params, + headers: { + 'Authorization': `Bearer ${authToken}` + }}, true); +} + +export async function request(config: any, retryFlag: boolean) { + let resp = null + try { + resp = await axios(config); + } catch (error: any) { + // DEVELOPER NOTES: + // here handle errors per ErrorResponses.md + console.log('Error message: [', error.message, ']'); + if (error.response) { + console.log("response code: " + error.response.status) + console.log("response text: " + JSON.stringify(error.response.data)) + // DEVELOPER NOTES: + // check for retryable (e.g. 500 & fhir) errors and do retrying... + if (retryFlag && isRetryable(error)) { + console.log("Request failed and is retryable, entering retry process...") + var retryResp = await do_retry(config) + if (retryResp) { + resp = retryResp; + } + } + else { + resp = error.response + } + } + else if (error.request) { + // something went wrong on sender side, not retryable + // error.request is an instance of XMLHttpRequest in the browser and an instance of + // http.ClientRequest in node.js + console.log("error.request: " + error.request); + } + // dump axios config for diagnosis + // console.log("config:") + // console.log(error.config); + } + return resp +} + +function isRetryable(error: any) { + if (error.response && error.response.status === 500) { + if (error.request.path && error.request.path.match("^/v[12]/fhir/.*")) { + return true; + } + } + return false; +} + +// for demo: retry init-interval = 5 sec, max attempt 3, with retry interval = init-interval * (2 ** n) +// where n retry attempted +async function do_retry(config: any) { + const interval = 5 + const max_attempts = 3 + var resp = null + for (let i = 0; i < max_attempts; i++) { + var wait_in_sec = interval * (2 ** i) + console.log("wait ", wait_in_sec, " seconds...") + await sleep(wait_in_sec * 1000) + console.log("retry attempts: ", i+1) + try { + resp = await axios(config); + console.log("retry successful:") + console.log(resp.data); + break; + } catch (error: any) { + console.log("retry error: [", JSON.stringify(error.message), "]") + if (error.response) { + console.log("response code: ", error.response.status) + console.log("response data: ", error.response.data) + resp = error.response + } + } + } + return resp +} + +function sleep(time: number) { + return new Promise((resolve) => { + setTimeout(resolve, time); + }); +} + \ No newline at end of file diff --git a/server/src/utils/user.ts b/server/src/utils/user.ts index 324431b..2cd9c8e 100644 --- a/server/src/utils/user.ts +++ b/server/src/utils/user.ts @@ -14,4 +14,6 @@ export function getLoggedInUser(db : DB) { export function clearBB2Data(user: User) { user.authToken = undefined; user.eobData = undefined; + user.patient = undefined; + user.coverage = undefined; } \ No newline at end of file From 46058aa65203546a2ae219f763405942cab184cf Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Fri, 14 Jan 2022 09:21:18 -0800 Subject: [PATCH 2/4] added ID column to claim, patient, coverage tables. --- client/src/components/records.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/src/components/records.tsx b/client/src/components/records.tsx index b11d790..3a7e680 100644 --- a/client/src/components/records.tsx +++ b/client/src/components/records.tsx @@ -136,6 +136,7 @@ export default function Records() { Medicare Patient Info + ID Name Gender Birth Date @@ -147,6 +148,9 @@ export default function Records() { {patients.map(record => { return ( + + {record.id} + {record.name} @@ -173,6 +177,7 @@ export default function Records() { Medicare Coverage Info + ID Subscriber ID Status Beneficiary Reference @@ -183,6 +188,9 @@ export default function Records() { {coverages.map(record => { return ( + + {record.id} + {record.subscriberId} @@ -206,6 +214,7 @@ export default function Records() { Medicare Prescription Drug Claims Data + ID NDC Code Prescription Drug Name Cost @@ -215,6 +224,9 @@ export default function Records() { {eobs.map(record => { return ( + + {record.id} + {record.code} From c03eaf6f46fbf33ead6c97ac91dd9ef9188e1338 Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Fri, 14 Jan 2022 11:03:01 -0800 Subject: [PATCH 3/4] extract diagnosis from EOB and add table for it. --- client/src/components/records.tsx | 63 +++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/client/src/components/records.tsx b/client/src/components/records.tsx index 3a7e680..005c303 100644 --- a/client/src/components/records.tsx +++ b/client/src/components/records.tsx @@ -8,6 +8,15 @@ export type EOBRecord = { amount: number } +export type DiagnosisRecord = { + id: string, + sequence: string, + diagnosisCode: string, + diagnosisDisplay: string, + typeCode: string, + typeDisplay: string +} + export type PatientRecord = { id: string, name: string, @@ -32,6 +41,7 @@ export type ErrorResponse = { export default function Records() { const [eobs, setRecords] = useState([]); + const [diagnosis, setDiagnosis] = useState([]); const [patients, setPatients] = useState([]); const [coverages, setCoverages] = useState([]); const [message, setMessage] = useState(); @@ -67,6 +77,18 @@ export default function Records() { } }); setRecords(records); + const diagnosisRecords: DiagnosisRecord[] = beneData.eobData.entry.map((resourceData: any) => { + const resource = resourceData.resource; + return { + id: resource.id, + sequence: resource.diagnosis[0]?.sequence || 'Unknown', + diagnosisCode: resource.diagnosis[0]?.diagnosisCodeableConcept?.coding[0]?.code || 'Unknown Diagnosis Coding Code', + diagnosisDisplay: resource.diagnosis[0]?.diagnosisCodeableConcept?.coding[0]?.display || 'Unknown Diagnosis Coding Display', + typeCode: resource.diagnosis[0]?.type[0]?.coding[0]?.code || 'Unknown Type Coding Code', + typeDisplay: resource.diagnosis[0]?.type[0]?.coding[0]?.display || 'Unknown Type Coding Display', + } + }); + setDiagnosis(diagnosisRecords); } if (beneData.patient && beneData.patient.entry) { @@ -242,6 +264,47 @@ export default function Records() {
+
+ + Medicare Diagnosis Data + + + ID + Sequence + Code + Name + Type Code + Type Name + + + + {diagnosis.map(record => { + return ( + + + {record.id} + + + {record.sequence} + + + {record.diagnosisCode} + + + {record.diagnosisDisplay} + + + {record.typeCode} + + + {record.typeDisplay} + + + ) + })} + +
+
); } From 9ac220d74af509e1112fd287ff7bd43df6aca2be Mon Sep 17 00:00:00 2001 From: James Fuqian Date: Fri, 14 Jan 2022 11:17:29 -0800 Subject: [PATCH 4/4] refactor data endpoints - appropriate naming. --- client/src/components/records.tsx | 2 +- server/src/routes/Authorize.ts | 4 ++-- server/src/routes/Data.ts | 7 ++++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/client/src/components/records.tsx b/client/src/components/records.tsx index 005c303..ca487b2 100644 --- a/client/src/components/records.tsx +++ b/client/src/components/records.tsx @@ -62,7 +62,7 @@ export default function Records() { * Carrier Claims, SNF, HHA, Hospice, Inpatient, and Outpatient */ useEffect(() => { - fetch('/api/data/benefit') + fetch('/api/data/beneficiary_data') .then(res => { return res.json(); }).then(beneData => { diff --git a/server/src/routes/Authorize.ts b/server/src/routes/Authorize.ts index 3b3c34f..f7a8ec3 100644 --- a/server/src/routes/Authorize.ts +++ b/server/src/routes/Authorize.ts @@ -3,7 +3,7 @@ import AuthorizationToken from '../entities/AuthorizationToken'; import Settings from '../entities/Settings'; import db from '../utils/db'; import { getAccessToken, generateAuthorizeUrl } from '../utils/bb2'; -import { getBenefitData, getPatientData, getCoverageData, getPatientJSON, getCoverageJSON } from './Data'; +import { getExplanationOfBenefitData, getPatientJSON, getCoverageJSON } from './Data'; import logger from '@shared/Logger'; import { clearBB2Data, getLoggedInUser } from 'src/utils/user'; @@ -56,7 +56,7 @@ export async function authorizationCallback(req: Request, res: Response) { * You could also request data for the Patient endpoint and/or the Coverage endpoint here * using similar functionality */ - loggedInUser.eobData = await getBenefitData( req, res); + loggedInUser.eobData = await getExplanationOfBenefitData( req, res); loggedInUser.patient = await getPatientJSON( req, res); loggedInUser.coverage = await getCoverageJSON( req, res); } diff --git a/server/src/routes/Data.ts b/server/src/routes/Data.ts index 1a69ed7..595c54b 100644 --- a/server/src/routes/Data.ts +++ b/server/src/routes/Data.ts @@ -14,7 +14,7 @@ import { refreshAccessToken } from 'src/utils/bb2'; // this function is used to query eob data for the authenticated Medicare.gov // user and returned - we are then storing in a mocked DB -export async function getBenefitData(req: Request, res: Response) { +export async function getExplanationOfBenefitData(req: Request, res: Response) { const loggedInUser = getLoggedInUser(db); const envConfig = config[db.settings.env]; const BB2_BENEFIT_URL = envConfig.bb2BaseUrl + '/' + db.settings.version + '/fhir/ExplanationOfBenefit/'; @@ -49,7 +49,7 @@ export async function getBenefitData(req: Request, res: Response) { * This would be replaced by a persistence service layer for whatever * DB you would choose to use */ -export async function getBenefitDataEndPoint(req: Request, res: Response) { +export async function getBeneficiaryDataEndPoint(req: Request, res: Response) { const loggedInUser = getLoggedInUser(db); const beneData = {'eobData': loggedInUser.eobData, 'patient': loggedInUser.patient, 'coverage': loggedInUser.coverage}; if ( beneData ) { @@ -99,7 +99,8 @@ export async function getUserProfileData(req: Request, res: Response) { const router = Router(); -router.get('/benefit', getBenefitDataEndPoint); +router.get('/beneficiary_data', getBeneficiaryDataEndPoint); +router.get('/eob', getExplanationOfBenefitData); router.get('/patient', getPatientData); router.get('/coverage', getCoverageData); router.get('/userprofile', getUserProfileData);