Skip to content

Commit 2276bd4

Browse files
author
James Fuqian
authored
Merge pull request #14 from CMSgov/jfuqian/BB2-1386-Python-SDK-Configurable-Retry-Params
[BB2-1386] Python SDK expose retry parameters as configurable
2 parents 28152f7 + ef3941e commit 2276bd4

6 files changed

Lines changed: 68 additions & 6 deletions

File tree

README.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ The configuration parameters are:
3636
- The app's callback url
3737
- The version number of the API
3838
- The app's environment (the BB2.0 web location where the app is registered)
39+
- The FHIR call retry settings
3940

4041
| Parameter | Value | Comments |
4142
| ------------ | ----------------------- | ------------------------------- |
@@ -48,6 +49,23 @@ The configuration parameters are:
4849
For application registration and client id and client secret, please refer to:
4950
[Blue Button 2.0 API Docs - Try the API](https://bluebutton.cms.gov/developers/#try-the-api)
5051

52+
FHIR requests retry:
53+
54+
Retry is enabled by default for FHIR requests, retry_settings: parameters for exponential back off retry algorithm
55+
56+
| retry parameter | value (default) | Comments |
57+
| ----------------- | -------------------- | -------------------------------- |
58+
| backoff_factor | 5 | back off factor in seconds |
59+
| total | 3 | max retries |
60+
| status_forcelist | [500, 502, 503, 504] | error response codes to retry on |
61+
62+
the exponential back off factor (in seconds) is used to calculate interval between retries by below formular, where i starts from 0:
63+
64+
backoff factor * (2 ** (i - 1))
65+
66+
e.g. for backoff_factor is 5 seconds, it will generate wait intervals: 2.5, 5, 10, ...
67+
68+
to disable the retry: set total = 0
5169

5270
There are three ways to configure the SDK when instantiating a `BlueButton` class instance:
5371

@@ -62,6 +80,11 @@ There are three ways to configure the SDK when instantiating a `BlueButton` clas
6280
"client_secret": "bar",
6381
"callback_url": "https://www.fake.com/callback",
6482
"version": 2,
83+
"retry_settings": {
84+
"total": 3,
85+
"backoff_factor": 5,
86+
"status_forcelist": [500, 502, 503, 504]
87+
}
6588
}
6689
```
6790
* JSON config file:
@@ -79,7 +102,12 @@ There are three ways to configure the SDK when instantiating a `BlueButton` clas
79102
"client_id": "foo",
80103
"client_secret": "bar",
81104
"callback_url": "https://www.fake.com/callback",
82-
"version": 2
105+
"version": 2,
106+
"retry_settings": {
107+
"total": 3,
108+
"backoff_factor": 5,
109+
"status_forcelist": [500, 502, 503, 504]
110+
}
83111
}
84112
```
85113

cms_bluebutton/auth.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ def set_dict(self, auth_token_dict):
4242
auth_token_dict.get("expires_at")
4343
).astimezone(datetime.timezone.utc)
4444
else:
45-
self.expires_at = datetime.datetime.now(datetime.timezone.utc)
46-
+datetime.timedelta(seconds=self.expires_in)
45+
self.expires_at = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(seconds=self.expires_in)
4746

4847
self.patient = auth_token_dict.get("patient")
4948
self.refresh_token = auth_token_dict.get("refresh_token")

cms_bluebutton/cms_bluebutton.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ def __init__(self, config=DEFAULT_CONFIG_FILE_LOCATION):
2828
self.client_secret = None
2929
self.callback_url = None
3030
self.version = 2 # Default to BB2 version 2
31+
# initilized with default
32+
self.retry_config = {"total": 3,
33+
"backoff_factor": 5,
34+
"status_forcelist": [500, 502, 503, 504]}
3135

3236
self.base_url = None
3337

@@ -88,6 +92,12 @@ def set_configuration(self, config):
8892
self.version = config_dict.get("version", 2)
8993
self.auth_base_url = "{}/v{}/o/authorize".format(self.base_url, self.version)
9094
self.auth_token_url = "{}/v{}/o/token/".format(self.base_url, self.version)
95+
retrycfg = config_dict.get("retry_settings")
96+
if retrycfg:
97+
# override default with normalization
98+
self.retry_config["total"] = retrycfg.get("total", 3)
99+
self.retry_config["backoff_factor"] = retrycfg.get("backoff_factor", 5)
100+
self.retry_config["status_forcelist"] = retrycfg.get("status_forcelist", [500, 502, 503, 504])
91101

92102
def get_patient_data(self, config):
93103
config["url"] = FHIR_RESOURCE_TYPE["Patient"]

cms_bluebutton/fhir_request.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import requests
22
from requests.adapters import HTTPAdapter, Retry
3-
3+
from .auth import refresh_auth_token
44
from .constants import SDK_HEADERS
55

66

@@ -12,7 +12,9 @@ def fhir_request(bb, config):
1212
auth_token = new_auth_token
1313

1414
retry_config = Retry(
15-
total=3, backoff_factor=5, status_forcelist=[500, 502, 503, 504]
15+
total=bb.retry_config.get("total"),
16+
backoff_factor=bb.retry_config.get("backoff_factor"),
17+
status_forcelist=bb.retry_config.get("status_forcelist")
1618
)
1719
full_url = "{}/v{}/{}".format(bb.base_url, bb.version, config["url"])
1820
headers = SDK_HEADERS
@@ -28,6 +30,6 @@ def fhir_request(bb, config):
2830

2931
def handle_expired(bb, auth_token):
3032
if auth_token.access_token_expired():
31-
return bb.refresh_auth_token(auth_token)
33+
return refresh_auth_token(bb, auth_token)
3234
else:
3335
return None

cms_bluebutton/tests/test_configs.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,17 @@ def test_valid_config():
5959
assert bb.client_secret == "<your BB2 client_secret here>"
6060
assert bb.callback_url == "https://www.fake-prod.com/your/callback/here"
6161
assert bb.version == 1
62+
assert bb.retry_config.get("total") == 3
63+
assert bb.retry_config.get("backoff_factor") == 5
64+
assert bb.retry_config.get("status_forcelist") == [500, 502, 503, 504]
65+
66+
67+
def test_valid_config_w_retry():
68+
# valid config sbx
69+
bb = BlueButton(config=CONFIGS_DIR + "json/bluebutton-sample-config-valid-w-retry.json")
70+
assert bb.retry_config.get("total") == 7
71+
assert bb.retry_config.get("backoff_factor") == 10
72+
assert bb.retry_config.get("status_forcelist") == [500, 502]
6273

6374

6475
def test_config_setting_environment():
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"environment": "PRODUCTION",
3+
"client_id": "<your BB2 client_id here>",
4+
"client_secret": "<your BB2 client_secret here>",
5+
"callback_url": "https://www.fake-prod.com/your/callback/here",
6+
"version": 1,
7+
"retry_settings": {
8+
"total": 7,
9+
"backoff_factor": 10,
10+
"status_forcelist": [500, 502]
11+
}
12+
}

0 commit comments

Comments
 (0)