Skip to content

feat: implement safe person retrieval with caching in Webex API inter…#2

Merged
ChristopherJHart merged 2 commits intomainfrom
fix/user-missing-error
Jul 27, 2025
Merged

feat: implement safe person retrieval with caching in Webex API inter…#2
ChristopherJHart merged 2 commits intomainfrom
fix/user-missing-error

Conversation

@aitestino
Copy link
Member

🛡️ Description

This pull request fixes a critical bug where the Webex summarizer would crash when encountering messages from deleted or deactivated users. The fix implements graceful error handling to ensure the summarizer continues operating even when historical data references users who no longer exist in the Webex system.

  • Bug Fix: Resolves crashes caused by 404 errors when fetching person details for deleted/deactivated Webex users
  • Robustness: Implements graceful degradation by using placeholder names for missing users
  • Performance: Adds caching to prevent repeated API calls for the same deleted users
  • Maintainability: Follows DRY and Single Responsibility principles with minimal, surgical changes

🔗 Related Issue(s)

No related issues.

✔️ Type of Change

[x] 🐛 Bug fix (non-breaking change which fixes an issue)
[x] 🛠️ Refactoring/Technical debt (internal code improvements with no direct user-facing changes)

🎯 Key Changes & Implementation Details

summarizer/webex.py:

  • Added safe_get_person() function that handles all person-fetching with error recovery
  • Implemented module-level cache (_person_cache) to avoid repeated API calls for deleted users
  • Modified create_message() to use the safe fetching function instead of direct API calls
  • Added ApiError import for proper exception handling
  • When a 404 error occurs, creates a placeholder User object with display name "[Deleted User]"
  • Logs at INFO level when deleted users are encountered

🧪 Testing Done

  • Manual E2E testing performed:
  • Scenario 1: Ran summarizer on dates containing messages from deleted users - verified it completes successfully
  • Scenario 2: Confirmed deleted user messages appear with "[Deleted User]" as sender name
  • Scenario 3: Verified caching prevents repeated API calls for same deleted user
  • Scenario 4: Ensured normal user fetching still works correctly

✅ Checklist

  • My code follows the style guidelines of this project
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new linting errors or warnings
  • The fix maintains backward compatibility
  • Error handling is graceful and informative

🖼️ Error Before Fix

(Webex-Conv-Summarizer) (.venv) atestini@ATESTINI-M-727R % uv run summarizer --user-email=<redacted> --webex-token=<redacted>  --start-date=2025-07-07 --end-date=2025-07-11

╔══════════════════════════════════════════════════════════╗
║                  2025-07-07 — Messages                   ║
╚══════════════════════════════════════════════════════════╝
[10:30:43] Connected as Andrea Testino                                                                                           runner.py:65
Looking for activity on 2025-07-07...
⠇ Scanning rooms for activity... Processed: 246 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⠏ Fetching messages from active rooms... Processed: 35/205 ━━━━━━╸━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Traceback (most recent call last):
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/.venv/bin/summarizer", line 10, in <module>
    sys.exit(app())
             ^^^^^
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/.venv/lib/python3.12/site-packages/typer/main.py", line 340, in __call__
    raise e
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/.venv/lib/python3.12/site-packages/typer/main.py", line 323, in __call__
    return get_command(self)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/.venv/lib/python3.12/site-packages/click/core.py", line 1161, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/.venv/lib/python3.12/site-packages/typer/core.py", line 677, in main
    return _main(
           ^^^^^^
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/.venv/lib/python3.12/site-packages/typer/core.py", line 195, in _main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/.venv/lib/python3.12/site-packages/click/core.py", line 1443, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/.venv/lib/python3.12/site-packages/click/core.py", line 788, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/.venv/lib/python3.12/site-packages/typer/main.py", line 698, in wrapper
    return callback(**use_params)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/summarizer/cli.py", line 204, in main
    _run_for_date(config, date_header=True)
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/summarizer/cli.py", line 117, in _run_for_date
    run_app(config, date_header=date_header)
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/summarizer/runner.py", line 70, in run_app
    message_data = webex_client.get_activity(
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/summarizer/webex.py", line 187, in get_activity
    messages = self.get_messages_for_rooms(active_rooms, date, local_tz)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/summarizer/webex.py", line 150, in get_messages_for_rooms
    result: MessageAnalysisResult = future.result()
                                    ^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/concurrent/futures/_base.py", line 449, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/opt/homebrew/Cellar/python@3.12/3.12.9/Frameworks/Python.framework/Versions/3.12/lib/python3.12/concurrent/futures/thread.py", line 59, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/summarizer/webex.py", line 271, in get_messages
    msg = create_message(sdk_message, client, room, local_tz)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/summarizer/webex.py", line 204, in create_message
    sdk_sender = client.people.get(sdk_message.personId)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/.venv/lib/python3.12/site-packages/webexpythonsdk/api/people.py", line 275, in get
    json_data = self._session.get(API_ENDPOINT + "/" + personId)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/.venv/lib/python3.12/site-packages/webexpythonsdk/restsession.py", line 392, in get
    response = self.request("GET", url, erc, params=params, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/.venv/lib/python3.12/site-packages/webexpythonsdk/restsession.py", line 357, in request
    check_response_code(response, erc)
  File "/Users/atestini/Desktop/Automation/Webex-Conv-Summarizer/.venv/lib/python3.12/site-packages/webexpythonsdk/utils.py", line 207, in check_response_code
    raise ApiError(response)
webexpythonsdk.exceptions.ApiError: [404] Not Found - Person not found [Tracking ID: ROUTERGW_a4fc6034-a9c5-4c5c-bbf9-358ca43bbf97]

➡️ Next Steps (if applicable)

Consider adding a summary at the end showing count of messages from deleted users
Could extend pattern to handle other deleted entities (rooms, etc.) if needed

❓ Questions & Open Items (if applicable)
N/A

aitestino and others added 2 commits July 15, 2025 10:43
…actions

* Added a new function `safe_get_person` to fetch person details while handling deleted users gracefully.
* Introduced caching for person lookups to minimize repeated API calls for deleted users.
* Updated `create_message` function to utilize the new `safe_get_person` for improved error handling and efficiency.
@ChristopherJHart ChristopherJHart merged commit 3c0ae88 into main Jul 27, 2025
24 checks passed
@ChristopherJHart ChristopherJHart deleted the fix/user-missing-error branch July 27, 2025 22:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants