Skip to content

feat(models): implement WebhookEvent model - #52

Merged
codebestia merged 3 commits into
ShadeProtocol:mainfrom
Depo-dev:feat/webhook-event-model
Jul 29, 2026
Merged

feat(models): implement WebhookEvent model#52
codebestia merged 3 commits into
ShadeProtocol:mainfrom
Depo-dev:feat/webhook-event-model

Conversation

@Depo-dev

@Depo-dev Depo-dev commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Description

Adds the WebhookEvent model — a parsed, verified webhook event delivered by the Shade platform — plus a WebhookEventType constants enum.

src/shade/models/webhook.py:

  • WebhookEvent(ShadeObject) with id: str, type: str, data: Any, created_at: datetime (aliased from createdAt), livemode: bool. Follows the convention established by Transfer: pydantic field aliases for camelCase→snake_case, extra="allow" so a server-side field addition never breaks an older SDK, and InvalidRequestError (not a raw ValidationError) on a malformed payload.
  • WebhookEventType(str, Enum) with the 11 event types listed in issue #68. Members compare equal to their wire values, so event.type == WebhookEventType.PAYMENT_COMPLETED works directly in conditionals. WebhookEvent.type stays a plain str, so an event type added server-side still parses.

Both are exported from shade.models and the package root.

Fixes #42

Scope notes

  • data is left as the raw decoded JSON object at this layer, per the issue's acceptance criteria. Coercion into a typed resource model is the resource layer's job (#66 / #69).
  • Webhook.construct_event() does not exist yet — src/shade/resources/ has not been created (issue #66). There is nothing to wire up in this PR; the model docstring documents the contract the resource layer is expected to honour.

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

tests/test_webhook_event.py — 23 tests covering:

  • Valid payload populates every field; createdAt parses to an aware datetime
  • data stays a raw dict (and passes non-dict payloads through unchanged)
  • The input payload is not mutated
  • Each required field missing (id, type, data, createdAt, livemode) raises InvalidRequestError
  • A non-dict payload raises InvalidRequestError
  • An invalid createdAt raises, with the offending field named in field_errors
  • livemode true / false / JSON-string coercion
  • Unknown extra fields preserved; to_dict() round-trips by alias; repr shows the event id
  • WebhookEventType constants equal their wire strings and work in conditionals
  • An unknown event type still parses

Results:

pytest                          220 passed, 3 skipped
coverage src/shade/models/webhook.py   100%  (24/24 stmts)
ruff check <changed files>      clean
black --check <changed files>   clean (black 23.x, the version pinned in pyproject.toml)
mypy src/shade/models/webhook.py   0 errors in webhook.py
flake8 <changed files>          clean

The mypy run reports 24 pre-existing errors in http.py, gateway.py and __init__.py; none are in the files this PR adds. black 24+ would want a blank line after the module docstring, but the repo pins black ^23.7 and every existing module omits it, so the new file matches transfer.py / base.py.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (module and class docstrings document the expected payload shape, from_dict behaviour and the raw-data typing contract)
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Summary by CodeRabbit

  • New Features
    • Added webhook event domain models for payment, invoice, transfer, and swap notifications.
    • Introduced a set of known webhook event type constants, while still supporting unknown types.
    • Exposed webhook event identifiers, event payload data, creation timestamp, and live-mode status.
    • Added package-level exports for the new webhook models.
  • Tests
    • Added coverage for webhook event parsing, alias handling, round-trip serialization, input immutability, validation failures, and type constant matching.

Adds src/shade/models/webhook.py with a WebhookEvent ShadeObject carrying
id, type, data, created_at (aliased from createdAt) and livemode, plus a
WebhookEventType str enum for the platform's event type strings.

data is deliberately kept as the raw decoded JSON object; coercion into a
typed resource model belongs to the webhooks resource layer (#66/#69),
which does not exist yet.

Exports both from shade.models and the package root.
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 55c6a019-ff3b-4b61-8b2b-ac1005b3c327

📥 Commits

Reviewing files that changed from the base of the PR and between 7b6762a and 0a328cf.

📒 Files selected for processing (4)
  • src/shade/__init__.py
  • src/shade/models/__init__.py
  • src/shade/models/webhook.py
  • tests/test_webhook_event.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/shade/models/init.py
  • src/shade/init.py
  • src/shade/models/webhook.py

📝 Walkthrough

Walkthrough

Adds WebhookEvent and WebhookEventType, exposes them through shade.models and shade, and tests payload parsing, validation, serialization, unknown types, and public exports.

Changes

Webhook event support

Layer / File(s) Summary
Webhook event model
src/shade/models/webhook.py
Defines webhook event type constants and a WebhookEvent model with aliased timestamps, raw data, and lifecycle fields.
Public model exports
src/shade/models/__init__.py, src/shade/__init__.py
Re-exports WebhookEvent and WebhookEventType from the models package and package root.
Webhook payload validation
tests/test_webhook_event.py
Tests parsing, validation, alias handling, serialization, unknown event types, raw data preservation, and package-root exports.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding the WebhookEvent model.
Description check ✅ Passed The description follows the template closely and includes summary, issue link, testing, change type, and checklist.
Linked Issues check ✅ Passed The changes satisfy #42 by adding WebhookEvent, populating its fields, preserving raw data, and exporting the new types.
Out of Scope Changes check ✅ Passed The PR stays focused on the webhook model, its exports, and tests; no unrelated changes are evident.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/shade/models/webhook.py`:
- Around line 86-88: Update the webhook model’s data and livemode fields to use
dict[str, Any] and StrictBool respectively, preserving the existing created_at
alias. Ensure malformed payloads are rejected with InvalidRequestError, and
revise or remove tests that expect non-dict data or string livemode values such
as "false" to be accepted or coerced.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 244eb1c8-6ab4-4e8c-a7de-e0eb61a4cb24

📥 Commits

Reviewing files that changed from the base of the PR and between 264d02d and 7b6762a.

📒 Files selected for processing (4)
  • src/shade/__init__.py
  • src/shade/models/__init__.py
  • src/shade/models/webhook.py
  • tests/test_webhook_event.py

Comment thread src/shade/models/webhook.py Outdated
@codebestia

Copy link
Copy Markdown
Contributor

Hello @Depo-dev
Please address the coderabbit reviews

Depo-dev added 2 commits July 29, 2026 01:51
# Conflicts:
#	src/shade/__init__.py
#	src/shade/models/__init__.py
data now requires dict[str, Any] and livemode requires StrictBool so
malformed payloads raise InvalidRequestError instead of silently
coercing (e.g. a JSON-encoded "false" string, or an array/scalar data
payload).

@codebestia codebestia left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!
Thank you for your contribution.

@codebestia
codebestia merged commit 65526a5 into ShadeProtocol:main Jul 29, 2026
2 checks passed
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.

Implement WebhookEvent model

2 participants