Skip to content

MPT-19660 Improve resource action implementation#261

Merged
albertsola merged 1 commit intomainfrom
MPT-19660/Review-duplicated-resource-action
Mar 30, 2026
Merged

MPT-19660 Improve resource action implementation#261
albertsola merged 1 commit intomainfrom
MPT-19660/Review-duplicated-resource-action

Conversation

@albertsola
Copy link
Copy Markdown
Contributor

@albertsola albertsola commented Mar 30, 2026

Closes MPT-19660

  • Add ResourceAccessor and AsyncResourceAccessor (mpt_api_client/http/resource_accessor.py) to provide resource-scoped do_request/get/post/put/delete with Accept header handling and model deserialization
  • Remove Service._resource_do_request and Service._resource_action; add Service._resource / AsyncService._resource to return resource accessors and delegate request handling
  • Replace _resource_action/_resource_do_request usage across many mixins and services to call the new resource-scoped API (.post(action, json=...), .put(...), .get(...), .do_request(...), .delete())
  • Add join_url_path utility (mpt_api_client/http/url_utils.py) and replace urljoin usages with join_url_path for consistent path joining
  • Add unit tests for ResourceAccessor and join_url_path and update pyproject flake8 per-file ignores for the new tests
  • Shorten docstrings in several mixins; preserve public signatures and behavior while centralizing HTTP method selection, action path construction, payload/query handling, and response-to-model conversion in the new accessors

@albertsola albertsola requested a review from a team as a code owner March 30, 2026 10:02
@albertsola albertsola requested review from alephsur and jentyk March 30, 2026 10:02
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaces centralized _resource_do_request / _resource_action helpers with new resource-scoped accessors (ResourceAccessor / AsyncResourceAccessor) and updates services and many mixins/resources to call self._resource(resource_id).<method>(...) / .do_request(...); several docstrings were also shortened.

Changes

