Skip to content

MPT-19905: add /integration/extensions/{id}/media service#285

Merged
albertsola merged 3 commits intomainfrom
MPT-19905/integration-extension-media
Apr 10, 2026
Merged

MPT-19905: add /integration/extensions/{id}/media service#285
albertsola merged 3 commits intomainfrom
MPT-19905/integration-extension-media

Conversation

@albertsola
Copy link
Copy Markdown
Contributor

@albertsola albertsola commented Apr 7, 2026

Summary

Implements the /public/v1/integration/extensions/{extensionId}/media endpoint group as part of MPT-13310.

Changes

  • mpt_api_client/resources/integration/extension_media.py — MediaMixin + PublishableMixin + CreateFileMixin + ModifiableResourceMixin + CollectionMixin
  • mpt_api_client/resources/integration/mixins/media_mixin.pyupload_image() for PUT /{id}/image
  • Updated extensions.py to expose media(extension_id) accessor
  • Unit tests: tests/unit/resources/integration/test_extension_media.py + test_media_mixin.py
  • E2e tests: tests/e2e/integration/extension_media/

Depends on

#277 (MPT-19892 — rename extensibility → integration)

⚠️ Draft — targets MPT-19892/e2e-extension-base-service until PR #277 merges.

Closes MPT-19905

  • Implemented /public/v1/integration/extensions/{extensionId}/media endpoint group with ExtensionMedia model supporting metadata, file attributes, and related entities
  • Created ExtensionMediaService and AsyncExtensionMediaService classes providing full-featured media management including CRUD operations, publish/unpublish, file download/upload, and collection iteration
  • Added MediaMixin and AsyncMediaMixin mixins with upload_image() method to support image uploads via PUT /{id}/image endpoint
  • Extended ExtensionsService and AsyncExtensionsService with media(extension_id) accessor methods to instantiate media service clients scoped to a specific extension
  • Added unit tests validating service mixins, model field mapping, HTTP interactions, and extension service accessors
  • Added end-to-end tests covering media lifecycle operations: creation, filtering, updates, publish/unpublish workflows, file downloads, and deletion

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 7, 2026

📝 Walkthrough

Walkthrough

Introduces a new extension media integration resource with synchronous and asynchronous service classes, including a model for media metadata and file properties. Adds media accessor methods to extension services and implements a media mixin providing image upload functionality. Includes comprehensive unit and end-to-end tests for media operations.

Changes

Cohort / File(s) Summary
Extension Media Resource
mpt_api_client/resources/integration/extension_media.py
New module defining ExtensionMedia model with metadata and file attributes, ExtensionMediaServiceConfig endpoint configuration, and dual service classes (ExtensionMediaService, AsyncExtensionMediaService) composed from mixins for full media lifecycle management.
Extensions Service Integration
mpt_api_client/resources/integration/extensions.py
Added media(extension_id: str) methods to both ExtensionsService and AsyncExtensionsService to construct and return media service instances scoped to a specific extension.
Media Mixin Framework
mpt_api_client/resources/integration/mixins/__init__.py, mpt_api_client/resources/integration/mixins/media_mixin.py
Introduced MediaMixin and AsyncMediaMixin classes with upload_image() methods for multipart image uploads to resource-specific endpoints; exports added to package __all__.
Unit Tests
tests/unit/resources/integration/test_extension_media.py, tests/unit/resources/integration/mixins/test_media_mixin.py
Added unit test coverage for ExtensionMediaService/AsyncExtensionMediaService mixin availability, model field mapping, HTTP behavior (create, download, POST/GET operations), and media mixin upload_image functionality with mocked HTTP responses.
E2E Tests
tests/e2e/integration/extension_media/conftest.py, tests/e2e/integration/extension_media/test_sync_extension_media.py, tests/e2e/integration/extension_media/test_async_extension_media.py
Added pytest fixtures providing extension media service clients, deterministic test payloads, and media lifecycle management; defined test suites for creation, filtering, updating, publishing/unpublishing, downloading, and deletion across sync and async code paths.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PR #277: Modifies ExtensionsService in mpt_api_client/resources/integration/extensions.py (endpoint updates/renames), potentially overlapping with this PR's addition of media accessor methods to the same service class.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Jira Issue Key In Title ✅ Passed PR title contains the Jira issue key MPT-19905 in the correct MPT-XXXX format.
Test Coverage Required ✅ Passed PR modifies 4 code files and includes 5 comprehensive test files covering unit and end-to-end testing scenarios.
Single Commit Required ✅ Passed The pull request contains exactly one commit (79174cd) that combines all related changes for implementing the extension media service endpoint, maintaining clean git history.

