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
13 changes: 13 additions & 0 deletions _refactored/restapi/operations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
from restapi.operations.catalog_operations import CatalogOperations
from restapi.operations.category_operations import CategoryOperations
from restapi.operations.contact_operations import ContactOperations
from restapi.operations.dynamic_content_operations import (
ContentFolderOperations,
ContentItemOperations,
ContentPlaceOperations,
ContentPublicationOperations,
)
from restapi.operations.employee_operations import EmployeeOperations
from restapi.operations.member_operations import MemberOperations
from restapi.operations.notifications_operations import NotificationsOperations
Expand All @@ -12,6 +18,7 @@
from restapi.operations.pricelist_assignment_operations import PricelistAssignmentOperations
from restapi.operations.pricelist_operations import PricelistOperations
from restapi.operations.product_operations import ProductOperations
from restapi.operations.promotion_operations import CouponOperations, PromotionOperations
from restapi.operations.role_operations import RoleOperations
from restapi.operations.settings_operations import SettingsOperations
from restapi.operations.user_operations import UserOperations
Expand All @@ -21,7 +28,12 @@
"ApiKeyOperations",
"CatalogOperations",
"CategoryOperations",
"ContentFolderOperations",
"ContentItemOperations",
"ContentPlaceOperations",
"ContentPublicationOperations",
"ContactOperations",
"CouponOperations",
"EmployeeOperations",
"MemberOperations",
"NotificationsOperations",
Expand All @@ -31,6 +43,7 @@
"PricelistAssignmentOperations",
"PricelistOperations",
"ProductOperations",
"PromotionOperations",
"RestBaseOperations",
"RoleOperations",
"SettingsOperations",
Expand Down
102 changes: 102 additions & 0 deletions _refactored/restapi/operations/dynamic_content_operations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"""REST API operations for VirtoCommerce Marketing dynamic content.

Covers content items, folders, placeholders (places), and publications.
All endpoints verified from Katalon Object Repository.
"""

from typing import Any

from restapi.operations.base import RestBaseOperations


class ContentItemOperations(RestBaseOperations):
PATH = "/api/marketing/contentitems"

def create(self, *, name: str, **overrides: Any) -> dict:
payload: dict[str, Any] = {"name": name, **overrides}
return self._client.post(self._url(self.PATH), json=payload)

def update(self, item: dict, **overrides: Any) -> dict:
return self._client.put(self._url(self.PATH), json={**item, **overrides})

def get_by_id(self, item_id: str) -> dict:
return self._client.get(self._url(f"{self.PATH}/{item_id}"))

def search(self, *, skip: int = 0, take: int = 20, **extra: Any) -> dict:
return self._client.post(self._url(f"{self.PATH}/search"), json={"skip": skip, "take": take, **extra})

def list_entries_search(self, *, skip: int = 0, take: int = 20, **extra: Any) -> dict:
return self._client.post(
self._url(f"{self.PATH}/listentries/search"), json={"skip": skip, "take": take, **extra}
)

def evaluate(self, criteria: dict) -> list[dict]:
return self._client.post(self._url(f"{self.PATH}/evaluate"), json=criteria)

def delete(self, *item_ids: str) -> None:
self._client.delete(self._url(self.PATH), params={"ids": list(item_ids)})


class ContentFolderOperations(RestBaseOperations):
PATH = "/api/marketing/contentfolders"

def create(self, *, name: str, **overrides: Any) -> dict:
payload: dict[str, Any] = {"name": name, **overrides}
return self._client.post(self._url(self.PATH), json=payload)

def update(self, folder: dict, **overrides: Any) -> dict:
return self._client.put(self._url(self.PATH), json={**folder, **overrides})

def get_by_id(self, folder_id: str) -> dict:
return self._client.get(self._url(f"{self.PATH}/{folder_id}"))

def delete(self, *folder_ids: str) -> None:
self._client.delete(self._url(self.PATH), params={"ids": list(folder_ids)})


class ContentPlaceOperations(RestBaseOperations):
PATH = "/api/marketing/contentplaces"

