Skip to content

Commit b6ae427

Browse files
authored
[ BB2-1119 ] Settings configuration via JSON or YAML files (#4)
* Create SDK JSON configuration file functionality - Create `Bb2` class methods for reading settings from JSON configuration files in `src/bb2.py`. - Create `src/tests/test_configs.py` with configuration and JSON related file tests. - Create JSON test files to be used in tests under `src/tests/test_configs/json/`. - Create JSON sample configuration for SDK user in `bluebutton-sample-config.json`. - Add SDK user config files to `.gitignore` BB2-1119 * Add YAML format configuration file functionality - Add `.yaml` file extension support to `Bb2` class methods in `src/bb2.py`. - Add tests for YAML configuration files in `src/tests/test_configs.py`. - Create YAML test files to be used in tests under `src/tests/test_configs/yaml/`. - Create YAML sample configuration for SDK user in `bluebutton-sample-config.yaml`. BB2-1119 * Update config and tests per PR review * Rename Bb2 to BlueButton class * Remove sample config file to be in readme later
1 parent 891fc45 commit b6ae427

17 files changed

+258
-41
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,7 @@ bb2_env/
162162
#.idea/
163163

164164
# End of https://www.toptal.com/developers/gitignore/api/python
165+
#
166+
# BB2 ignores
167+
.bluebutton-config.json
168+
.bluebutton-config.yaml

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,4 @@ $ pip install check-manifest # If not already installed.
112112
$ check-manifest --create
113113
$ python setup.py sdist
114114
```
115+

bluebutton-sample-config.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"environment": "SANDBOX",
3+
"client_id": "<your BB2 client_id here>",
4+
"client_secret": "<your BB2 client_secret here.>",
5+
"callback_url": "https://www.fake.com/your/callback/here",
6+
"version": 2
7+
}

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"Topic :: Software Development",
2626
"Topic :: Software Development :: Libraries :: Python Modules",
2727
],
28-
py_modules=["bb2"],
28+
py_modules=["blueButton"],
2929
package_dir={"": "src"},
3030
python_requires=">=3.6",
3131
install_requires=[

src/bb2.py

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

src/bluebutton.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
"""
2+
Blue Button 2.0 SDK Class
3+
4+
"""
5+
import json
6+
import os
7+
import pathlib
8+
import yaml
9+
10+
ROOT_DIR = os.path.abspath(os.curdir) + "/"
11+
DEFAULT_CONFIG_FILE_LOCATION = ROOT_DIR + "./.bluebutton-config.json"
12+
13+
ENVIRONMENT_URLS = {
14+
"SANDBOX": "https://sandbox.bluebutton.cms.gov",
15+
"PRODUCTION": "https://api.bluebutton.cms.gov",
16+
}
17+
18+
19+
class BlueButton:
20+
name = "bb2"
21+
verbose_name = "Blue Button 2.0 SDK Package"
22+
23+
def __init__(self, config=DEFAULT_CONFIG_FILE_LOCATION):
24+
self.client_id = None
25+
self.client_secret = None
26+
self.callback_url = None
27+
self.version = 2 # Default to BB2 version 2
28+
29+
self.base_url = None
30+
31+
self.set_configuration(config)
32+
33+
def _read_json(self, file_path):
34+
with open(file_path, "r") as f:
35+
data = json.load(f)
36+
return data
37+
38+
def _read_yaml(self, file_path):
39+
with open(file_path, "r") as f:
40+
return yaml.safe_load(f)
41+
42+
def _read_config(self, config):
43+
extension = pathlib.Path(config).suffix
44+
45+
if extension == ".json":
46+
return self._read_json(config)
47+
elif extension == ".yaml":
48+
return self._read_yaml(config)
49+
else:
50+
raise ValueError(
51+
"Error: Configuration file extension must be .json"
52+
" or .yaml for: {}".format(config)
53+
)
54+
55+
def set_configuration(self, config):
56+
# Is config param a file path or dict?
57+
if isinstance(config, str):
58+
config_dict = self._read_config(config)
59+
else:
60+
config_dict = config
61+
62+
# Check environment setting
63+
env = config_dict.get("environment", None)
64+
if env in ["SANDBOX", "PRODUCTION"]:
65+
self.base_url = ENVIRONMENT_URLS.get(env, None)
66+
else:
67+
raise ValueError(
68+
"Error: Configuration environment must be set to"
69+
" SANDBOX or PRODUCTION in: {}".format(config)
70+
)
71+
72+
# Check other settings are provided
73+
for s in ["client_id", "client_secret", "callback_url"]:
74+
setting = config_dict.get(s, None)
75+
if setting is None:
76+
raise ValueError(
77+
'Error: Configuration setting "'
78+
+ s
79+
+ '" is missing in: {}'.format(config)
80+
)
81+
82+
self.client_id = config_dict.get("client_id")
83+
self.client_secret = config_dict.get("client_secret")
84+
self.callback_url = config_dict.get("callback_url")
85+
self.version = config_dict.get("version", 2)

src/test_bb2.py

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

src/tests/test_configs.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import pytest
2+
3+
from bluebutton import BlueButton
4+
5+
6+
CONFIGS_DIR = "src/tests/test_configs/"
7+
8+
9+
def test_invalid_file_extension():
10+
# Test extension not matching .json or .yaml
11+
with pytest.raises(
12+
ValueError,
13+
match=r"Error: Configuration file extension"
14+
" must be .json or .yaml for:.*",
15+
):
16+
BlueButton(config="file.xxx")
17+
18+
19+
def test_read_config_json_equals_yaml():
20+
bb = BlueButton(config=CONFIGS_DIR +
21+
"json/bluebutton-sample-config-valid-sbx.json")
22+
23+
# Test dict's are equal for both JSON & YAML
24+
assert bb._read_config(
25+
config=CONFIGS_DIR + "json/bluebutton-sample-config-valid-sbx.json"
26+
) == bb._read_config(
27+
config=CONFIGS_DIR + "yaml/bluebutton-sample-config-valid-sbx.yaml"
28+
)
29+
30+
31+
def test_valid_config():
32+
# valid config sbx
33+
bb = BlueButton(config=CONFIGS_DIR +
34+
"json/bluebutton-sample-config-valid-sbx.json")
35+
assert bb.base_url == "https://sandbox.bluebutton.cms.gov"
36+
assert bb.client_id == "<your BB2 client_id here>"
37+
assert bb.client_secret == "<your BB2 client_secret here>"
38+
assert bb.callback_url == "https://www.fake-sandbox.com/your/callback/here"
39+
assert bb.version == 1
40+
41+
# valid config prod
42+
bb = BlueButton(config=CONFIGS_DIR +
43+
"json/bluebutton-sample-config-valid-prod.json")
44+
assert bb.base_url == "https://api.bluebutton.cms.gov"
45+
assert bb.client_id == "<your BB2 client_id here>"
46+
assert bb.client_secret == "<your BB2 client_secret here>"
47+
assert bb.callback_url == "https://www.fake-prod.com/your/callback/here"
48+
assert bb.version == 1
49+
50+
# valid config from a dictionary
51+
config_dict = {
52+
"environment": "PRODUCTION",
53+
"client_id": "<your BB2 client_id here>",
54+
"client_secret": "<your BB2 client_secret here>",
55+
"callback_url": "https://www.fake-prod.com/your/callback/here",
56+
"version": 1,
57+
}
58+
bb = BlueButton(config_dict)
59+
assert bb.base_url == "https://api.bluebutton.cms.gov"
60+
assert bb.client_id == "<your BB2 client_id here>"
61+
assert bb.client_secret == "<your BB2 client_secret here>"
62+
assert bb.callback_url == "https://www.fake-prod.com/your/callback/here"
63+
assert bb.version == 1
64+
65+
66+
def test_config_setting_environment():
67+
for file_name in [
68+
"json/bluebutton-sample-config-environment-missing.json",
69+
"json/bluebutton-sample-config-environment-wrong.json",
70+
]:
71+
with pytest.raises(
72+
ValueError,
73+
match=r"Error: Configuration environment must"
74+
" be set to SANDBOX or PRODUCTION in:.*",
75+
):
76+
BlueButton(config=CONFIGS_DIR + file_name)
77+
78+
79+
def test_config_setting_missing():
80+
for setting, file_name in [
81+
["client_id", "json/bluebutton-sample-config-missing-id.json"],
82+
["client_secret", "json/bluebutton-sample-config-missing-secret.json"],
83+
["callback_url",
84+
"json/bluebutton-sample-config-missing-callback.json"],
85+
]:
86+
with pytest.raises(
87+
ValueError,
88+
match=r"Error: Configuration setting \"{}\""
89+
" is missing in:.*".format(setting),
90+
):
91+
BlueButton(config=CONFIGS_DIR + file_name)
92+
93+
94+
def test_config_version_missing_defaults_to_v2():
95+
bb = BlueButton(config=CONFIGS_DIR +
96+
"json/bluebutton-sample-config-missing-version.json")
97+
assert bb.version == 2
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"client_id": "<your BB2 client_id here>",
3+
"client_secret": "<your BB2 client_secret here>",
4+
"callback_url": "https://www.fake-sandbox.com/your/callback/here",
5+
"version": 1
6+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"environment": "WRONG",
3+
"client_id": "<your BB2 client_id here>",
4+
"client_secret": "<your BB2 client_secret here>",
5+
"callback_url": "https://www.fake-sandbox.com/your/callback/here",
6+
"version": 1
7+
}

0 commit comments

Comments
 (0)