Skip to content

Commit ef1e4ff

Browse files
added extra method to convert credential json with no quotes
1 parent 2c73200 commit ef1e4ff

File tree

2 files changed

+106
-10
lines changed

2 files changed

+106
-10
lines changed

src/gen3_metadata/gen3_metadata_parser.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import requests
33
import pandas as pd
44
import jwt
5+
import re
56

67

78
class Gen3MetadataParser:
@@ -20,6 +21,20 @@ def __init__(self, key_file_path):
2021
self.headers = {}
2122
self.data_store = {}
2223
self.data_store_pd = {}
24+
25+
def _add_quotes_to_json(self, input_str):
26+
try:
27+
# Try parsing as-is
28+
return json.loads(input_str)
29+
except json.JSONDecodeError:
30+
# Add quotes around keys
31+
fixed = re.sub(r'([{,]\s*)(\w+)\s*:', r'\1"\2":', input_str)
32+
# Add quotes around simple string values (skip existing quoted values)
33+
fixed = re.sub(r':\s*([A-Za-z0-9._:@/-]+)(?=\s*[},])', r': "\1"', fixed)
34+
try:
35+
return json.loads(fixed)
36+
except json.JSONDecodeError as e:
37+
raise ValueError(f"Could not fix JSON: {e}")
2338

2439
def _load_api_key(self) -> dict:
2540
"""
@@ -28,8 +43,27 @@ def _load_api_key(self) -> dict:
2843
Returns:
2944
dict: The API key loaded from the JSON file.
3045
"""
31-
with open(self.key_file_path) as json_file:
32-
return json.load(json_file)
46+
try:
47+
# Read the file as plain text
48+
with open(self.key_file_path, "r") as f:
49+
content = f.read()
50+
# If the content does not contain any double or single quotes, try to fix it
51+
if '"' not in content and "'" not in content:
52+
return self._add_quotes_to_json(content)
53+
54+
# Read the file as JSON
55+
with open(self.key_file_path) as json_file:
56+
return json.load(json_file)
57+
except FileNotFoundError as fnf_err:
58+
print(f"File not found: {fnf_err}")
59+
raise
60+
except json.JSONDecodeError as json_err:
61+
print(f"JSON decode error: {json_err}")
62+
print("Please make sure the file contains valid JSON with quotes and proper formatting.")
63+
raise
64+
except Exception as err:
65+
print(f"An unexpected error occurred while loading API key: {err}")
66+
raise
3367