✏️ 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.

Base automatically changed from MPT-19892/e2e-extension-base-service to main April 7, 2026 12:00
@albertsola albertsola force-pushed the MPT-19905/integration-extension-media branch 3 times, most recently from b520317 to 52db84c Compare April 9, 2026 10:33
@albertsola albertsola marked this pull request as ready for review April 9, 2026 10:33
@albertsola albertsola requested a review from a team as a code owner April 9, 2026 10:33
@albertsola albertsola requested review from d3rky and robcsegal April 9, 2026 10:33
@albertsola albertsola self-assigned this Apr 9, 2026
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 (2)
mpt_api_client/resources/integration/extension_media.py (1)

74-87: Align docstring placeholder naming with the configured endpoint.

At Line 74 and Line 87, docstrings use {extensionId} while the actual endpoint template at Line 57 uses {extension_id}. Matching these improves clarity for maintainers.

📝 Proposed docstring alignment
-    """Sync service for the /public/v1/integration/extensions/{extensionId}/media endpoint."""
+    """Sync service for the /public/v1/integration/extensions/{extension_id}/media endpoint."""
@@
-    """Async service for the /public/v1/integration/extensions/{extensionId}/media endpoint."""
+    """Async service for the /public/v1/integration/extensions/{extension_id}/media endpoint."""
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mpt_api_client/resources/integration/extension_media.py` around lines 74 -
87, Update the two docstrings that currently reference the endpoint placeholder
"{extensionId}" to use the configured template "{extension_id}" so they match
the endpoint definition; specifically, edit the module-level docstring and the
AsyncExtensionMediaService class docstring (class name:
AsyncExtensionMediaService) to replace "{extensionId}" with "{extension_id}" for
consistency with the endpoint template.
tests/unit/resources/integration/test_extension_media.py (1)

93-107: Strengthen multipart contract assertions in the create test.

At Line 105-Line 107, the test proves path/method/response, but it can still pass if multipart composition regresses (e.g., missing media or file part). Adding request-content assertions would better protect _upload_data_key / _upload_file_key behavior.

♻️ Proposed test hardening
     assert mock_route.call_count == 1
-    assert mock_route.calls[0].request.method == "POST"
+    request = mock_route.calls[0].request
+    assert request.method == "POST"
+    assert b'name="media"' in request.content
+    assert b'name="file"' in request.content
     assert result.to_dict() == expected_response

As per coding guidelines, **/test_*.py: Follow the testing standards defined in standards/testing-standard.md from the mpt-extension-skills repository.

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

In `@tests/unit/resources/integration/test_extension_media.py` around lines 93 -
107, The test_extension_media_create currently only asserts path/method/response
but should also validate the multipart request body; update the test (in
test_extension_media_create) to inspect mock_route.calls[0].request (e.g.,
request.content or request.read() and headers) and assert the multipart payload
contains a part named with the expected data key (match your _upload_data_key,
e.g., "media") containing the JSON payload, and a part named with the expected
file key (match your _upload_file_key, e.g., "file") containing the image bytes
and correct filename/Content-Type; ensure Content-Type is multipart/form-data
and boundary is present so regressions in media/file part names or composition
fail the test.
🤖 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/integration/extension_media.py`:
- Around line 74-87: Update the two docstrings that currently reference the
endpoint placeholder "{extensionId}" to use the configured template
"{extension_id}" so they match the endpoint definition; specifically, edit the
module-level docstring and the AsyncExtensionMediaService class docstring (class
name: AsyncExtensionMediaService) to replace "{extensionId}" with
"{extension_id}" for consistency with the endpoint template.

In `@tests/unit/resources/integration/test_extension_media.py`:
- Around line 93-107: The test_extension_media_create currently only asserts
path/method/response but should also validate the multipart request body; update
the test (in test_extension_media_create) to inspect mock_route.calls[0].request
(e.g., request.content or request.read() and headers) and assert the multipart
payload contains a part named with the expected data key (match your
_upload_data_key, e.g., "media") containing the JSON payload, and a part named
with the expected file key (match your _upload_file_key, e.g., "file")
containing the image bytes and correct filename/Content-Type; ensure
Content-Type is multipart/form-data and boundary is present so regressions in
media/file part names or composition fail the test.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro

Run ID: 3569fbd6-9442-434e-ba65-607bb078be6e

📥 Commits

Reviewing files that changed from the base of the PR and between a6b5f31 and 52db84c.

