chores: creates pydantic base model for response objects - #47
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (5)
📝 WalkthroughWalkthroughAdds Pydantic as a runtime dependency and introduces the exported ChangesShadeObject model
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Caller
participant ShadeObject
participant Pydantic
participant InvalidRequestError
Caller->>ShadeObject: from_dict(data)
ShadeObject->>Pydantic: Validate model data
Pydantic-->>ShadeObject: Model or ValidationError
ShadeObject->>InvalidRequestError: Convert validation failure
ShadeObject-->>Caller: Model or InvalidRequestError
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
codebestia
left a comment
There was a problem hiding this comment.
LGTM!
Great Implementation.
Thank you for your contribution @KodeSage
Rebuild the Merchant model on the shared pydantic ShadeObject introduced on main (ShadeProtocol#47), replacing the standalone plain-Python model and base. - Move src/shade/merchant.py -> src/shade/models/merchant.py and delete the now-redundant src/shade/base.py. - Map camelCase JSON to snake_case fields with pydantic Field(alias=...); from_dict / to_dict / repr come from ShadeObject. - Enforce validation via pydantic: StrictBool for active/verified (no silent coercion of strings like "false"), a Stellar public-key field_validator on address, and a before-validator rejecting boolean merchant_id (which pydantic would otherwise coerce to 1/0). All surface as InvalidRequestError through the base. - Export Merchant from shade.models and the top-level package. - Update tests: unknown keys are now preserved (extra="allow"), the merchant_id error param is the "merchantId" alias, and add a boolean merchant_id rejection case.
Closes #36
Description
Adds
ShadeObject, the common Pydantic base class that every Shade API response model will inherit from. It provides dict round-tripping (from_dict/to_dict), a readable__repr__, and translation of Pydantic validation failures into the SDK's ownInvalidRequestError.Motivation: response models were each going to hand-roll parsing, validation, and
__repr__. Building onpydantic.BaseModelremoves that boilerplate and gives us free type coercion (e.g. a stringy"500"from the API becomes anint), whileextra="allow"means a field added server-side never breaks an older SDK — the unknown value is preserved and re-emitted onto_dict().Design notes worth a reviewer's attention:
to_dict()defaults toby_alias=Trueso output matches the API wire format. Round-tripping still works becausepopulate_by_name=Truelets models accept either the alias or the Python field name.__init__is overridden as well asfrom_dict. Wrapping onlyfrom_dictwould still leak a rawValidationErrorout of direct construction (Payment(id="pay_1")), so both paths funnel through the same translation.InvalidRequestErroralready carries from API responses:field_errorsbecomes{location: [messages]}andparamis set to the first offending field._id_fieldis aClassVar(default"id") that subclasses override when their primary key is named differently, e.g._id_field = "invoice_id". When the field is absent,reprdegrades to<Account>rather than raising.Dependencies: this adds
pydantic ^2.0topyproject.tomlandpoetry.lock. It is the SDK's first runtime dependency beyondhttpxandstellar-sdk.Fixes #36
Type of change
How Has This Been Tested?
Added
tests/test_models_base.py(12 tests) covering the acceptance criteria, exercised against three sample subclasses: a plain model, one with an aliased field and overridden_id_field, and one with no ID field at all.from_dict(m.to_dict())preserves data, includingdatetimevalues and aliased fieldsinvoiceId) and the field name (invoice_id), and dump under the aliasto_dict(){"amount": "500"}yieldsamount == 500__repr__—<Payment id='pay_1'>,<Invoice invoice_id='inv_1'>, and<Account>for a model with no ID fieldInvalidRequestError(neverValidationError), fromfrom_dictand from direct construction, withparamandfield_errorspopulated; non-dict input tofrom_dictis rejected with a clear messageReproduce with:
Test configuration: Python 3.10 (matches CI), pydantic 2.13.4, macOS. Full suite is 144 passed; flake8 reports 0 findings on the new files.
Checklist:
ShadeObjectis an internal base class with no concrete models on it so far. Happy to add a README section once the first models land.Note for reviewers: there is a pre-existing empty
src/shade/base.pysitting alongside the newsrc/shade/models/base.py. I left it untouched to keep this PR scoped, but the two names are easy to confuse — worth deleting in a follow-up if nothing is planned for it.Summary by CodeRabbit
New Features
Tests