Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion e2e_config.test.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,6 @@
"notifications.category.id": "NTC-6157-0397",
"notifications.message.id": "MSG-0000-6215-1019-0139",
"notifications.subscriber.id": "NTS-0829-7123-7123",
"integration.extension.id": "EXT-4401-0953"
"integration.extension.id": "EXT-6587-4477",
"integration.installation.id": "EXI-0022-3978-5547"
}
69 changes: 69 additions & 0 deletions mpt_api_client/resources/integration/installations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from mpt_api_client.http import AsyncService, Service
from mpt_api_client.http.mixins import (
AsyncCollectionMixin,
AsyncManagedResourceMixin,
CollectionMixin,
ManagedResourceMixin,
)
from mpt_api_client.models import Model
from mpt_api_client.models.model import BaseModel
from mpt_api_client.resources.integration.mixins import (
AsyncInstallationMixin,
InstallationMixin,
)


class Installation(Model):
"""Installation resource.

Attributes:
name: Installation name.
revision: Revision number.
account: Reference to the account.
extension: Reference to the extension.
status: Installation status (Invited, Installed, Uninstalled, Expired).
configuration: Installation configuration data.
invitation: Invitation details.
modules: Modules included in the installation.
terms: Accepted terms for this installation.
audit: Audit information (created, updated, invited, installed, expired, uninstalled).
"""

name: str | None
revision: int | None
account: BaseModel | None
extension: BaseModel | None
status: str | None
configuration: BaseModel | None
invitation: BaseModel | None
modules: list[BaseModel] | None
terms: list[BaseModel] | None
audit: BaseModel | None


class InstallationsServiceConfig:
"""Installations service configuration."""

_endpoint = "/public/v1/integration/installations"
_model_class = Installation
_collection_key = "data"


class InstallationsService(
InstallationMixin[Installation],
ManagedResourceMixin[Installation],
CollectionMixin[Installation],
Service[Installation],
InstallationsServiceConfig,
):
"""Sync service for the /public/v1/integration/installations endpoint."""


class AsyncInstallationsService(
AsyncInstallationMixin[Installation],
AsyncManagedResourceMixin[Installation],
AsyncCollectionMixin[Installation],
AsyncService[Installation],
InstallationsServiceConfig,
):
"""Async service for the /public/v1/integration/installations endpoint."""
14 changes: 14 additions & 0 deletions mpt_api_client/resources/integration/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
AsyncExtensionsService,
ExtensionsService,
)
from mpt_api_client.resources.integration.installations import (
AsyncInstallationsService,
InstallationsService,
)


class Integration:
Expand All @@ -16,6 +20,11 @@ def extensions(self) -> ExtensionsService:
"""Extensions service."""
return ExtensionsService(http_client=self.http_client)

@property
def installations(self) -> InstallationsService:
"""Installations service."""
return InstallationsService(http_client=self.http_client)


class AsyncIntegration:
"""Async Integration MPT API Module."""
Expand All @@ -27,3 +36,8 @@ def __init__(self, *, http_client: AsyncHTTPClient):
def extensions(self) -> AsyncExtensionsService:
"""Extensions service."""
return AsyncExtensionsService(http_client=self.http_client)

@property
def installations(self) -> AsyncInstallationsService:
"""Installations service."""
return AsyncInstallationsService(http_client=self.http_client)
6 changes: 6 additions & 0 deletions mpt_api_client/resources/integration/mixins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@
AsyncExtensionMixin,
ExtensionMixin,
)
from mpt_api_client.resources.integration.mixins.installation_mixin import (
AsyncInstallationMixin,
InstallationMixin,
)

__all__ = [ # noqa: WPS410
"AsyncExtensionMixin",
"AsyncInstallationMixin",
"ExtensionMixin",
"InstallationMixin",
]
105 changes: 105 additions & 0 deletions mpt_api_client/resources/integration/mixins/installation_mixin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from mpt_api_client.models import ResourceData


