Fix: Improve data handling and error logging

This commit improves the application's robustness by enhancing data handling and adding error logging.

Specifically, it:
- Updates the project version to 1.0.4 in `pyproject.toml` and `setup.py`.
- Removes redis from requirements.txt
- Improves data parsing in `KeyService.parse_hash_data` to handle None values and simplify the logic.
- Adds error logging to `KeyService.parse_hash_data` and `KeyService.process_list_values` to catch and log parsing errors.
- Modifies `KeyService.process_list_values` to ensure the return value is always a list.
- Adds try-except blocks in `get_keys` and `get_key_value` routes to handle potential exceptions during key processing and log warnings.
This commit is contained in:
Jacob Schmidt 2025-04-13 16:48:32 -05:00
parent 8774cad94d
commit aa144c1349
5 changed files with 91 additions and 69 deletions

View File

@ -37,21 +37,23 @@ def get_keys():
for key in keys:
key_type = DatabaseClient.get_key_type(client, key)
if key_type == "string":
value = client.string_ops.string_get(key)
if value is not None:
strings.append({"key": key, "value": value})
elif key_type == "list":
value = client.list_ops.list_range(key, 0, -1)
if value is not None:
is_email = 'email' in key.lower()
processed_value = KeyService.process_list_values(value, is_email)
lists.append({"key": key, "value": processed_value})
elif key_type == "hash":
value = client.hash_ops.hash_get_all(key)
if value is not None:
processed_value = KeyService.parse_hash_data(value)
hashes.append({"key": key, "value": processed_value})
try:
if key_type == "string":
value = client.string_ops.string_get(key)
if value is not None:
strings.append({"key": key, "value": value})
elif key_type == "list":
list_values = client.list_ops.list_range(key, 0, -1)
if list_values is not None:
processed_value = KeyService.process_list_values(list_values)
lists.append({"key": key, "value": processed_value})
elif key_type == "hash":
hash_values = client.hash_ops.hash_get_all(key)
if hash_values is not None:
processed_value = KeyService.parse_hash_data(hash_values)
hashes.append({"key": key, "value": processed_value})
except Exception as e:
logger.warning(f"Error processing key {key}: {e}")
return jsonify({
'success': True,
@ -90,15 +92,22 @@ def get_key_value(key):
}), 404
value = None
if key_type == "string":
value = client.string_ops.string_get(key)
elif key_type == "hash":
value = client.hash_ops.hash_get_all(key)
value = KeyService.parse_hash_data(value)
elif key_type == "list":
value = client.list_ops.list_range(key, 0, -1)
is_email = 'email' in key.lower()
value = KeyService.process_list_values(value, is_email)
try:
if key_type == "string":
value = client.string_ops.string_get(key)
elif key_type == "hash":
hash_values = client.hash_ops.hash_get_all(key)
value = KeyService.parse_hash_data(hash_values)
elif key_type == "list":
list_values = client.list_ops.list_range(key, 0, -1)
value = KeyService.process_list_values(list_values)
except Exception as e:
logger.warning(f"Error processing key {key}: {e}")
return jsonify({
'success': False,
'error': f'Error processing key: {str(e)}',
'connection_status': True
}), 500
return jsonify({
'success': True,

View File

@ -3,33 +3,40 @@ from .logger import logger
class KeyService:
@staticmethod
def parse_hash_data(hash_data):
properly_parsed_hash = {}
"""
Process hash data from Redis.
if isinstance(hash_data, dict):
all_pairs = []
for k in hash_data.keys():
if '=' in k:
all_pairs.append(k)
for v in hash_data.values():
if isinstance(v, str) and '=' in v:
all_pairs.append(v)
Args:
hash_data: Hash data from Redis, should be a dictionary
for pair in all_pairs:
if '=' in pair:
field, value = pair.split('=', 1)
properly_parsed_hash[field.strip()] = KeyService._parse_value(value.strip())
Returns:
Processed dictionary of hash fields and values
"""
if hash_data is None:
return {}
elif isinstance(hash_data, str):
lines = hash_data.strip().split('\n')
for line in lines:
if '=' in line:
field, value = line.split('=', 1)
properly_parsed_hash[field.strip()] = KeyService._parse_value(value.strip())
return properly_parsed_hash
try:
if isinstance(hash_data, dict):
return {k: KeyService._parse_value(v) for k, v in hash_data.items()}
return hash_data
except Exception as e:
logger.error(f"Error parsing hash data: {e}")
return {} if hash_data is None else hash_data
@staticmethod
def _parse_value(value):
"""
Parse a value, handling special formats like arrays.
Args:
value: The value to parse
Returns:
Parsed value
"""
if not isinstance(value, str):
return value
if value.startswith('[') and value.endswith(']'):
try:
array_str = value[1:-1]
@ -42,28 +49,35 @@ class KeyService:
@staticmethod
def clean_redis_value(value):
if isinstance(value, str) and value.startswith(('+', '-', ':', '$', '*')):
"""
Clean a Redis value by removing protocol prefixes.
Args:
value: The Redis value to clean
Returns:
Cleaned value
"""
if isinstance(value, str) and value.startswith(('+', '-', ':', '`', '*')):
return value[1:].strip()
return value
@staticmethod
def format_list(values):
formatted_items = []
for i in range(0, len(values), 2):
if i + 1 < len(values):
field = KeyService.clean_redis_value(values[i])
value = KeyService.clean_redis_value(values[i + 1])
field = field.capitalize()
formatted_items.append(f"{field}: {value}")
return formatted_items
@staticmethod
def process_list_values(values, is_email=False):
if is_email and len(values) >= 2 and len(values) % 2 == 0:
return KeyService.format_list(values)
else:
cleaned_values = []
for v in values:
cleaned_v = KeyService.clean_redis_value(v)
cleaned_values.append(cleaned_v)
return cleaned_values
def process_list_values(values):
"""
Process list values.
Args:
values: List of values to process
Returns:
Processed list of values
"""
if values is None:
return []
try:
return list(values) # Just ensure it's a list
except Exception as e:
logger.error(f"Error processing list values: {e}")
return values if isinstance(values, list) else []

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "firefly-viewer"
version = "1.0.3"
version = "1.0.4"
description = "A web-based viewer for Firefly databases"
readme = "README.md"
authors = [{ name = "IDSolutions", email = "info@innovativedevsolutions.org" }]

View File

@ -1,5 +1,4 @@
Flask
redis
python-dotenv
ifireflylib
click

View File

@ -8,7 +8,7 @@ with open("requirements.txt", "r", encoding="utf-8") as fh:
setup(
name="firefly-viewer",
version="1.0.3",
version="1.0.4",
author="IDSolutions",
author_email="info@innovativedevsolutions.org",
description="A web-based viewer for Firefly databases",