Cohort / File(s) Summary
New Resource Accessor API
mpt_api_client/http/resource_accessor.py
Adds ResourceAccessor and AsyncResourceAccessor that encapsulate a resource URL, build action URLs, forward requests to the HTTP client via do_request, and provide get/post/put/delete helpers that deserialize responses with the model's from_response.
Service Base Classes
mpt_api_client/http/service.py, mpt_api_client/http/async_service.py
Removes _resource_do_request / _resource_action; adds _resource(resource_id: str) returning a (A)syncResourceAccessor bound to the resource URL and model class.
HTTP Mixins (core)
mpt_api_client/http/mixins/...
mpt_api_client/http/mixins/update_mixin.py, get_mixin.py, delete_mixin.py, enable_mixin.py, disable_mixin.py, download_file_mixin.py, update_file_mixin.py
Replaces direct URL construction and calls to _resource_do_request/_resource_actionwith resource-scoped calls like.get(...), .post(...), .put(...), .delete()or.do_request(...); some docstrings shortened and urljoinreplaced byjoin_url_path` where applicable.
URL Utilities
mpt_api_client/http/url_utils.py
Adds join_url_path(base, *segments) for deterministic path joining (no trailing slash, skips empty segments, preserves literal segments).
Resources — Accounts
mpt_api_client/resources/accounts/..., mpt_api_client/resources/accounts/mixins/*_mixin.py
Converted account actions (activate/deactivate, block/unblock, invite flows, validate, buyers/sellers/users actions such as synchronize/transfer/disable/sso/set_password) to use self._resource(resource_id).post/put/...; signatures unchanged.
Resources — Billing
mpt_api_client/resources/billing/mixins/*_mixin.py, custom_ledgers.py, journals.py
Replaced many billing action calls to use _resource(...).post(...) and switched upload path construction to join_url_path; imports adjusted.
Resources — Catalog
mpt_api_client/resources/catalog/mixins/..., products.py, pricing_policies.py
Switched activate/deactivate/publish/unpublish and product settings update to resource-scoped .post(...) / .put(...); docstrings condensed.
Resources — Commerce
mpt_api_client/resources/commerce/...
Order transitions and endpoints (validate, process, query, complete, fail, quote, notify, render, template, terminate) now use .post(...) or .do_request(...) on the resource accessor.
Resources — Helpdesk & Notifications
mpt_api_client/resources/helpdesk/*, mpt_api_client/resources/notifications/*
Replaced _resource_action usages across cases, chat answers, forms, queues, categories, contacts, etc., with resource-scoped .post(...) calls; public APIs unchanged.
Tests & Lint
tests/unit/http/test_resource_accessor.py, tests/unit/http/test_url_utils.py, pyproject.toml
Adds unit tests for ResourceAccessor/AsyncResourceAccessor and join_url_path; updates pyproject.toml per-file-ignores for the new test.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Single Commit Required ❓ Inconclusive Unable to definitively determine the exact number of commits in this PR due to limitations in accessing git branch references in the sandbox environment. Run 'git log --oneline main..HEAD' or 'git log --oneline origin/main..HEAD' locally to count commits unique to this PR's source branch.
✅ Passed checks (2 passed)
Check name Status Explanation
Jira Issue Key In Title ✅ Passed The PR title contains exactly one Jira issue key (MPT-19660) in the correct MPT-XXXX format, matching the referenced issue in the PR description.
Test Coverage Required ✅ Passed PR modifies 39 code files and includes 2 test files, satisfying the requirement that test files are included or no code files are modified.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@albertsola albertsola force-pushed the MPT-19660/Review-duplicated-resource-action branch from 0dad396 to 2100741 Compare March 30, 2026 10:56
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
mpt_api_client/resources/accounts/mixins/activatable_mixin.py (1)

1-1: Prefer importing decorators from the public mpt_api_client.http API surface.

This keeps mixins decoupled from internal module layout (http.resource) and uses the stable export you just added.

Proposed change
-from mpt_api_client.http.resource import async_resource_action, resource_action
+from mpt_api_client.http import async_resource_action, resource_action
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mpt_api_client/resources/accounts/mixins/activatable_mixin.py` at line 1, The
mixin imports decorators from the internal module mpt_api_client.http.resource;
change the import to use the public API surface by importing
async_resource_action and resource_action from mpt_api_client.http instead
(update the import statement that currently references
mpt_api_client.http.resource to import these two symbols from
mpt_api_client.http so ActivatableMixin / any functions in activatable_mixin.py
continue to use the public decorators).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@mpt_api_client/resources/accounts/mixins/activatable_mixin.py`:
- Line 1: The mixin imports decorators from the internal module
mpt_api_client.http.resource; change the import to use the public API surface by
importing async_resource_action and resource_action from mpt_api_client.http
instead (update the import statement that currently references
mpt_api_client.http.resource to import these two symbols from
mpt_api_client.http so ActivatableMixin / any functions in activatable_mixin.py
continue to use the public decorators).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: b6112a3c-73d5-48c1-87a0-58665b70452f

📥 Commits

Reviewing files that changed from the base of the PR and between 0dad396 and 2100741.

📒 Files selected for processing (5)
  • mpt_api_client/http/__init__.py
  • mpt_api_client/http/mixins/update_mixin.py
  • mpt_api_client/http/resource.py
  • mpt_api_client/resources/accounts/mixins/activatable_mixin.py
  • mpt_api_client/resources/catalog/mixins/activatable_mixin.py
✅ Files skipped from review due to trivial changes (1)
  • mpt_api_client/resources/catalog/mixins/activatable_mixin.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • mpt_api_client/http/mixins/update_mixin.py

@albertsola albertsola force-pushed the MPT-19660/Review-duplicated-resource-action branch 3 times, most recently from 7313469 to 5a364a0 Compare March 30, 2026 14:41
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
mpt_api_client/resources/notifications/categories.py (1)

31-47: Consider reusing the existing publishable mixin here.

These four methods now mirror mpt_api_client/resources/catalog/mixins/publishable_mixin.py almost line-for-line. Pulling that mixin up to a shared location and inheriting it here would keep future accessor changes centralized.

Also applies to: 58-74

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mpt_api_client/resources/notifications/categories.py` around lines 31 - 47,
These publish/unpublish methods duplicate logic in
mpt_api_client/resources/catalog/mixins/publishable_mixin.py; refactor by moving
that mixin to a shared location and have this resource class inherit the shared
PublishableMixin instead of reimplementing publish and unpublish (also apply the
same change for the other two methods referenced at lines 58-74); remove the
duplicate implementations of publish, unpublish (and the matching pair at
58-74), add the import for the shared PublishableMixin, and ensure the existing
calls that use _resource(), ResourceData and Model types keep working with the
mixin’s method names.
mpt_api_client/resources/billing/mixins/recalculatable_mixin.py (1)

