Skip to content

fix(webhook): handle raw dict input in mode='before' validator (#282)#3

Open
z23 wants to merge 1 commit into
mainfrom
fix/webhook-validator-dict-access
Open

fix(webhook): handle raw dict input in mode='before' validator (#282)#3
z23 wants to merge 1 commit into
mainfrom
fix/webhook-validator-dict-access

Conversation

@z23
Copy link
Copy Markdown
Owner

@z23 z23 commented Apr 25, 2026

Summary

Fixes upstream issue thatmattlove/hyperglass#282: all HTTP webhook delivery (Slack, MS Teams, generic) silently fails with:

ERROR | hyperglass.external._base:aexit:102 - 'dict' object has no attribute 'source'
ERROR | hyperglass.api.tasks:send_webhook:68 - Failed to send webhook

Webhook.validate_webhook is decorated with @model_validator(mode='before'), which runs prior to model construction and receives the raw input passed to `Webhook(**query)` — a dict. The current code treats it as an instance:

```python
@model_validator(mode="before")
def validate_webhook(cls, model: "Webhook") -> "Webhook":
if model.source in ("127.0.0.1", "::1"): # AttributeError on dict
model.network = {}
return model
```

Both lines raise on a dict, so the validator always fails and the webhook is never sent.

Fix

Treat the input as a dict, guard with `isinstance`, and use `.get()` so a missing `source` key falls through to the model's default rather than raising:

```python
@model_validator(mode="before")
@classmethod
def validate_webhook(cls, data: t.Any) -> t.Any:
if isinstance(data, dict) and data.get("source") in ("127.0.0.1", "::1"):
data["network"] = {}
return data
```

Test plan

  • New `hyperglass/models/tests/test_webhook.py` covers: construction with a public source, network reset for `127.0.0.1` and `::1`, end-to-end Slack and MS Teams payload rendering. All 5 pass.
  • Full suite: `pytest hyperglass/` — 53 passed, 1 deselected (the deselected `test_device_builtins_vrf_directive_renders_vrf_in_command` already fails on `main` and is unrelated).
  • Manual verification: configure a Slack webhook in `config.yaml`, run a query, confirm the message arrives.

🤖 Generated with Claude Code

…attlove#282)

`Webhook.validate_webhook` is decorated with `@model_validator(mode="before")`,
which means it runs prior to model construction and receives the raw input
dict from `Webhook(**query)`. The implementation accessed it as if it were a
model instance — `model.source` and `model.network = {}` — which raised
`AttributeError: 'dict' object has no attribute 'source'` and silently broke
all HTTP webhook delivery (Slack, MS Teams, generic).

Treat the input as a dict, guard with isinstance, and use `.get()` so an
absent `source` key doesn't raise either. Source defaults to `"Unknown"` on
the model anyway, so a missing key correctly falls through to the model
default.

Refs: thatmattlove#282

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@z23 z23 self-assigned this Apr 25, 2026
@z23 z23 requested a review from Copilot April 25, 2026 09:38
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes webhook delivery failures caused by a @model_validator(mode="before") assuming it receives a constructed Webhook instance instead of the raw input mapping, which prevented Slack/MS Teams/generic webhook payloads from being built and sent.

Changes:

  • Update Webhook.validate_webhook to safely handle raw-dict input in a mode="before" validator and reset network for localhost sources.
  • Add regression tests covering validator behavior and Slack/MS Teams payload rendering.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
hyperglass/models/webhook.py Corrects the mode="before" validator to operate on raw input data and avoid AttributeError during webhook construction.
hyperglass/models/tests/test_webhook.py Adds regression tests to ensure webhook model construction and payload rendering work for common providers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

if model.source in ("127.0.0.1", "::1"):
model.network = {}
return model
@classmethod
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@model_validator methods elsewhere in this codebase are defined as plain methods (no @classmethod). Adding @classmethod here is inconsistent and can be brittle depending on how Pydantic wraps validators; consider dropping @classmethod and keeping the same (cls, data) signature so it matches the established pattern (e.g., DnsOverHttps.validate_dns, FRRPath.validate_path).

Suggested change
@classmethod

Copilot uses AI. Check for mistakes.
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