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
15 changes: 14 additions & 1 deletion seed/accounts/accounts.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
import logging
import os

from mpt_api_client.resources.accounts.account import Account
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
from seed.helper import init_resource

logger = logging.getLogger(__name__)


async def seed_accounts() -> None: # noqa: WPS217
async def get_account() -> Account: # noqa: RUF029 async for compatibility purposes with init_resource
"""Get account ID from environment variable."""
account_id = os.getenv("CLIENT_ACCOUNT_ID")
if not account_id:
raise ValueError("CLIENT_ACCOUNT_ID environment variable is required")
return Account({"id": account_id})
Comment on lines +16 to +21
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix Ruff findings: remove unused noqa + address TRY003 with a custom exception
Ruff reports # noqa: RUF029 is unused and the ValueError("...") message triggers TRY003; both are straightforward to fix.

+class MissingClientAccountIdError(ValueError):
+    """CLIENT_ACCOUNT_ID environment variable is required."""
+
 async def get_account() -> Account:  # noqa: RUF029 async for compatibility purposes with init_resource
     """Get account ID from environment variable."""
     account_id = os.getenv("CLIENT_ACCOUNT_ID")
     if not account_id:
-        raise ValueError("CLIENT_ACCOUNT_ID environment variable is required")
+        raise MissingClientAccountIdError
     return Account({"id": account_id})

…and then remove the now-unneeded # noqa: RUF029.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.14.8)

16-16: Unused noqa directive (non-enabled: RUF029)

Remove unused noqa directive

(RUF100)