📒 Files selected for processing (12)
  • e2e_config.test.json
  • mpt_api_client/resources/integration/extension_media.py
  • mpt_api_client/resources/integration/extensions.py
  • mpt_api_client/resources/integration/mixins/__init__.py
  • mpt_api_client/resources/integration/mixins/media_mixin.py
  • mpt_api_client/resources/integration/mixins/publishable_mixin.py
  • tests/e2e/integration/extension_media/__init__.py
  • tests/e2e/integration/extension_media/conftest.py
  • tests/e2e/integration/extension_media/test_async_extension_media.py
  • tests/e2e/integration/extension_media/test_sync_extension_media.py
  • tests/unit/resources/integration/mixins/test_media_mixin.py
  • tests/unit/resources/integration/test_extension_media.py

albertsola and others added 3 commits April 10, 2026 09:41
…endpoint

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sionId}/media

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@albertsola albertsola force-pushed the MPT-19905/integration-extension-media branch from 52db84c to 79174cd Compare April 10, 2026 08:51
@sonarqubecloud
Copy link
Copy Markdown

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/integration/extension_media.py (1)

66-90: Remove redundant delete mixins from service inheritance.

Both DeleteMixin (line 72) and AsyncDeleteMixin (line 86) are redundant because ModifiableResourceMixin and AsyncModifiableResourceMixin already inherit delete behavior respectively.

♻️ Suggested cleanup
 class ExtensionMediaService(
     MediaMixin[ExtensionMedia],
     PublishableMixin[ExtensionMedia],
     DownloadFileMixin[ExtensionMedia],
     CreateFileMixin[ExtensionMedia],
     ModifiableResourceMixin[ExtensionMedia],
-    DeleteMixin,
     CollectionMixin[ExtensionMedia],
     Service[ExtensionMedia],
     ExtensionMediaServiceConfig,
 ):
@@
 class AsyncExtensionMediaService(
     AsyncMediaMixin[ExtensionMedia],
     AsyncPublishableMixin[ExtensionMedia],
     AsyncDownloadFileMixin[ExtensionMedia],
     AsyncCreateFileMixin[ExtensionMedia],
     AsyncModifiableResourceMixin[ExtensionMedia],
-    AsyncDeleteMixin,
     AsyncCollectionMixin[ExtensionMedia],
     AsyncService[ExtensionMedia],
     ExtensionMediaServiceConfig,
 ):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mpt_api_client/resources/integration/extension_media.py` around lines 66 -
90, Remove the redundant DeleteMixin and AsyncDeleteMixin entries from the
ExtensionMediaService and AsyncExtensionMediaService class inheritance lists
respectively; ModifiableResourceMixin and AsyncModifiableResourceMixin already
provide the delete behavior, so update the classes (ExtensionMediaService and
AsyncExtensionMediaService) to exclude DeleteMixin and AsyncDeleteMixin from
their bases.
🤖 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/integration/extension_media.py`:
- Around line 66-90: Remove the redundant DeleteMixin and AsyncDeleteMixin
entries from the ExtensionMediaService and AsyncExtensionMediaService class
inheritance lists respectively; ModifiableResourceMixin and
AsyncModifiableResourceMixin already provide the delete behavior, so update the
classes (ExtensionMediaService and AsyncExtensionMediaService) to exclude
DeleteMixin and AsyncDeleteMixin from their bases.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro

Run ID: 2a87d78f-0cd3-420a-a2a6-d0636f952253

📥 Commits

Reviewing files that changed from the base of the PR and between 52db84c and 79174cd.

📒 Files selected for processing (10)
  • mpt_api_client/resources/integration/extension_media.py
  • mpt_api_client/resources/integration/extensions.py
  • mpt_api_client/resources/integration/mixins/__init__.py
  • mpt_api_client/resources/integration/mixins/media_mixin.py
  • tests/e2e/integration/extension_media/__init__.py
  • tests/e2e/integration/extension_media/conftest.py
  • tests/e2e/integration/extension_media/test_async_extension_media.py
  • tests/e2e/integration/extension_media/test_sync_extension_media.py
  • tests/unit/resources/integration/mixins/test_media_mixin.py
  • tests/unit/resources/integration/test_extension_media.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • mpt_api_client/resources/integration/extensions.py
  • tests/e2e/integration/extension_media/test_sync_extension_media.py
  • tests/e2e/integration/extension_media/test_async_extension_media.py

@albertsola albertsola merged commit 235d013 into main Apr 10, 2026
4 checks passed
@albertsola albertsola deleted the MPT-19905/integration-extension-media branch April 10, 2026 11:56
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.

3 participants