Refactor: deduplicate request/response handling in entity classes - #104
Open
kir1903 wants to merge 5 commits into
Open
Refactor: deduplicate request/response handling in entity classes#104kir1903 wants to merge 5 commits into
kir1903 wants to merge 5 commits into
Conversation
Workspace, CurrentUser and ReportTimeEntry each repeated the same "call httpx client -> raise_for_status -> response.json() -> validate with a Pydantic schema" block roughly 25 times, both for single objects, lists and boolean success responses. Add _request, _request_and_validate, _request_and_validate_list and _request_and_check_success to the shared ApiWrapper base class so that duplication can be removed from the entity classes.
Replace repeated client-call/raise_for_status/json/model_validate blocks in Workspace's 12 methods with calls to the new _request_and_validate / _request_and_validate_list / _request_and_check_success helpers. No behavior change.
Same mechanical deduplication as the Workspace entity: replace repeated client-call/raise_for_status/json/model_validate blocks in CurrentUser's 11 methods with the shared helpers. get_current_time_entry keeps its manual json() call since it needs to special-case an empty body into None. No behavior change.
Same mechanical deduplication as Workspace and CurrentUser, applied to ReportTimeEntry.search. Pagination logic (first_row_number calculation) is unchanged. No behavior change.
payload_schema.model_dump(mode="json", exclude_none=True[, exclude_unset=True]) was repeated 13 times across Workspace, CurrentUser and ReportTimeEntry to turn a validated Pydantic schema into query params / a request body. Add dump_payload() to schemas/base.py and use it at all matching call sites. Calls with a different shape (model_dump_json(), or BulkEditMethodParams' plain model_dump(mode="json") without exclude_none) are intentionally left as-is since they are not the same pattern. No behavior change.
kir1903
force-pushed
the
refactor/entities-deduplication
branch
from
September 9, 2026 14:26
80ab52f to
117c9a4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Workspace,CurrentUser,ReportTimeEntry) into shared_request,_request_and_validate,_request_and_validate_listand_request_and_check_successhelpers on the baseApiWrapperclass.payload_schema.model_dump(mode="json", exclude_none=True[, exclude_unset=True])call (13 occurrences) into adump_payload()helper inschemas/base.py.Principles applied
ApiWrapper), which every entity class already inherits from.Test plan
poetry run pytest -m "not integration"— 191 passed, coverage 97.85% (threshold 95%)tests/test_api.py(4 tests) covering the newApiWrapperhelpers (success + error paths) viarespxtests/test_schemas_base.py(4 tests) coveringdump_payload()(exclude_none,exclude_unsetsemantics)poetry run ruff checkclean on all touched filestest_workspace.py,test_user.py,test_project.py,test_time_entry.py,test_report_time_entry.py) pass unmodified