Skip to content
Merged
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
10 changes: 5 additions & 5 deletions mpt_api_client/resources/commerce/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,21 @@ class AssetTemplate(Model):
"""Asset template resource."""


class AssetsServiceConfig:
class AssetServiceConfig:
"""Assets service config."""

_endpoint = "/public/v1/commerce/assets"
_model_class = Asset
_collection_key = "data"


class AssetsService(
class AssetService(
CreateMixin[Asset],
UpdateMixin[Asset],
GetMixin[Asset],
CollectionMixin[Asset],
Service[Asset],
AssetsServiceConfig,
AssetServiceConfig,
):
"""Assets service."""

Expand Down Expand Up @@ -63,13 +63,13 @@ def render(self, asset_id: str) -> AssetTemplate:
return AssetTemplate.from_response(response)


class AsyncAssetsService(
class AsyncAssetService(
AsyncCreateMixin[Asset],
AsyncUpdateMixin[Asset],
AsyncGetMixin[Asset],
AsyncCollectionMixin[Asset],
AsyncService[Asset],
AssetsServiceConfig,
AssetServiceConfig,
):
"""Asynchronous Assets service."""

Expand Down
10 changes: 5 additions & 5 deletions mpt_api_client/resources/commerce/commerce.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from mpt_api_client.http import AsyncHTTPClient, HTTPClient
from mpt_api_client.resources.commerce.agreements import AgreementsService, AsyncAgreementsService
from mpt_api_client.resources.commerce.assets import AssetsService, AsyncAssetsService
from mpt_api_client.resources.commerce.assets import AssetService, AsyncAssetService
from mpt_api_client.resources.commerce.orders import AsyncOrdersService, OrdersService
from mpt_api_client.resources.commerce.subscriptions import (
AsyncSubscriptionsService,
Expand Down Expand Up @@ -30,9 +30,9 @@ def subscriptions(self) -> SubscriptionsService:
return SubscriptionsService(http_client=self.http_client)

@property
def assets(self) -> AssetsService:
def assets(self) -> AssetService:
"""Asset service."""
return AssetsService(http_client=self.http_client)
return AssetService(http_client=self.http_client)


class AsyncCommerce:
Expand All @@ -57,6 +57,6 @@ def subscriptions(self) -> AsyncSubscriptionsService:
return AsyncSubscriptionsService(http_client=self.http_client)

@property
def assets(self) -> AsyncAssetsService:
def assets(self) -> AsyncAssetService:
"""Asset service."""
return AsyncAssetsService(http_client=self.http_client)
return AsyncAssetService(http_client=self.http_client)
6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dev = [
"ipython==9.*",
"mypy==1.15.*",
"pre-commit==4.2.*",
"pyfakefs==5.10.*",
"pytest==8.3.*",
"pytest-asyncio==1.2.*",
"pytest-cov==6.1.*",
Expand Down Expand Up @@ -114,7 +115,7 @@ per-file-ignores = [
"mpt_api_client/http/mixins.py: WPS202 WPS204 WPS235",
"mpt_api_client/resources/*: WPS215",
"mpt_api_client/models/model.py: WPS215 WPS110",
"mpt_api_client/resources/accounts/*.py: WPS202 WPS215 WPS214 WPS235",
"mpt_api_client/resources/accounts/*.py: WPS202 WPS215 WPS214 WPS235 WPS453",
"mpt_api_client/resources/billing/*.py: WPS202 WPS204 WPS214 WPS215",
"mpt_api_client/resources/catalog/*.py: WPS110 WPS214 WPS215 WPS235",
"mpt_api_client/resources/catalog/mixins.py: WPS110 WPS202 WPS214 WPS215 WPS235",
Expand All @@ -130,7 +131,8 @@ per-file-ignores = [
"tests/e2e/accounts/*.py: WPS430 WPS202",
"tests/e2e/catalog/*.py: WPS202 WPS421",
"tests/e2e/catalog/items/*.py: WPS110 WPS202",
"tests/*: WPS432 WPS202"
"tests/*: WPS432 WPS202",
"seed/accounts/*.py: WPS453",
]

[tool.ruff]
Expand Down
23 changes: 23 additions & 0 deletions seed/accounts/accounts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import logging

from seed.accounts.api_tokens import seed_api_token
from seed.accounts.buyer import seed_buyer
from seed.accounts.licensee import seed_licensee
from seed.accounts.module import seed_module
from seed.accounts.seller import seed_seller
from seed.accounts.user_group import seed_user_group

logger = logging.getLogger(__name__)


async def seed_accounts() -> None: # noqa: WPS217
"""Seed accounts data including account."""
logger.debug("Seeding accounts ...")
await seed_seller()
await seed_buyer()
await seed_module()
await seed_api_token()
await seed_user_group()
await seed_licensee()

logger.debug("Seeded accounts completed.")
75 changes: 75 additions & 0 deletions seed/accounts/api_tokens.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import logging
import os

from dependency_injector.wiring import inject

from mpt_api_client import AsyncMPTClient
from mpt_api_client.resources.accounts.api_tokens import ApiToken
from seed.context import Context
from seed.defaults import DEFAULT_CONTEXT, DEFAULT_MPT_OPERATIONS

logger = logging.getLogger(__name__)


@inject
async def get_api_token(
context: Context = DEFAULT_CONTEXT,
mpt_ops: AsyncMPTClient = DEFAULT_MPT_OPERATIONS,
) -> ApiToken | None:
"""Get API token from context or fetch from API."""
api_token_id = context.get_string("accounts.api_token.id")
if not api_token_id:
return None
try:
api_token = context.get_resource("accounts.api_token", api_token_id)
except ValueError:
api_token = None
if not isinstance(api_token, ApiToken):
api_token = await mpt_ops.accounts.api_tokens.get(api_token_id)
context.set_resource("accounts.api_token", api_token)
context["accounts.api_token.id"] = api_token.id
return api_token
return api_token


@inject
def build_api_token_data(
context: Context = DEFAULT_CONTEXT,
) -> dict[str, object]:
"""Get API token data dictionary for creation."""
account_id = os.getenv("CLIENT_ACCOUNT_ID")
module_id = context.get_string("accounts.module.id")
return {
"account": {"id": account_id},
"name": "E2E Seeded API Token",
"description": "This is a seeded API token for end-to-end testing.",
"icon": "",
"modules": [{"id": module_id}],
}


@inject
async def init_api_token(
context: Context = DEFAULT_CONTEXT,
mpt_ops: AsyncMPTClient = DEFAULT_MPT_OPERATIONS,
) -> ApiToken:
"""Get or create API token."""
api_token = await get_api_token(context=context, mpt_ops=mpt_ops)
if api_token is None:
logger.debug("Creating API token ...")
api_token_data = build_api_token_data(context=context)
api_token = await mpt_ops.accounts.api_tokens.create(api_token_data)
context.set_resource("accounts.api_token", api_token)
context["accounts.api_token.id"] = api_token.id
logger.info("API token created: %s", api_token.id)
else:
logger.info("API token found: %s", api_token.id)
return api_token


@inject
async def seed_api_token() -> None:
"""Seed API token."""
logger.debug("Seeding API token ...")
await init_api_token()
logger.debug("Seeding API token completed.")
94 changes: 94 additions & 0 deletions seed/accounts/buyer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import logging
import os
import pathlib

from dependency_injector.wiring import inject

from mpt_api_client import AsyncMPTClient
from mpt_api_client.resources.accounts.buyers import Buyer
from seed.context import Context
from seed.defaults import DEFAULT_CONTEXT, DEFAULT_MPT_OPERATIONS

logger = logging.getLogger(__name__)

icon = pathlib.Path("seed/data/logo.png").resolve()


@inject
async def get_buyer(
context: Context = DEFAULT_CONTEXT,
mpt_operations: AsyncMPTClient = DEFAULT_MPT_OPERATIONS,
) -> Buyer | None:
"""Get buyer from context or fetch from API."""
buyer_id = context.get_string("accounts.buyer.id")
if not buyer_id:
return None
try:
buyer = context.get_resource("accounts.buyer", buyer_id)
except ValueError:
buyer = None
if not isinstance(buyer, Buyer):
buyer = await mpt_operations.accounts.buyers.get(buyer_id)
context.set_resource("accounts.buyer", buyer)
context["accounts.buyer.id"] = buyer.id
return buyer
return buyer


@inject
def build_buyer_data(context: Context = DEFAULT_CONTEXT) -> dict[str, object]:
"""Build buyer data dictionary for creation."""
buyer_account_id = os.getenv("CLIENT_ACCOUNT_ID")
if not buyer_account_id:
raise ValueError("CLIENT_ACCOUNT_ID environment variable is required")
seller_id = context.get_string("accounts.seller.id")
if not seller_id:
raise ValueError("accounts.seller.id missing from context; seed seller before buyer.")
return {
"name": "E2E Seeded Buyer",
"account": {"id": buyer_account_id},
"sellers": [{"id": seller_id}],
"contact": {
"firstName": "first",
"lastName": "last",
"email": "created.buyer@example.com",
},
"address": {
"addressLine1": "123 Main St",
"city": "Los Angeles",
"state": "CA",
"postCode": "12345",
"country": "US",
},
}


@inject
async def init_buyer(
context: Context = DEFAULT_CONTEXT,
mpt_operations: AsyncMPTClient = DEFAULT_MPT_OPERATIONS,
) -> Buyer:
"""Get or create buyer."""
buyer = await get_buyer(context=context, mpt_operations=mpt_operations)
if buyer is None:
buyer_data = build_buyer_data(context=context)
logger.debug("Creating buyer ...")
with open(str(icon), "rb") as icon_file: # noqa: PTH123
created = await mpt_operations.accounts.buyers.create(buyer_data, file=icon_file)
if isinstance(created, Buyer):
context.set_resource("accounts.buyer", created)
context["accounts.buyer.id"] = created.id
logger.info("Buyer created: %s", created.id)
return created
logger.warning("Buyer creation failed")
raise ValueError("Buyer creation failed")
logger.info("Buyer found: %s", buyer.id)
return buyer


@inject
async def seed_buyer() -> None:
"""Seed buyer."""
logger.debug("Seeding buyer ...")
await init_buyer()
logger.debug("Seeding buyer completed.")
105 changes: 105 additions & 0 deletions seed/accounts/licensee.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import logging
import os
import pathlib

from dependency_injector.wiring import inject

from mpt_api_client import AsyncMPTClient
from mpt_api_client.resources.accounts.licensees import Licensee
from seed.context import Context
from seed.defaults import DEFAULT_CONTEXT, DEFAULT_MPT_CLIENT

logger = logging.getLogger(__name__)

icon = pathlib.Path("seed/data/logo.png").resolve()


@inject
async def get_licensee(
context: Context = DEFAULT_CONTEXT,
mpt_client: AsyncMPTClient = DEFAULT_MPT_CLIENT,
) -> Licensee | None:
"""Get licensee from context or fetch from API."""
licensee_id = context.get_string("accounts.licensee.id")
if not licensee_id:
return None
try:
licensee = context.get_resource("accounts.licensee", licensee_id)
except ValueError:
licensee = None
if not isinstance(licensee, Licensee):
licensee = await mpt_client.accounts.licensees.get(licensee_id)
context.set_resource("accounts.licensee", licensee)
context["accounts.licensee.id"] = licensee.id
return licensee
return licensee


@inject
def build_licensee_data( # noqa: WPS238
context: Context = DEFAULT_CONTEXT,
) -> dict[str, object]:
"""Get licensee data dictionary for creation."""
account_id = os.getenv("CLIENT_ACCOUNT_ID")
if not account_id:
raise ValueError("CLIENT_ACCOUNT_ID environment variable is required")
seller_id = context.get_string("accounts.seller.id")
if not seller_id:
raise ValueError("Seller ID is required in context")
buyer_id = context.get_string("accounts.buyer.id")
if not buyer_id:
raise ValueError("Buyer ID is required in context")
group = context.get_resource("accounts.user_group")
if group is None:
raise ValueError("User group is required in context")
licensee_type = "Client"
return {
"name": "E2E Seeded Licensee",
"address": {
"addressLine1": "123 Main St",
"city": "Los Angeles",
"state": "CA",
"postCode": "67890",
"country": "US",
},
"useBuyerAddress": False,
"seller": {"id": seller_id},
"buyer": {"id": buyer_id},
"account": {"id": account_id},
"eligibility": {"client": True, "partner": False},
"groups": [{"id": group.id}],
"type": licensee_type,
"status": "Enabled",
"defaultLanguage": "en-US",
}


@inject
async def init_licensee(
context: Context = DEFAULT_CONTEXT,
mpt_client: AsyncMPTClient = DEFAULT_MPT_CLIENT,
) -> Licensee:
"""Get or create licensee."""
licensee = await get_licensee(context=context, mpt_client=mpt_client)
if licensee is None:
licensee_data = build_licensee_data(context=context)
logger.debug("Creating licensee ...")
with open(str(icon), "rb") as icon_file: # noqa: PTH123
created = await mpt_client.accounts.licensees.create(licensee_data, file=icon_file)
if isinstance(created, Licensee):
context.set_resource("accounts.licensee", created)
context["accounts.licensee.id"] = created.id
logger.info("Licensee created: %s", created.id)
return created
logger.warning("Licensee creation failed")
raise ValueError("Licensee creation failed")
logger.info("Licensee found: %s", licensee.id)
return licensee


@inject
async def seed_licensee() -> None:
"""Seed licensee."""
logger.debug("Seeding licensee ...")
await init_licensee()
logger.info("Seeding licensee completed.")
Loading