Skip to content

Commit fcf3f67

Browse files
author
Robert Segal
committed
Seeded accounts e2e data
1 parent c24cde9 commit fcf3f67

25 files changed

+1234
-28
lines changed

pyproject.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ per-file-ignores = [
113113
"mpt_api_client/http/mixins.py: WPS202 WPS204 WPS235",
114114
"mpt_api_client/resources/*: WPS215",
115115
"mpt_api_client/models/model.py: WPS215 WPS110",
116-
"mpt_api_client/resources/accounts/*.py: WPS202 WPS215 WPS214 WPS235",
116+
"mpt_api_client/resources/accounts/*.py: WPS202 WPS215 WPS214 WPS235 WPS453",
117117
"mpt_api_client/resources/billing/*.py: WPS202 WPS204 WPS214 WPS215",
118118
"mpt_api_client/resources/catalog/*.py: WPS110 WPS214 WPS215 WPS235",
119119
"mpt_api_client/resources/catalog/mixins.py: WPS110 WPS202 WPS214 WPS215 WPS235",
@@ -129,7 +129,8 @@ per-file-ignores = [
129129
"tests/e2e/accounts/*.py: WPS430 WPS202",
130130
"tests/e2e/catalog/*.py: WPS202 WPS421",
131131
"tests/e2e/catalog/items/*.py: WPS110 WPS202",
132-
"tests/*: WPS432 WPS202"
132+
"tests/*: WPS432 WPS202",
133+
"seed/accounts/*.py: WPS453",
133134
]
134135

135136
[tool.ruff]

seed/accounts/accounts.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import logging
2+
3+
from seed.accounts.api_tokens import seed_api_token
4+
from seed.accounts.buyer import seed_buyer
5+
from seed.accounts.licensee import seed_licensee
6+
from seed.accounts.module import seed_module
7+
from seed.accounts.seller import seed_seller
8+
from seed.accounts.user_group import seed_user_group
9+
10+
logger = logging.getLogger(__name__)
11+
12+
13+
async def seed_accounts() -> None: # noqa: WPS217
14+
"""Seed accounts data including account."""
15+
logger.debug("Seeding accounts ...")
16+
await seed_seller()
17+
await seed_buyer()
18+
await seed_module()
19+
await seed_api_token()
20+
await seed_user_group()
21+
await seed_licensee()
22+
23+
logger.debug("Seeded accounts completed.")

seed/accounts/api_tokens.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import logging
2+
import os
3+
4+
from dependency_injector.wiring import inject
5+
6+
from mpt_api_client import AsyncMPTClient
7+
from mpt_api_client.resources.accounts.api_tokens import ApiToken
8+
from seed.context import Context
9+
from seed.defaults import DEFAULT_CONTEXT, DEFAULT_MPT_OPERATIONS
10+
11+
logger = logging.getLogger(__name__)
12+
13+
14+
@inject
15+
async def get_api_token(
16+
context: Context = DEFAULT_CONTEXT,
17+
mpt_ops: AsyncMPTClient = DEFAULT_MPT_OPERATIONS,
18+
) -> ApiToken | None:
19+
"""Get API token from context or fetch from API."""
20+
api_token_id = context.get_string("accounts.api_token.id")
21+
if not api_token_id:
22+
return None
23+
try:
24+
api_token = context.get_resource("accounts.api_token", api_token_id)
25+
except ValueError:
26+
api_token = None
27+
if not isinstance(api_token, ApiToken):
28+
api_token = await mpt_ops.accounts.api_tokens.get(api_token_id)
29+
context.set_resource("accounts.api_token", api_token)
30+
context["accounts.api_token.id"] = api_token.id
31+
return api_token
32+
return api_token
33+
34+
35+
@inject
36+
def build_api_token_data(
37+
context: Context = DEFAULT_CONTEXT,
38+
) -> dict[str, object]:
39+
"""Get API token data dictionary for creation."""
40+
account_id = os.getenv("CLIENT_ACCOUNT_ID")
41+
module_id = context.get_string("accounts.module.id")
42+
return {
43+
"account": {"id": account_id},
44+
"name": "E2E Seeded API Token",
45+
"description": "This is a seeded API token for end-to-end testing.",
46+
"icon": "",
47+
"modules": [{"id": module_id}],
48+
}
49+
50+
51+
@inject
52+
async def init_api_token(
53+
context: Context = DEFAULT_CONTEXT,
54+
mpt_ops: AsyncMPTClient = DEFAULT_MPT_OPERATIONS,
55+
) -> ApiToken:
56+
"""Get or create API token."""
57+
api_token = await get_api_token(context=context, mpt_ops=mpt_ops)
58+
if api_token is None:
59+
logger.debug("Creating API token ...")
60+
api_token_data = build_api_token_data(context=context)
61+
api_token = await mpt_ops.accounts.api_tokens.create(api_token_data)
62+
context.set_resource("accounts.api_token", api_token)
63+
context["accounts.api_token.id"] = api_token.id
64+
logger.info("API token created: %s", api_token.id)
65+
else:
66+
logger.info("API token found: %s", api_token.id)
67+
return api_token
68+
69+
70+
@inject
71+
async def seed_api_token() -> None:
72+
"""Seed API token."""
73+
logger.debug("Seeding API token ...")
74+
await init_api_token()
75+
logger.debug("Seeding API token completed.")

seed/accounts/buyer.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import logging
2+
import os
3+
import pathlib
4+
5+
from dependency_injector.wiring import inject
6+
7+
from mpt_api_client import AsyncMPTClient
8+
from mpt_api_client.resources.accounts.buyers import Buyer
9+
from seed.context import Context
10+
from seed.defaults import DEFAULT_CONTEXT, DEFAULT_MPT_OPERATIONS
11+
12+
logger = logging.getLogger(__name__)
13+
14+
icon = pathlib.Path(__file__).parent / "logo.png"
15+
16+
17+
@inject
18+
async def get_buyer(
19+
context: Context = DEFAULT_CONTEXT,
20+
mpt_operations: AsyncMPTClient = DEFAULT_MPT_OPERATIONS,
21+
) -> Buyer | None:
22+
"""Get buyer from context or fetch from API."""
23+
buyer_id = context.get_string("accounts.buyer.id")
24+
if not buyer_id:
25+
return None
26+
try:
27+
buyer = context.get_resource("accounts.buyer", buyer_id)
28+
except ValueError:
29+
buyer = None
30+
if not isinstance(buyer, Buyer):
31+
buyer = await mpt_operations.accounts.buyers.get(buyer_id)
32+
context.set_resource("accounts.buyer", buyer)
33+
context["accounts.buyer.id"] = buyer.id
34+
return buyer
35+
return buyer
36+
37+
38+
@inject
39+
def build_buyer_data(context: Context = DEFAULT_CONTEXT) -> dict[str, object]:
40+
"""Build buyer data dictionary for creation."""
41+
buyer_account_id = os.getenv("CLIENT_ACCOUNT_ID")
42+
if not buyer_account_id:
43+
raise ValueError("CLIENT_ACCOUNT_ID environment variable is required")
44+
seller_id = context.get_string("accounts.seller.id")
45+
return {
46+
"name": "E2E Seeded Buyer",
47+
"account": {"id": buyer_account_id},
48+
"sellers": [{"id": seller_id}],
49+
"contact": {
50+
"firstName": "first",
51+
"lastName": "last",
52+
"email": "created.buyer@example.com",
53+
},
54+
"address": {
55+
"addressLine1": "123 Main St",
56+
"city": "Los Angeles",
57+
"state": "CA",
58+
"postCode": "12345",
59+
"country": "US",
60+
},
61+
}
62+
63+
64+
@inject
65+
async def init_buyer(
66+
context: Context = DEFAULT_CONTEXT,
67+
mpt_operations: AsyncMPTClient = DEFAULT_MPT_OPERATIONS,
68+
) -> Buyer:
69+
"""Get or create buyer."""
70+
buyer = await get_buyer(context=context, mpt_operations=mpt_operations)
71+
if buyer is None:
72+
buyer_data = build_buyer_data(context=context)
73+
logger.debug("Creating buyer ...")
74+
with pathlib.Path.open(icon, "rb") as icon_file:
75+
created = await mpt_operations.accounts.buyers.create(buyer_data, file=icon_file)
76+
if isinstance(created, Buyer):
77+
buyer = await mpt_operations.accounts.buyers.get(created.id)
78+
context.set_resource("accounts.buyer", buyer)
79+
context["accounts.buyer.id"] = buyer.id
80+
logger.info("Buyer created: %s", buyer.id)
81+
return buyer
82+
logger.warning("Buyer creation failed")
83+
raise ValueError("Buyer creation failed")
84+
logger.info("Buyer found: %s", buyer.id)
85+
return buyer
86+
87+
88+
@inject
89+
async def seed_buyer() -> None:
90+
"""Seed buyer."""
91+
logger.debug("Seeding buyer ...")
92+
await init_buyer()
93+
logger.debug("Seeding buyer completed.")

seed/accounts/licensee.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import logging
2+
import os
3+
import pathlib
4+
5+
from dependency_injector.wiring import inject
6+
7+
from mpt_api_client import AsyncMPTClient
8+
from mpt_api_client.resources.accounts.licensees import Licensee
9+
from seed.context import Context
10+
from seed.defaults import DEFAULT_CONTEXT, DEFAULT_MPT_CLIENT
11+
12+
logger = logging.getLogger(__name__)
13+
14+
icon = pathlib.Path(__file__).parent / "logo.png"
15+
16+
17+
@inject
18+
async def get_licensee(
19+
context: Context = DEFAULT_CONTEXT,
20+
mpt_client: AsyncMPTClient = DEFAULT_MPT_CLIENT,
21+
) -> Licensee | None:
22+
"""Get licensee from context or fetch from API."""
23+
licensee_id = context.get_string("accounts.licensee.id")
24+
if not licensee_id:
25+
return None
26+
try:
27+
licensee = context.get_resource("accounts.licensee", licensee_id)
28+
except ValueError:
29+
licensee = None
30+
if not isinstance(licensee, Licensee):
31+
licensee = await mpt_client.accounts.licensees.get(licensee_id)
32+
context.set_resource("accounts.licensee", licensee)
33+
context["accounts.licensee.id"] = licensee.id
34+
return licensee
35+
return licensee
36+
37+
38+
@inject
39+
def build_licensee_data(
40+
context: Context = DEFAULT_CONTEXT,
41+
) -> dict[str, object]:
42+
"""Get licensee data dictionary for creation."""
43+
account_id = os.getenv("CLIENT_ACCOUNT_ID")
44+
seller_id = context.get_string("accounts.seller.id")
45+
buyer_id = context.get_string("accounts.buyer.id")
46+
group = context.get_resource("accounts.user_group")
47+
licensee_type = "Client"
48+
return {
49+
"name": "E2E Seeded Licensee",
50+
"address": {
51+
"addressLine1": "123 Main St",
52+
"city": "Los Angeles",
53+
"state": "CA",
54+
"postCode": "67890",
55+
"country": "US",
56+
},
57+
"useBuyerAddress": False,
58+
"seller": {"id": seller_id},
59+
"buyer": {"id": buyer_id},
60+
"account": {"id": account_id},
61+
"eligibility": {"client": True, "partner": False},
62+
"groups": [{"id": group.id}],
63+
"type": licensee_type,
64+
"status": "Enabled",
65+
"defaultLanguage": "en-US",
66+
}
67+
68+
69+
@inject
70+
async def init_licensee(
71+
context: Context = DEFAULT_CONTEXT,
72+
mpt_client: AsyncMPTClient = DEFAULT_MPT_CLIENT,
73+
) -> Licensee:
74+
"""Get or create licensee."""
75+
licensee = await get_licensee(context=context, mpt_client=mpt_client)
76+
if licensee is None:
77+
licensee_data = build_licensee_data(context=context)
78+
logger.debug("Creating licensee ...")
79+
with pathlib.Path.open(icon, "rb") as icon_file:
80+
created = await mpt_client.accounts.licensees.create(licensee_data, file=icon_file)
81+
if isinstance(created, Licensee):
82+
context.set_resource("accounts.licensee", created)
83+
context["accounts.licensee.id"] = created.id
84+
logger.info("Licensee created: %s", created.id)
85+
return created
86+
logger.warning("Licensee creation failed")
87+
raise ValueError("Licensee creation failed")
88+
logger.info("Licensee found: %s", licensee.id)
89+
return licensee
90+
91+
92+
@inject
93+
async def seed_licensee() -> None:
94+
"""Seed licensee."""
95+
logger.debug("Seeding licensee ...")
96+
await init_licensee()
97+
logger.info("Seeding licensee completed.")

seed/accounts/logo.png

929 KB
Loading

seed/accounts/module.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import logging
2+
3+
from dependency_injector.wiring import inject
4+
5+
from mpt_api_client import AsyncMPTClient
6+
from mpt_api_client.resources.accounts.modules import Module
7+
from mpt_api_client.rql.query_builder import RQLQuery
8+
from seed.context import Context
9+
from seed.defaults import DEFAULT_CONTEXT, DEFAULT_MPT_OPERATIONS
10+
11+
logger = logging.getLogger(__name__)
12+
13+
14+
@inject
15+
async def get_module(
16+
context: Context = DEFAULT_CONTEXT,
17+
mpt_operations: AsyncMPTClient = DEFAULT_MPT_OPERATIONS,
18+
) -> Module | None:
19+
"""Get module from context or fetch from API."""
20+
module_id = context.get_string("accounts.module.id")
21+
if not module_id:
22+
return None
23+
try:
24+
module = context.get_resource("accounts.module", module_id)
25+
except ValueError:
26+
module = None
27+
if not isinstance(module, Module):
28+
module = await mpt_operations.accounts.modules.get(module_id)
29+
context.set_resource("accounts.module", module)
30+
context["accounts.module.id"] = module.id
31+
return module
32+
return module
33+
34+
35+
@inject
36+
async def refresh_module(
37+
context: Context = DEFAULT_CONTEXT,
38+
mpt_operations: AsyncMPTClient = DEFAULT_MPT_OPERATIONS,
39+
) -> Module | None:
40+
"""Refresh module in context (always fetch)."""
41+
module = await get_module(context=context, mpt_operations=mpt_operations)
42+
if module is None:
43+
filtered_modules = mpt_operations.accounts.modules.filter(
44+
RQLQuery(name="Access Management")
45+
)
46+
modules = [mod async for mod in filtered_modules.iterate()]
47+
if modules:
48+
first_module = modules[0]
49+
if isinstance(first_module, Module):
50+
context["accounts.module.id"] = first_module.id
51+
context.set_resource("accounts.module", first_module)
52+
return first_module
53+
logger.warning("First module is not a Module instance.")
54+
return None
55+
logger.warning("Module 'Access Management' not found.")
56+
return None
57+
return module
58+
59+
60+
@inject
61+
async def seed_module() -> Module:
62+
"""Seed module."""
63+
logger.debug("Seeding module ...")
64+
existing_module = await get_module()
65+
if existing_module is None:
66+
refreshed = await refresh_module()
67+
logger.debug("Seeding module completed.")
68+
if refreshed is None:
69+
raise ValueError("Could not seed module: no valid Module found.")
70+
return refreshed
71+
logger.debug("Seeding module completed.")
72+
return existing_module

0 commit comments

Comments
 (0)