7-32: Method overlap between IssuableMixin and RecalculatableMixin — design consideration.

Both mixins define recalculate() and queue() methods with identical signatures. While no class currently inherits from both mixins simultaneously, having the same method in two separate mixins is redundant and could cause silent MRO shadowing if combined in the future. Consider consolidating these overlapping methods into a single mixin or renaming them to clarify their distinct purposes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mpt_api_client/resources/billing/mixins/recalculatable_mixin.py` around lines
7 - 32, Rework the duplicate recalculate and queue methods across IssuableMixin
and RecalculatableMixin by consolidating the shared implementations into a
single mixin (e.g., RecalculatableMixin as the canonical source) or a new common
mixin that both can inherit; remove the duplicate method definitions from the
other mixin(s) so only one implementation of recalculate() and queue() exists,
and update any classes that previously inherited both mixins to import/use the
single consolidated mixin; reference the existing method names recalculate,
queue and the mixin classes IssuableMixin and RecalculatableMixin when making
the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mpt_api_client/http/async_service.py`:
- Around line 21-32: The _resource method currently uses
urljoin(f"{self.path}/", resource_id) which treats resource_id as an absolute
path/URL; change it to build the URL by safely joining and escaping path
segments (e.g., use posixpath.join or manual "/" joining with urllib.parse.quote
on resource_id) so leading slashes or absolute URLs cannot drop or override
self.path, and return AsyncResourceAccessor(self.http_client, safe_joined_url,
self._model_class); apply the same fix pattern to the similar join logic in
resource_accessor.py where action names are concatenated to resource URLs.

---

Nitpick comments:
In `@mpt_api_client/resources/billing/mixins/recalculatable_mixin.py`:
- Around line 7-32: Rework the duplicate recalculate and queue methods across
IssuableMixin and RecalculatableMixin by consolidating the shared
implementations into a single mixin (e.g., RecalculatableMixin as the canonical
source) or a new common mixin that both can inherit; remove the duplicate method
definitions from the other mixin(s) so only one implementation of recalculate()
and queue() exists, and update any classes that previously inherited both mixins
to import/use the single consolidated mixin; reference the existing method names
recalculate, queue and the mixin classes IssuableMixin and RecalculatableMixin
when making the change.

In `@mpt_api_client/resources/notifications/categories.py`:
- Around line 31-47: These publish/unpublish methods duplicate logic in
mpt_api_client/resources/catalog/mixins/publishable_mixin.py; refactor by moving
that mixin to a shared location and have this resource class inherit the shared
PublishableMixin instead of reimplementing publish and unpublish (also apply the
same change for the other two methods referenced at lines 58-74); remove the
duplicate implementations of publish, unpublish (and the matching pair at
58-74), add the import for the shared PublishableMixin, and ensure the existing
calls that use _resource(), ResourceData and Model types keep working with the
mixin’s method names.
🪄 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: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 45058694-b640-4a28-861c-2ba3cc498b86

📥 Commits

Reviewing files that changed from the base of the PR and between 2100741 and f94e032.

📒 Files selected for processing (34)
  • mpt_api_client/http/async_service.py
  • mpt_api_client/http/mixins/delete_mixin.py
  • mpt_api_client/http/mixins/disable_mixin.py
  • mpt_api_client/http/mixins/download_file_mixin.py
  • mpt_api_client/http/mixins/enable_mixin.py
  • mpt_api_client/http/mixins/get_mixin.py
  • mpt_api_client/http/mixins/update_mixin.py
  • mpt_api_client/http/resource_accessor.py
  • mpt_api_client/http/service.py
  • mpt_api_client/resources/accounts/buyers.py
  • mpt_api_client/resources/accounts/mixins/activatable_mixin.py
  • mpt_api_client/resources/accounts/mixins/blockable_mixin.py
  • mpt_api_client/resources/accounts/mixins/invitable_mixin.py
  • mpt_api_client/resources/accounts/mixins/validate_mixin.py
  • mpt_api_client/resources/accounts/sellers.py
  • mpt_api_client/resources/accounts/users.py
  • mpt_api_client/resources/billing/mixins/acceptable_mixin.py
  • mpt_api_client/resources/billing/mixins/issuable_mixin.py
  • mpt_api_client/resources/billing/mixins/recalculatable_mixin.py
  • mpt_api_client/resources/billing/mixins/regeneratable_mixin.py
  • mpt_api_client/resources/catalog/mixins/activatable_mixin.py
  • mpt_api_client/resources/catalog/mixins/publishable_mixin.py
  • mpt_api_client/resources/catalog/pricing_policies.py
  • mpt_api_client/resources/catalog/products.py
  • mpt_api_client/resources/commerce/mixins/render_mixin.py
  • mpt_api_client/resources/commerce/mixins/template_mixin.py
  • mpt_api_client/resources/commerce/mixins/terminate_mixin.py
  • mpt_api_client/resources/commerce/orders.py
  • mpt_api_client/resources/helpdesk/cases.py
  • mpt_api_client/resources/helpdesk/chat_answers.py
  • mpt_api_client/resources/helpdesk/forms.py
  • mpt_api_client/resources/helpdesk/queues.py
  • mpt_api_client/resources/notifications/categories.py
  • mpt_api_client/resources/notifications/contacts.py