def create(self, *, name: str, **overrides: Any) -> dict:
payload: dict[str, Any] = {"name": name, **overrides}
return self._client.post(self._url(self.PATH), json=payload)

def update(self, place: dict, **overrides: Any) -> dict:
return self._client.put(self._url(self.PATH), json={**place, **overrides})

def get_by_id(self, place_id: str) -> dict:
return self._client.get(self._url(f"{self.PATH}/{place_id}"))

def search(self, *, skip: int = 0, take: int = 20, **extra: Any) -> dict:
return self._client.post(self._url(f"{self.PATH}/search"), json={"skip": skip, "take": take, **extra})

def list_entries_search(self, *, skip: int = 0, take: int = 20, **extra: Any) -> dict:
return self._client.post(
self._url(f"{self.PATH}/listentries/search"), json={"skip": skip, "take": take, **extra}
)

def delete(self, *place_ids: str) -> None:
self._client.delete(self._url(self.PATH), params={"ids": list(place_ids)})


class ContentPublicationOperations(RestBaseOperations):
PATH = "/api/marketing/contentpublications"

def create(self, *, name: str, **overrides: Any) -> dict:
payload: dict[str, Any] = {"name": name, **overrides}
return self._client.post(self._url(self.PATH), json=payload)

def update(self, publication: dict, **overrides: Any) -> dict:
return self._client.put(self._url(self.PATH), json={**publication, **overrides})

def get_by_id(self, pub_id: str) -> dict:
return self._client.get(self._url(f"{self.PATH}/{pub_id}"))

def get_new(self) -> dict:
return self._client.get(self._url(f"{self.PATH}/new"))

def search(self, *, skip: int = 0, take: int = 20, **extra: Any) -> dict:
return self._client.post(self._url(f"{self.PATH}/search"), json={"skip": skip, "take": take, **extra})

def delete(self, *pub_ids: str) -> None:
self._client.delete(self._url(self.PATH), params={"ids": list(pub_ids)})
51 changes: 51 additions & 0 deletions _refactored/restapi/operations/promotion_operations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""REST API operations for VirtoCommerce promotions and coupons.