class InstallationMixin[Model]:
"""Mixin that adds installation lifecycle actions: invite, install, uninstall, expire."""

def invite(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
"""Invite an installation.

Args:
resource_id: Installation ID.
resource_data: Optional request body.

Returns:
Updated installation.
"""
return self._resource(resource_id).post("invite", json=resource_data) # type: ignore[attr-defined, no-any-return]

def install(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
"""Mark an installation as installed.

Args:
resource_id: Installation ID.
resource_data: Optional request body.

Returns:
Updated installation.
"""
return self._resource(resource_id).post("install", json=resource_data) # type: ignore[attr-defined, no-any-return]

def uninstall(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
"""Uninstall an installation.

Args:
resource_id: Installation ID.
resource_data: Optional request body.

Returns:
Updated installation.
"""
return self._resource(resource_id).post("uninstall", json=resource_data) # type: ignore[attr-defined, no-any-return]

def expire(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
"""Expire an installation.

Args:
resource_id: Installation ID.
resource_data: Optional request body.

Returns:
Updated installation.
"""
return self._resource(resource_id).post("expire", json=resource_data) # type: ignore[attr-defined, no-any-return]


class AsyncInstallationMixin[Model]:
"""Async mixin for installation lifecycle actions: invite, install, uninstall, expire."""

async def invite(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
"""Invite an installation.

Args:
resource_id: Installation ID.
resource_data: Optional request body.

Returns:
Updated installation.
"""
return await self._resource(resource_id).post("invite", json=resource_data) # type: ignore[attr-defined, no-any-return]

async def install(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
"""Mark an installation as installed.

Args:
resource_id: Installation ID.
resource_data: Optional request body.

Returns:
Updated installation.
"""
return await self._resource(resource_id).post("install", json=resource_data) # type: ignore[attr-defined, no-any-return]

async def uninstall(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
"""Uninstall an installation.

Args:
resource_id: Installation ID.
resource_data: Optional request body.

Returns:
Updated installation.
"""
return await self._resource(resource_id).post("uninstall", json=resource_data) # type: ignore[attr-defined, no-any-return]

async def expire(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
"""Expire an installation.

Args:
resource_id: Installation ID.
resource_data: Optional request body.

Returns:
Updated installation.
"""
return await self._resource(resource_id).post("expire", json=resource_data) # type: ignore[attr-defined, no-any-return]
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ per-file-ignores = [
"tests/unit/resources/catalog/test_products.py: WPS202 WPS210",
"tests/e2e/integration/*.py: WPS453",
"tests/e2e/integration/extensions/*.py: WPS453 WPS202",
"tests/e2e/integration/installations/*.py: WPS453 WPS202",
"tests/unit/resources/integration/*.py: WPS202 WPS210 WPS218 WPS453",
"tests/unit/resources/integration/mixins/*.py: WPS453 WPS202",
"tests/unit/resources/commerce/*.py: WPS202 WPS204",
Expand Down
8 changes: 4 additions & 4 deletions tests/e2e/integration/extensions/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@


@pytest.fixture
def extensions_service(mpt_ops):
return mpt_ops.integration.extensions
def extensions_service(mpt_vendor):
return mpt_vendor.integration.extensions


@pytest.fixture
def async_extensions_service(async_mpt_ops):
return async_mpt_ops.integration.extensions
def async_extensions_service(async_mpt_vendor):
return async_mpt_vendor.integration.extensions


@pytest.fixture(scope="session")
Expand Down
5 changes: 1 addition & 4 deletions tests/e2e/integration/extensions/test_async_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
pytestmark = [pytest.mark.flaky]


@pytest.mark.skip(reason="unable to create extensions for testing")

def test_create_extension(async_created_extension, extension_data):
Comment on lines 9 to 11
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix formatting to resolve pipeline failure.

The pipeline reports ruff format --check failed. Removing the @pytest.mark.skip decorator left an extra blank line, resulting in 3 consecutive blank lines before test_create_extension. Ruff format expects exactly 2 blank lines between top-level definitions.

🛠️ Proposed fix
 pytestmark = [pytest.mark.flaky]
 
 
-
 def test_create_extension(async_created_extension, extension_data):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/integration/extensions/test_async_extensions.py` around lines 9 -
11, The test file has three consecutive blank lines before the top-level test
function test_create_extension causing ruff format --check to fail; edit the
file to remove one blank line so there are exactly two blank lines separating
top-level definitions (ensure there are two blank lines immediately before def
test_create_extension and no leftover `@pytest.mark.skip` or stray blank lines).

result = async_created_extension.name

Expand All @@ -27,7 +27,6 @@ async def test_get_extension_not_found(async_extensions_service):
await async_extensions_service.get(bogus_id)


@pytest.mark.skip(reason="unable to create extensions for testing")
async def test_update_extension(
async_extensions_service, async_created_extension, logo_fd, short_uuid
):
Expand All @@ -40,7 +39,6 @@ async def test_update_extension(
assert result.name == update_data["name"]


@pytest.mark.skip(reason="unable to create extensions for testing")
async def test_delete_extension(async_extensions_service, async_created_extension):
await async_extensions_service.delete(async_created_extension.id) # act

Expand All @@ -51,7 +49,6 @@ async def test_filter_extensions(async_extensions_service, extension_id):
) # act


@pytest.mark.skip(reason="unable to create extensions for testing")
async def test_download_icon(async_extensions_service, async_created_extension):
result = await async_extensions_service.download_icon(async_created_extension.id)

Expand Down
3 changes: 0 additions & 3 deletions tests/e2e/integration/extensions/test_sync_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
]


@pytest.mark.skip(reason="unable to create extensions for testing")
def test_create_extension(created_extension, extension_data):
result = created_extension.name

Expand All @@ -29,7 +28,6 @@ def test_get_extension_not_found(extensions_service):
extensions_service.get(bogus_id)


@pytest.mark.skip(reason="unable to create extensions for testing")
def test_update_extension(extensions_service, created_extension, logo_fd, short_uuid):
update_data = {"name": f"e2e - please delete {short_uuid}"}

Expand All @@ -38,7 +36,6 @@ def test_update_extension(extensions_service, created_extension, logo_fd, short_
assert result.name == update_data["name"]


@pytest.mark.skip(reason="unable to create extensions for testing")
def test_delete_extension(extensions_service, created_extension):
extensions_service.delete(created_extension.id) # act

Expand Down
Empty file.
51 changes: 51 additions & 0 deletions tests/e2e/integration/installations/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import pytest

from mpt_api_client.exceptions import MPTAPIError


@pytest.fixture
def installations_service(mpt_ops):
return mpt_ops.integration.installations


@pytest.fixture
def async_installations_service(async_mpt_ops):
return async_mpt_ops.integration.installations


@pytest.fixture(scope="session")
def installation_id(e2e_config):
return e2e_config["integration.installation.id"]


@pytest.fixture
def installation_data():
return {
"extension": {"id": "EXT-0000-0000"},
"account": {"id": "ACC-0000-0000"},
"modules": [],
}


@pytest.fixture
def created_installation(installations_service, installation_data):
installation = installations_service.create(installation_data)

yield installation

try:
installations_service.delete(installation.id)
except MPTAPIError as error:
print(f"TEARDOWN - Unable to delete installation {installation.id}: {error.title}") # noqa: WPS421


@pytest.fixture
async def async_created_installation(async_installations_service, installation_data):
installation = await async_installations_service.create(installation_data)

yield installation

try:
await async_installations_service.delete(installation.id)
except MPTAPIError as error:
print(f"TEARDOWN - Unable to delete installation {installation.id}: {error.title}") # noqa: WPS421
Loading
Loading