3468
def _url_from_jwt(self, cred: dict) -> str:
3569
"""

tests/test_gen3_metadata_parser.py

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,89 @@
99

1010
@pytest.fixture
1111
def fake_api_key():
12-
"""Fixture to provide a fake API key. Note: these credentials have been inactivated"""
12+
"""Fixture to provide a fake API key. Note: these credentials have been inactivated."""
13+
# This is a valid JWT and UUID, but is not active.
1314
return {
14-
"api_key": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImZlbmNlX2tleV9rZXkiLCJ0eXAiOiJKV1QifQ.eyJwdXIiOiJhcGlfa2V5Iiwic3ViIjoiMjEiLCJpc3MiOiJodHRwczovL2RhdGEudGVzdC5iaW9jb21tb25zLm9yZy5hdS91c2VyIiwiYXVkIjpbImh0dHBzOi8vZGF0YS50ZXN0LmJpb2NvbW1vbnMub3JnLmF1L3VzZXIiXSwiaWF0IjoxNzQyMjUzNDgwLCJleHAiOjE3NDQ4NDU0ODAsImp0aSI6ImI5MDQyNzAxLWIwOGYtNDBkYS04OWEzLTc1M2JlNGVkMTIyOSIsImF6cCI6IiIsInNjb3BlIjpbImdvb2dsZV9jcmVkZW50aWFscyIsIm9wZW5pZCIsImdvb2dsZV9zZXJ2aWNlX2FjY291bnQiLCJkYXRhIiwiZmVuY2UiLCJnb29nbGVfbGluayIsImFkbWluIiwidXNlciIsImdhNGdoX3Bhc3Nwb3J0X3YxIl19.SGPjs6ljCJbwDu-6WAnI5dN8o5467_ktcnsxRFrX_aCQNrOwSPgTCDvWEzamRmB5Oa0yB6cnjduhWRKnPWIZDal86H0etm77wilCteHF_zFl1IV6LW23AfOVOG3zB9KL6o-ZYqpSRyo0FDj0vQJzrHXPjqvQ15S6Js2sIwIa3ONTeHbR6fRecfPaLK1uGIY5tJFeigXzrLzlifKCEnt_2gqpMU2_b2QgW1315FixNIUOl8A7FZJ2-ddSMJPO0IYQ0QMSWV9-bbxie4Zjsaa1HtQYOhfXLU3vSdUOBO0btSfd6-NnWfx_-lDo5V9lkSH_aecEyew0IHBx-e7rSR5cxA",
15+
"api_key": (
16+
"eyJhbGciOiJSUzI1NiIsImtpZCI6ImZlbmNlX2tleV9rZXkiLCJ0eXAiOiJKV1QifQ."
17+
"eyJwdXIiOiJhcGlfa2V5Iiwic3ViIjoiMjEiLCJpc3MiOiJodHRwczovL2RhdGEudGVzdC5i"
18+
"aW9jb21tb25zLm9yZy5hdS91c2VyIiwiYXVkIjpbImh0dHBzOi8vZGF0YS50ZXN0LmJpb2Nv"
19+
"bW1vbnMub3JnLmF1L3VzZXIiXSwiaWF0IjoxNzQyMjUzNDgwLCJleHAiOjE3NDQ4NDU0ODAs"
20+
"Imp0aSI6ImI5MDQyNzAxLWIwOGYtNDBkYS04OWEzLTc1M2JlNGVkMTIyOSIsImF6cCI6IiIs"
21+
"InNjb3BlIjpbImdvb2dsZV9jcmVkZW50aWFscyIsIm9wZW5pZCIsImdvb2dsZV9zZXJ2aWNl"
22+
"X2FjY291bnQiLCJkYXRhIiwiZmVuY2UiLCJnb29nbGVfbGluayIsImFkbWluIiwidXNlciIs"
23+
"ImdhNGdoX3Bhc3Nwb3J0X3YxIl19."
24+
"SGPjs6ljCJbwDu-6WAnI5dN8o5467_ktcnsxRFrX_aCQNrOwSPgTCDvWEzamRmB5Oa0yB6cn"
25+
"jduhWRKnPWIZDal86H0etm77wilCteHF_zFl1IV6LW23AfOVOG3zB9KL6o-ZYqpSRyo0FDj0"
26+
"vQJzrHXPjqvQ15S6Js2sIwIa3ONTeHbR6fRecfPaLK1uGIY5tJFeigXzrLzlifKCEnt_2gqp"
27+
"MU2_b2QgW1315FixNIUOl8A7FZJ2-ddSMJPO0IYQ0QMSWV9-bbxie4Zjsaa1HtQYOhfXLU3v"
28+
"SdUOBO0btSfd6-NnWfx_-lDo5V9lkSH_aecEyew0IHBx-e7rSR5cxA"
29+
),
1530
"key_id": "b9042701-b08f-40da-89a3-753be4ed1229"
1631
}
1732

1833
@pytest.fixture
1934
def gen3_metadata_parser():
2035
"""Fixture to create a Gen3MetadataParser instance."""
21-
return Gen3MetadataParser(
22-
key_file_path="fake_credentials.json"
23-
)
36+
return Gen3MetadataParser(key_file_path="fake_credentials.json")
2437

38+
@pytest.fixture
39+
def malformed_json_credentials():
40+
"""Fixture for malformed JSON credentials (no quotes, not valid Python dict)."""
41+
# This is a string, not a dict, to simulate malformed file content.
42+
return '{api_key: abc.def.ghi, key_id: 18bdaa-b018}'
43+
44+
def test_add_quotes_to_json_valid(gen3_metadata_parser):
45+
"""Test _add_quotes_to_json with valid JSON (should parse as-is)."""
46+
valid_json = '{"api_key": "abc.def.ghi", "key_id": "18bdaa-b018"}'
47+
result = gen3_metadata_parser._add_quotes_to_json(valid_json)
48+
assert result == {"api_key": "abc.def.ghi", "key_id": "18bdaa-b018"}
49+
50+
def test_add_quotes_to_json_malformed(gen3_metadata_parser):
51+
"""Test _add_quotes_to_json with malformed JSON (no quotes)."""
52+
malformed = '{api_key: abc.def.ghi, key_id: 18bdaa-b018}'
53+
result = gen3_metadata_parser._add_quotes_to_json(malformed)
54+
assert result == {"api_key": "abc.def.ghi", "key_id": "18bdaa-b018"}
55+
56+
def test_add_quotes_to_json_url_and_uuid(gen3_metadata_parser):
57+
"""Test _add_quotes_to_json with keys/values including url and uuid."""
58+
malformed = '{key1: value1, key2:123, url: https://example.com, uuid: 18bdaa-b018}'
59+
result = gen3_metadata_parser._add_quotes_to_json(malformed)
60+
assert result == {
61+
"key1": "value1",
62+
"key2": "123",
63+
"url": "https://example.com",
64+
"uuid": "18bdaa-b018"
65+
}
66+
67+
def test_add_quotes_to_json_invalid(gen3_metadata_parser):
68+
"""Test _add_quotes_to_json with unrecoverable malformed JSON."""
69+
bad = '{key1 value1, key2:}'
70+
with pytest.raises(ValueError):
71+
gen3_metadata_parser._add_quotes_to_json(bad)
2572

26-
def test_load_api_key(gen3_metadata_parser, fake_api_key):
27-
"""Test the _load_api_key method."""
28-
# Mock open() to simulate reading the fake API key from a file
73+
def test_load_api_key_valid_json(gen3_metadata_parser, fake_api_key):
74+
"""Test the _load_api_key method with valid JSON file content."""
75+
# Simulate reading a valid JSON file
2976
with patch("builtins.open", mock_open(read_data=json.dumps(fake_api_key))):
3077
result = gen3_metadata_parser._load_api_key()
3178
assert result == fake_api_key
3279

80+
def test_load_api_key_malformed_json(gen3_metadata_parser, malformed_json_credentials):
81+
"""Test the _load_api_key method with malformed JSON (no quotes)."""
82+
# Simulate reading a malformed JSON file (no quotes)
83+
with patch("builtins.open", mock_open(read_data=malformed_json_credentials)):
84+
result = gen3_metadata_parser._load_api_key()
85+
assert result == {"api_key": "abc.def.ghi", "key_id": "18bdaa-b018"}
86+
87+
def test_load_api_key_invalid_json(gen3_metadata_parser):
88+
"""Test the _load_api_key method with unrecoverable malformed JSON."""
89+
# Simulate reading a badly malformed JSON file
90+
bad_content = '{key1 value1, key2:}'
91+
with patch("builtins.open", mock_open(read_data=bad_content)):
92+
with pytest.raises(ValueError):
93+
gen3_metadata_parser._load_api_key()
94+
3395
def test_url_from_jwt(gen3_metadata_parser, fake_api_key):
3496
"""Test if you can infer the data commons url from the JWT token"""
3597
url = gen3_metadata_parser._url_from_jwt(fake_api_key)

0 commit comments

Comments
 (0)