From aa144c13494abb35fd6d54bd2c0a6f8201c746ce Mon Sep 17 00:00:00 2001 From: Jacob Schmidt Date: Sun, 13 Apr 2025 16:48:32 -0500 Subject: [PATCH] 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. --- firefly/routes.py | 57 +++++++++++++++----------- firefly/services.py | 98 ++++++++++++++++++++++++++------------------- pyproject.toml | 2 +- requirements.txt | 1 - setup.py | 2 +- 5 files changed, 91 insertions(+), 69 deletions(-) diff --git a/firefly/routes.py b/firefly/routes.py index 3082cf6..735a947 100644 --- a/firefly/routes.py +++ b/firefly/routes.py @@ -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, diff --git a/firefly/services.py b/firefly/services.py index 0d44cc0..0216e80 100644 --- a/firefly/services.py +++ b/firefly/services.py @@ -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 \ No newline at end of file + 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 [] \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index add10e7..f76c432 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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" }] diff --git a/requirements.txt b/requirements.txt index 7536c0a..36e40c8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ Flask -redis python-dotenv ifireflylib click diff --git a/setup.py b/setup.py index eb9e549..f335ce6 100644 --- a/setup.py +++ b/setup.py @@ -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",