Endpoints verified from Katalon Object Repository.
"""

from typing import Any

from restapi.operations.base import RestBaseOperations


class PromotionOperations(RestBaseOperations):
PATH = "/api/marketing/promotions"

def create(self, *, name: str, **overrides: Any) -> dict:
payload: dict[str, Any] = {"name": name, "isActive": True, **overrides}
return self._client.post(self._url(self.PATH), json=payload)

def update(self, promo: dict, **overrides: Any) -> dict:
return self._client.put(self._url(self.PATH), json={**promo, **overrides})

def get_by_id(self, promo_id: str) -> dict:
return self._client.get(self._url(f"{self.PATH}/{promo_id}"))

def get_new(self) -> dict:
return self._client.get(self._url(f"{self.PATH}/new"))

def search(self, *, skip: int = 0, take: int = 20, **extra: Any) -> dict:
return self._client.post(self._url(f"{self.PATH}/search"), json={"skip": skip, "take": take, **extra})

def delete(self, *promo_ids: str) -> None:
self._client.delete(self._url(self.PATH), params={"ids": list(promo_ids)})


class CouponOperations(RestBaseOperations):
PATH = "/api/marketing/promotions/coupons"

def add(self, coupons: list[dict]) -> None:
"""POST /api/marketing/promotions/coupons/add."""
self._client.post(self._url(f"{self.PATH}/add"), json=coupons)

def get_by_id(self, coupon_id: str) -> dict:
return self._client.get(self._url(f"{self.PATH}/{coupon_id}"))

def search(self, *, promotion_id: str | None = None, skip: int = 0, take: int = 20, **extra: Any) -> dict:
payload: dict[str, Any] = {"skip": skip, "take": take, **extra}
if promotion_id:
payload["promotionId"] = promotion_id
return self._client.post(self._url(f"{self.PATH}/search"), json=payload)

def delete(self, *coupon_ids: str) -> None:
self._client.delete(self._url(f"{self.PATH}/delete"), params={"ids": list(coupon_ids)})
Empty file.
138 changes: 138 additions & 0 deletions _refactored/tests/restapi/marketing/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
"""Marketing module fixtures."""

import uuid
from typing import Any, Callable, Generator

import pytest

from core.clients.rest import RestClient
from restapi.operations import (
ContentFolderOperations,
ContentItemOperations,
ContentPlaceOperations,
ContentPublicationOperations,
CouponOperations,
PromotionOperations,
)


@pytest.fixture
def content_item_ops(rest_client: RestClient, backend_base_url: str) -> ContentItemOperations:
return ContentItemOperations(rest_client, backend_base_url)


@pytest.fixture
def content_folder_ops(rest_client: RestClient, backend_base_url: str) -> ContentFolderOperations:
return ContentFolderOperations(rest_client, backend_base_url)


@pytest.fixture
def content_place_ops(rest_client: RestClient, backend_base_url: str) -> ContentPlaceOperations:
return ContentPlaceOperations(rest_client, backend_base_url)


@pytest.fixture
def content_pub_ops(rest_client: RestClient, backend_base_url: str) -> ContentPublicationOperations:
return ContentPublicationOperations(rest_client, backend_base_url)


@pytest.fixture
def promo_ops(rest_client: RestClient, backend_base_url: str) -> PromotionOperations:
return PromotionOperations(rest_client, backend_base_url)


@pytest.fixture
def coupon_ops(rest_client: RestClient, backend_base_url: str) -> CouponOperations:
return CouponOperations(rest_client, backend_base_url)


@pytest.fixture
def make_content_item(content_item_ops: ContentItemOperations) -> Generator[Callable[..., dict], None, None]:
created_ids: list[str] = []

def _make(**overrides: Any) -> dict:
name = overrides.pop("name", f"QAItem_{uuid.uuid4().hex[:8]}")
item = content_item_ops.create(name=name, **overrides)
created_ids.append(item["id"])
return item

yield _make
for iid in reversed(created_ids):
try:
content_item_ops.delete(iid)
except Exception:
pass


@pytest.fixture
def make_content_folder(content_folder_ops: ContentFolderOperations) -> Generator[Callable[..., dict], None, None]:
created_ids: list[str] = []

def _make(**overrides: Any) -> dict:
name = overrides.pop("name", f"QAFolder_{uuid.uuid4().hex[:8]}")
folder = content_folder_ops.create(name=name, **overrides)
created_ids.append(folder["id"])
return folder

yield _make
for fid in reversed(created_ids):
try:
content_folder_ops.delete(fid)
except Exception:
pass


@pytest.fixture
def make_content_place(content_place_ops: ContentPlaceOperations) -> Generator[Callable[..., dict], None, None]:
created_ids: list[str] = []

def _make(**overrides: Any) -> dict:
name = overrides.pop("name", f"QAPlace_{uuid.uuid4().hex[:8]}")
place = content_place_ops.create(name=name, **overrides)
created_ids.append(place["id"])
return place

yield _make
for pid in reversed(created_ids):
try:
content_place_ops.delete(pid)
except Exception:
pass


@pytest.fixture
def make_content_publication(
content_pub_ops: ContentPublicationOperations,
) -> Generator[Callable[..., dict], None, None]:
created_ids: list[str] = []

def _make(**overrides: Any) -> dict:
name = overrides.pop("name", f"QAPub_{uuid.uuid4().hex[:8]}")
pub = content_pub_ops.create(name=name, **overrides)
created_ids.append(pub["id"])
return pub

yield _make
for pid in reversed(created_ids):
try:
content_pub_ops.delete(pid)
except Exception:
pass


@pytest.fixture
def make_promotion(promo_ops: PromotionOperations) -> Generator[Callable[..., dict], None, None]:
created_ids: list[str] = []

def _make(**overrides: Any) -> dict:
name = overrides.pop("name", f"QAPromo_{uuid.uuid4().hex[:8]}")
promo = promo_ops.create(name=name, **overrides)
created_ids.append(promo["id"])
return promo

yield _make
for pid in reversed(created_ids):
try:
promo_ops.delete(pid)
except Exception:
pass
Loading
Loading