✅ Files skipped from review due to trivial changes (1)
  • mpt_api_client/resources/notifications/contacts.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • mpt_api_client/resources/catalog/mixins/activatable_mixin.py

@albertsola albertsola force-pushed the MPT-19660/Review-duplicated-resource-action branch 3 times, most recently from 1321f15 to e02a234 Compare March 30, 2026 16:07
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/unit/http/test_url_utils.py (1)

60-61: Make join_url_path()'s typing match its None behavior.

Line 61 needs # type: ignore[arg-type] only because the helper still advertises *segments: str. If skipping None is intentional, widen the signature to str | None so callers do not need type escapes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/http/test_url_utils.py` around lines 60 - 61, The test shows
join_url_path accepts None segments at runtime but its signature is declared as
*segments: str, so update join_url_path's type signature to accept optional
strings (e.g., *segments: str | None or *segments: Optional[str]) and adjust any
internal typing (imports from typing if using Optional) so callers don't need #
type: ignore; keep the runtime behavior of skipping None intact and update the
function docstring/type hints accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/unit/http/test_url_utils.py`:
- Around line 42-45: The test currently asserts that join_url_path preserves a
dot-segment ("../other"), but we must instead treat dot-segments as unsafe and
either reject or escape them; update the implementation of join_url_path to
detect path segments equal to ".." or "." and either raise a ValueError (or
return an error) or percent-encode those segments before joining, and then
change test_dotdot_segment_is_literal to expect the new behavior (error/escape)
rather than the raw "../other"; use the symbol join_url_path and the test name
test_dotdot_segment_is_literal to locate and update the code and test.

---

Nitpick comments:
In `@tests/unit/http/test_url_utils.py`:
- Around line 60-61: The test shows join_url_path accepts None segments at
runtime but its signature is declared as *segments: str, so update
join_url_path's type signature to accept optional strings (e.g., *segments: str
| None or *segments: Optional[str]) and adjust any internal typing (imports from
typing if using Optional) so callers don't need # type: ignore; keep the runtime
behavior of skipping None intact and update the function docstring/type hints
accordingly.
🪄 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: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 5c02cf3a-710c-4550-8dd0-7bfc3ffbc1a8

📥 Commits

Reviewing files that changed from the base of the PR and between 5a364a0 and 4e5da88.

📒 Files selected for processing (41)
  • mpt_api_client/http/async_service.py
  • mpt_api_client/http/mixins/delete_mixin.py
  • mpt_api_client/http/mixins/disable_mixin.py
  • mpt_api_client/http/mixins/download_file_mixin.py
  • mpt_api_client/http/mixins/enable_mixin.py
  • mpt_api_client/http/mixins/get_mixin.py
  • mpt_api_client/http/mixins/update_file_mixin.py
  • mpt_api_client/http/mixins/update_mixin.py
  • mpt_api_client/http/resource_accessor.py
  • mpt_api_client/http/service.py
  • mpt_api_client/http/url_utils.py
  • mpt_api_client/resources/accounts/buyers.py
  • mpt_api_client/resources/accounts/mixins/activatable_mixin.py
  • mpt_api_client/resources/accounts/mixins/blockable_mixin.py
  • mpt_api_client/resources/accounts/mixins/invitable_mixin.py
  • mpt_api_client/resources/accounts/mixins/validate_mixin.py
  • mpt_api_client/resources/accounts/sellers.py
  • mpt_api_client/resources/accounts/users.py
  • mpt_api_client/resources/billing/custom_ledgers.py
  • mpt_api_client/resources/billing/journals.py
  • mpt_api_client/resources/billing/mixins/acceptable_mixin.py
  • mpt_api_client/resources/billing/mixins/issuable_mixin.py
  • mpt_api_client/resources/billing/mixins/recalculatable_mixin.py
  • mpt_api_client/resources/billing/mixins/regeneratable_mixin.py
  • mpt_api_client/resources/catalog/mixins/activatable_mixin.py
  • mpt_api_client/resources/catalog/mixins/publishable_mixin.py
  • mpt_api_client/resources/catalog/pricing_policies.py
  • mpt_api_client/resources/catalog/products.py
  • mpt_api_client/resources/commerce/mixins/render_mixin.py
  • mpt_api_client/resources/commerce/mixins/template_mixin.py
  • mpt_api_client/resources/commerce/mixins/terminate_mixin.py
  • mpt_api_client/resources/commerce/orders.py
  • mpt_api_client/resources/helpdesk/cases.py
  • mpt_api_client/resources/helpdesk/chat_answers.py
  • mpt_api_client/resources/helpdesk/forms.py
  • mpt_api_client/resources/helpdesk/queues.py
  • mpt_api_client/resources/notifications/categories.py
  • mpt_api_client/resources/notifications/contacts.py
  • pyproject.toml
  • tests/unit/http/test_resource_accessor.py
  • tests/unit/http/test_url_utils.py
✅ Files skipped from review due to trivial changes (14)
  • mpt_api_client/http/mixins/enable_mixin.py
  • pyproject.toml
  • mpt_api_client/http/url_utils.py
  • mpt_api_client/http/mixins/disable_mixin.py
  • mpt_api_client/resources/catalog/pricing_policies.py
  • mpt_api_client/resources/accounts/sellers.py
  • mpt_api_client/resources/accounts/mixins/blockable_mixin.py
  • mpt_api_client/resources/commerce/mixins/terminate_mixin.py
  • mpt_api_client/resources/commerce/orders.py
  • mpt_api_client/resources/helpdesk/chat_answers.py
  • mpt_api_client/resources/accounts/buyers.py
  • mpt_api_client/resources/billing/mixins/issuable_mixin.py
  • mpt_api_client/resources/accounts/users.py
  • mpt_api_client/resources/catalog/mixins/publishable_mixin.py
🚧 Files skipped from review as they are similar to previous changes (19)
  • mpt_api_client/http/mixins/delete_mixin.py
  • mpt_api_client/resources/commerce/mixins/render_mixin.py
  • mpt_api_client/resources/commerce/mixins/template_mixin.py
  • mpt_api_client/resources/accounts/mixins/validate_mixin.py
  • mpt_api_client/resources/helpdesk/forms.py
  • mpt_api_client/http/mixins/get_mixin.py
  • mpt_api_client/resources/notifications/categories.py
  • mpt_api_client/resources/accounts/mixins/activatable_mixin.py
  • mpt_api_client/resources/helpdesk/queues.py
  • mpt_api_client/resources/accounts/mixins/invitable_mixin.py
  • mpt_api_client/resources/billing/mixins/recalculatable_mixin.py
  • mpt_api_client/http/mixins/download_file_mixin.py
  • mpt_api_client/http/mixins/update_mixin.py
  • mpt_api_client/resources/notifications/contacts.py
  • mpt_api_client/resources/billing/mixins/acceptable_mixin.py
  • mpt_api_client/resources/catalog/mixins/activatable_mixin.py
  • mpt_api_client/resources/billing/mixins/regeneratable_mixin.py
  • mpt_api_client/resources/helpdesk/cases.py
  • mpt_api_client/resources/catalog/products.py

@albertsola albertsola force-pushed the MPT-19660/Review-duplicated-resource-action branch 6 times, most recently from 58c6a66 to 24ec029 Compare March 30, 2026 16:33
@albertsola albertsola force-pushed the MPT-19660/Review-duplicated-resource-action branch from 24ec029 to 9bdbf50 Compare March 30, 2026 16:36
@sonarqubecloud
Copy link
Copy Markdown

@albertsola albertsola merged commit 72e9a02 into main Mar 30, 2026
4 checks passed
@albertsola albertsola deleted the MPT-19660/Review-duplicated-resource-action branch March 30, 2026 16:41
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