Skip to content

Commit cd56db8

Browse files
author
James Fuqian
committed
changes per feedbacks.
1 parent 850490a commit cd56db8

12 files changed

Lines changed: 129 additions & 244 deletions

File tree

README.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,7 @@ Usage Examples
6363
To start the sample in Docker :
6464

6565
1. go to the base directory of the repo
66-
2. start.sh docker
67-
68-
To start the sample in native OS (e.g. Linux) :
69-
70-
1. go to the base directory of the repo
71-
2. start.sh native
66+
2. docker-compose up
7267

7368
To start the sample in native OS (e.g. Linux) with server and client components started in separate windows :
7469

client/src/components/header.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Badge } from '@cmsgov/design-system';
22
import { Link as RouterLink } from 'react-router-dom';
33

4-
export default function Header({ }) {
4+
export default function Header() {
55
return (
66
<header className="ds-u-padding--3 ds-u-sm-padding--6 ds-u-display--block ds-u-fill--primary-darkest">
77
<h1 className="ds-u-margin--0 ds-u-color--white ds-u-font-size--display ds-u-text-align--center">

client/src/components/patientData.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import chart from '../images/who-charted.png'
44
import { SettingsType } from '../types/settings';
55
import { useState } from 'react';
66

7-
export default function PatientData({ }) {
7+
export default function PatientData() {
88
const [header] = useState('Add your Medicare Prescription Drug data');
99
const [settingsState] = useState<SettingsType>({
1010
pkce: true,

client/src/components/records.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export type EOBRecord = {
88
amount: number
99
}
1010

11-
export default function Records({ }) {
11+
export default function Records() {
1212
const [records, setRecords] = useState<EOBRecord[]>([]);
1313
/*
1414
* DEVELOPER NOTES:

server/src/index.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import './pre-start'; // Must be the first import
22
import app from '@server';
33
import logger from '@shared/Logger';
4-
import { schedule } from './utils/retry';
5-
6-
// start retry cron job
7-
logger.info('Schedule retry cron job...');
8-
schedule();
94

105
// Start the server
116
/* DEVELOPER NOTE:

server/src/routes/Data.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Router, Request, Response } from 'express';
22
import config from '../configs/config';
33
import db from '../utils/db';
44
import { getLoggedInUser } from 'src/utils/user';
5-
import { endpointGet } from '../utils/call'
5+
import { get } from '../utils/request'
66

77
/* DEVELOPER NOTES:
88
* This is our mocked Data Service layer for both the BB2 API
@@ -16,8 +16,8 @@ export async function getBenefitData(req: Request, res: Response) {
1616
const loggedInUser = getLoggedInUser(db);
1717
const envConfig = config[db.settings.env];
1818
// get EOB end point
19-
const response = await endpointGet(envConfig.bb2BaseUrl + '/' + db.settings.version + '/fhir/ExplanationOfBenefit/', req.query, `${loggedInUser.authToken?.access_token}`);
20-
return response.data;
19+
const response = await get(envConfig.bb2BaseUrl + '/' + db.settings.version + '/fhir/ExplanationOfBenefit/', req.query, `${loggedInUser.authToken?.access_token}`);
20+
return (response) ? response.data : null;
2121
}
2222

2323
/*
@@ -37,23 +37,23 @@ export async function getPatientData(req: Request, res: Response) {
3737
const loggedInUser = getLoggedInUser(db);
3838
const envConfig = config[db.settings.env];
3939
// get Patient end point
40-
const response = await endpointGet(envConfig.bb2BaseUrl + '/' + db.settings.version + '/fhir/Patient/', req.query, `${loggedInUser.authToken?.access_token}`);
40+
const response = await get(envConfig.bb2BaseUrl + '/' + db.settings.version + '/fhir/Patient/', req.query, `${loggedInUser.authToken?.access_token}`);
4141
res.json(response.data);
4242
}
4343

4444
export async function getCoverageData(req: Request, res: Response) {
4545
const loggedInUser = getLoggedInUser(db);
4646
const envConfig = config[db.settings.env];
4747
// get Coverage end point
48-
const response = await endpointGet(envConfig.bb2BaseUrl + '/' + db.settings.version + '/fhir/Coverage/', req.query, `${loggedInUser.authToken?.access_token}`);
48+
const response = await get(envConfig.bb2BaseUrl + '/' + db.settings.version + '/fhir/Coverage/', req.query, `${loggedInUser.authToken?.access_token}`);
4949
res.json(response.data);
5050
}
5151

5252
export async function getUserProfileData(req: Request, res: Response) {
5353
const loggedInUser = getLoggedInUser(db);
5454
const envConfig = config[db.settings.env];
5555
// get usrinfo end point
56-
const response = await endpointGet(envConfig.bb2BaseUrl + '/' + db.settings.version + '/connect/userinfo', req.query, `${loggedInUser.authToken?.access_token}`);
56+
const response = await get(envConfig.bb2BaseUrl + '/' + db.settings.version + '/connect/userinfo', req.query, `${loggedInUser.authToken?.access_token}`);
5757
res.json(response.data);
5858
}
5959

server/src/utils/bb2.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import FormData from 'form-data';
33
import db from './db';
44
import config from '../configs/config';
55
import { generateCodeChallenge, generateRandomState } from './generatePKCE';
6-
import { endpointPost } from './call'
6+
import { post } from './call'
77

88
export function generateAuthorizeUrl(): string {
99
const envConfig = config[db.settings.env];
@@ -33,7 +33,6 @@ export async function getAccessToken(code: string, state: string | undefined) {
3333
const envConfig = config[db.settings.env];
3434

3535
const BB2_ACCESS_TOKEN_URL = envConfig.bb2BaseUrl + '/' + db.settings.version + '/o/token/';
36-
// const BB2_ACCESS_TOKEN_URL = 'http://host.docker.internal:8000/' + db.settings.version + '/o/token/';
3736

3837
const form = new FormData();
3938
form.append('client_id', envConfig.bb2ClientId);
@@ -48,5 +47,5 @@ export async function getAccessToken(code: string, state: string | undefined) {
4847
form.append('code_challenge', codeChallenge.codeChallenge);
4948
}
5049

51-
return await endpointPost(BB2_ACCESS_TOKEN_URL, form, form.getHeaders());
50+
return await post(BB2_ACCESS_TOKEN_URL, form, form.getHeaders());
5251
}

server/src/utils/call.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
import axios from 'axios';
22
import FormData from 'form-data';
3-
import { try_later } from './queue';
4-
import { is_retryable } from './retry';
53
import logger from '@shared/Logger';
64

7-
export async function endpointPost(endpoint_url: string, data: FormData, extra: any) {
8-
return await httpCall({
5+
export async function post(endpoint_url: string, data: FormData, extra: any) {
6+
return await request({
97
method: 'post',
108
url: endpoint_url,
119
data: data,
1210
headers: extra}, true);
1311
}
1412

15-
export async function endpointGet(endpoint_url: string, req_qry: any, token: string) {
16-
return await httpCall({
13+
export async function get(endpoint_url: string, req_qry: any, token: string) {
14+
return await request({
1715
method: 'get',
1816
url: endpoint_url,
1917
params: req_qry,
@@ -22,7 +20,7 @@ export async function endpointGet(endpoint_url: string, req_qry: any, token: str
2220
}}, true);
2321
}
2422

25-
export async function httpCall(config: any, retry_flag: boolean) {
23+
export async function request(config: any, retry_flag: boolean) {
2624
var resp = null
2725
try {
2826
resp = await axios(config);
@@ -39,7 +37,7 @@ export async function httpCall(config: any, retry_flag: boolean) {
3937
// DEVELOPER NOTES:
4038
// check for retryable (500) errors and enqueue it for retry
4139
if (retry_flag && is_retryable(error)) {
42-
try_later(error);
40+
// try_later(error);
4341
logger.info("Request failed and is retryable, saved for retry later.")
4442
}
4543
}
@@ -59,4 +57,13 @@ export async function httpCall(config: any, retry_flag: boolean) {
5957
logger.info(error.config);
6058
}
6159
return resp
62-
}
60+
}
61+
62+
function is_retryable(error: any) {
63+
if (error.response && error.response.status === 500) {
64+
if (error.request.path && error.request.path.match("^/v[12]/fhir/.*")) {
65+
return true;
66+
}
67+
}
68+
return false;
69+
}

server/src/utils/queue.ts

Lines changed: 0 additions & 59 deletions
This file was deleted.

server/src/utils/request.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import axios from 'axios';
2+
import FormData from 'form-data';
3+
import logger from '@shared/Logger';
4+
5+
export async function post(endpoint_url: string, data: FormData, extra: any) {
6+
return await request({
7+
method: 'post',
8+
url: endpoint_url,
9+
data: data,
10+
headers: extra}, true);
11+
}
12+
13+
export async function get(endpoint_url: string, req_qry: any, token: string) {
14+
return await request({
15+
method: 'get',
16+
url: endpoint_url,
17+
params: req_qry,
18+
headers: {
19+
'Authorization': `Bearer ${token}`
20+
}}, true);
21+
}
22+
23+
export async function request(config: any, retry_flag: boolean) {
24+
var resp = null
25+
try {
26+
resp = await axios(config);
27+
} catch (error: any) {
28+
// DEVELOPER NOTES:
29+
// here handle errors per errors.md
30+
console.log('Error message: [', error.message, ']');
31+
if (error.response) {
32+
console.log("response code: " + error.response.status)
33+
console.log("response text: " + error.response.data)
34+
// DEVELOPER NOTES:
35+
// check for retryable (e.g. 500 & fhir) errors and do retrying...
36+
if (retry_flag && is_retryable(error)) {
37+
console.log("Request failed and is retryable, entering retry process...")
38+
var retry_resp = await do_retry(config)
39+
if (retry_resp) {
40+
resp = retry_resp;
41+
}
42+
}
43+
else {
44+
resp = error.response
45+
}
46+
}
47+
else if (error.request) {
48+
// something went wrong on sender side, not retryable
49+
// error.request is an instance of XMLHttpRequest in the browser and an instance of
50+
// http.ClientRequest in node.js
51+
console.log("error.request: " + error.request);
52+
}
53+
// dump axios config for diagnosis
54+
console.log("config:")
55+
console.log(error.config);
56+
}
57+
return resp
58+
}
59+
60+
function is_retryable(error: any) {
61+
if (error.response && error.response.status === 500) {
62+
if (error.request.path && error.request.path.match("^/v[12]/fhir/.*")) {
63+
return true;
64+
}
65+
}
66+
return false;
67+
}
68+
69+
// for demo: retry init-interval = 5 sec, max attempt 3, with retry interval = init-interval * (2 ** n)
70+
// where n retry attempted
71+
async function do_retry(config: any) {
72+
const interval = 5
73+
const max_attempts = 3
74+
var resp = null
75+
for (let i = 0; i < max_attempts; i++) {
76+
var wait_in_sec = interval * (2 ** i)
77+
console.log("wait ", wait_in_sec, " seconds...")
78+
await sleep(wait_in_sec * 1000)
79+
console.log("retry attempts: ", i+1)
80+
try {
81+
resp = await axios(config);
82+
console.log("retry successful:")
83+
console.log(resp.data);
84+
break;
85+
} catch (error: any) {
86+
console.log("retry error: [", error.message, "]")
87+
if (error.response) {
88+
console.log("response code: ", error.response.status)
89+
console.log("response data: ", error.response.data)
90+
}
91+
}
92+
}
93+
return resp
94+
}
95+
96+
function sleep(ms: any) {
97+
return new Promise((resolve) => {
98+
setTimeout(resolve, ms);
99+
});
100+
}
101+

0 commit comments

Comments
 (0)