20-20: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In seed/accounts/accounts.py around lines 16 to 21, remove the unnecessary "#
noqa: RUF029" and replace the generic ValueError raised when CLIENT_ACCOUNT_ID
is missing with a custom exception (e.g., define a MissingClientAccountError in
this module or import one from a shared errors module) so Ruff's TRY003 is
satisfied; add the custom exception definition near the top of the file (or
import it), then raise MissingClientAccountError("CLIENT_ACCOUNT_ID environment
variable is required") instead of ValueError, and delete the now-unneeded noqa
comment.



async def seed_accounts() -> None: # noqa: WPS213 WPS217
"""Seed accounts data including account."""
logger.debug("Seeding accounts ...")
await init_resource("accounts.account.id", get_account)

await seed_seller()
await seed_buyer()
await seed_module()
Expand Down
48 changes: 8 additions & 40 deletions seed/accounts/api_tokens.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,23 @@
import logging
import os

from dependency_injector.wiring import Provide, inject

from mpt_api_client import AsyncMPTClient
from mpt_api_client.resources.accounts.api_tokens import ApiToken
from seed.container import Container
from seed.context import Context
from seed.helper import init_resource, require_context_id

logger = logging.getLogger(__name__)


@inject
async def get_api_token(
context: Context = Provide[Container.context],
mpt_ops: AsyncMPTClient = Provide[Container.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 = Provide[Container.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")
account_id = require_context_id(context, "accounts.account.id", "creating API token")
module_id = require_context_id(context, "accounts.module.id", "creating API token")
return {
"account": {"id": account_id},
"name": "E2E Seeded API Token",
Expand All @@ -49,27 +28,16 @@ def build_api_token_data(


@inject
async def init_api_token(
context: Context = Provide[Container.context],
async def create_api_token(
mpt_ops: AsyncMPTClient = Provide[Container.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
"""Creates an API token."""
api_token_data = build_api_token_data()
return await mpt_ops.accounts.api_tokens.create(api_token_data)


@inject
async def seed_api_token() -> None:
"""Seed API token."""
logger.debug("Seeding API token ...")
await init_api_token()
await init_resource("accounts.api_token.id", create_api_token)
logger.debug("Seeding API token completed.")
57 changes: 9 additions & 48 deletions seed/accounts/buyer.py
Original file line number Diff line number Diff line change
@@ -1,47 +1,21 @@
import logging
import os

from dependency_injector.wiring import Provide, inject

from mpt_api_client import AsyncMPTClient
from mpt_api_client.resources.accounts.buyers import Buyer
from seed.container import Container
from seed.context import Context
from seed.helper import init_resource, require_context_id
from seed.static.static import ICON

logger = logging.getLogger(__name__)


@inject
async def get_buyer(
context: Context = Provide[Container.context],
mpt_operations: AsyncMPTClient = Provide[Container.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 = Provide[Container.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.")
buyer_account_id = require_context_id(context, "accounts.account.id", "creating buyer")
seller_id = require_context_id(context, "accounts.seller.id", "creating buyer")
return {
"name": "E2E Seeded Buyer",
"account": {"id": buyer_account_id},
Expand All @@ -62,31 +36,18 @@ def build_buyer_data(context: Context = Provide[Container.context]) -> dict[str,


@inject
async def init_buyer(
async def create_buyer(
context: Context = Provide[Container.context],
mpt_operations: AsyncMPTClient = Provide[Container.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 ICON.open("rb") as icon_fd:
created = await mpt_operations.accounts.buyers.create(buyer_data, file=icon_fd)
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") # type: ignore[unreachable]
raise ValueError("Buyer creation failed")
logger.info("Buyer found: %s", buyer.id)
return buyer
"""Creates a buyer."""
buyer_data = build_buyer_data(context=context)
with ICON.open("rb") as icon_fd:
return await mpt_operations.accounts.buyers.create(buyer_data, file=icon_fd)


@inject
async def seed_buyer() -> None:
"""Seed buyer."""
logger.debug("Seeding buyer ...")
await init_buyer()
await init_resource("accounts.buyer.id", create_buyer)
logger.debug("Seeding buyer completed.")
70 changes: 13 additions & 57 deletions seed/accounts/licensee.py
Original file line number Diff line number Diff line change
@@ -1,55 +1,27 @@
import logging
import os

from dependency_injector.wiring import Provide, inject

from mpt_api_client import AsyncMPTClient
from mpt_api_client.resources.accounts.licensees import Licensee
from seed.container import Container
from seed.context import Context
from seed.helper import init_resource, require_context_id
from seed.static.static import ICON

logger = logging.getLogger(__name__)


@inject
async def get_licensee(
context: Context = Provide[Container.context],
mpt_client: AsyncMPTClient = Provide[Container.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 = Provide[Container.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")
account_id = require_context_id(context, "accounts.account.id", "creating licensee")
seller_id = require_context_id(context, "accounts.seller.id", "create licensee")
buyer_id = require_context_id(context, "accounts.buyer.id", "create licensee")
user_group_id = require_context_id(context, "accounts.user_group.id", "create licensee")
Comment on lines +20 to +23
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Unify action strings passed to require_context_id for consistent error messages.
Line 20 uses "creating licensee" while Lines 21-23 use "create licensee". Pick one phrasing (e.g., "create licensee") so missing-context errors read consistently.

🤖 Prompt for AI Agents
In seed/accounts/licensee.py around lines 20 to 23, the action string passed to
require_context_id is inconsistent: line 20 uses "creating licensee" while lines
21–23 use "create licensee"; change the action on line 20 to "create licensee"
so all four calls use the identical phrase and produce consistent
missing-context error messages.


licensee_type = "Client"
return {
"name": "E2E Seeded Licensee",
Expand All @@ -65,39 +37,23 @@ def build_licensee_data( # noqa: WPS238
"buyer": {"id": buyer_id},
"account": {"id": account_id},
"eligibility": {"client": True, "partner": False},
"groups": [{"id": group.id}],
"groups": [{"id": user_group_id}],
"type": licensee_type,
"status": "Enabled",
"defaultLanguage": "en-US",
}


@inject
async def init_licensee(
context: Context = Provide[Container.context],
mpt_client: AsyncMPTClient = Provide[Container.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 ICON.open("rb") as icon_file:
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") # type: ignore[unreachable]
raise ValueError("Licensee creation failed")
logger.info("Licensee found: %s", licensee.id)
return licensee
async def create_licensee(mpt_client: AsyncMPTClient = Provide[Container.mpt_client]) -> Licensee:
"""Create licensee."""
licensee_data = build_licensee_data()
with ICON.open("rb") as icon_fd:
return await mpt_client.accounts.licensees.create(licensee_data, file=icon_fd)


@inject
async def seed_licensee() -> None:
"""Seed licensee."""
logger.debug("Seeding licensee ...")
await init_licensee()
await init_resource("accounts.licensee.id", create_licensee)
logger.info("Seeding licensee completed.")
64 changes: 13 additions & 51 deletions seed/accounts/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,67 +6,29 @@
from mpt_api_client.resources.accounts.modules import Module
from mpt_api_client.rql.query_builder import RQLQuery
from seed.container import Container
from seed.context import Context
from seed.helper import init_resource

logger = logging.getLogger(__name__)


@inject
async def get_module(
context: Context = Provide[Container.context],
async def find_module(
mpt_operations: AsyncMPTClient = Provide[Container.mpt_operations],
) -> Module | None:
"""Get module from context or fetch from API."""
module_id = context.get_string("accounts.module.id")
if not module_id:
return None
try:
module = context.get_resource("accounts.module", module_id)
except ValueError:
module = None
if not isinstance(module, Module):
module = await mpt_operations.accounts.modules.get(module_id)
context.set_resource("accounts.module", module)
context["accounts.module.id"] = module.id
return module
return module
) -> Module:
"""Selects an existing module to use for seeding purposes.

Currently selects the "Access Management" module from the marketplace.
"""
filtered = mpt_operations.accounts.modules.filter(RQLQuery(name="Access Management"))
modules = [module async for module in filtered.iterate()]

@inject
async def refresh_module(
context: Context = Provide[Container.context],
mpt_operations: AsyncMPTClient = Provide[Container.mpt_operations],
) -> Module | None:
"""Refresh module in context (always fetch)."""
module = await get_module(context=context, mpt_operations=mpt_operations)
if module is None:
filtered_modules = mpt_operations.accounts.modules.filter(
RQLQuery(name="Access Management")
)
modules = [mod async for mod in filtered_modules.iterate()]
if modules:
first_module = modules[0]
if isinstance(first_module, Module):
context["accounts.module.id"] = first_module.id
context.set_resource("accounts.module", first_module)
return first_module
logger.warning("First module is not a Module instance.") # type: ignore[unreachable]
return None
logger.warning("Module 'Access Management' not found.")
return None
return module
if not modules:
raise ValueError("Module 'Access Management' not found.")
return modules[0]


@inject
async def seed_module() -> Module:
async def seed_module() -> None:
"""Seed module."""
logger.debug("Seeding module ...")
existing_module = await get_module()
if existing_module is None:
refreshed = await refresh_module()
logger.debug("Seeding module completed.")
if refreshed is None:
raise ValueError("Could not seed module: no valid Module found.")
return refreshed
await init_resource("accounts.module.id", find_module)
logger.debug("Seeding module completed.")
return existing_module
Loading