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
3 changes: 2 additions & 1 deletion e2e_config.test.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,6 @@
"notifications.message.id": "MSG-0000-6215-1019-0139",
"notifications.subscriber.id": "NTS-0829-7123-7123",
"integration.extension.id": "EXT-6587-4477",
"integration.term.id": "ETC-6587-4477-0062"
"integration.term.id": "ETC-6587-4477-0062",
"program.program.id": "PRG-9643-3741"
}
12 changes: 12 additions & 0 deletions mpt_api_client/mpt_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
AsyncHelpdesk,
AsyncIntegration,
AsyncNotifications,
AsyncProgram,
Audit,
Billing,
Catalog,
Expand All @@ -20,6 +21,7 @@
Helpdesk,
Integration,
Notifications,
Program,
)


Expand Down Expand Up @@ -97,6 +99,11 @@ def integration(self) -> AsyncIntegration:
"""Integration MPT API Client."""
return AsyncIntegration(http_client=self.http_client)

@property
def program(self) -> AsyncProgram:
"""Program MPT API Client."""
return AsyncProgram(http_client=self.http_client)


class MPTClient:
"""MPT API Client."""
Expand Down Expand Up @@ -176,3 +183,8 @@ def exchange(self) -> Exchange:
def integration(self) -> Integration:
"""Integration MPT API Client."""
return Integration(http_client=self.http_client)

@property
def program(self) -> Program:
"""Program MPT API Client."""
return Program(http_client=self.http_client)
3 changes: 3 additions & 0 deletions mpt_api_client/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from mpt_api_client.resources.helpdesk import AsyncHelpdesk, Helpdesk
from mpt_api_client.resources.integration import AsyncIntegration, Integration
from mpt_api_client.resources.notifications import AsyncNotifications, Notifications
from mpt_api_client.resources.program import AsyncProgram, Program

__all__ = [ # noqa: WPS410
"Accounts",
Expand All @@ -19,6 +20,7 @@
"AsyncHelpdesk",
"AsyncIntegration",
"AsyncNotifications",
"AsyncProgram",
"Audit",
"Billing",
"Catalog",
Expand All @@ -27,4 +29,5 @@
"Helpdesk",
"Integration",
"Notifications",
"Program",
]
6 changes: 6 additions & 0 deletions mpt_api_client/resources/program/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from mpt_api_client.resources.program.program import AsyncProgram, Program

__all__ = [ # noqa: WPS410
"AsyncProgram",
"Program",
]
9 changes: 9 additions & 0 deletions mpt_api_client/resources/program/mixins/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from mpt_api_client.resources.program.mixins.publishable_mixin import (
AsyncPublishableMixin,
PublishableMixin,
)

__all__ = [ # noqa: WPS410
"AsyncPublishableMixin",
"PublishableMixin",
]
45 changes: 45 additions & 0 deletions mpt_api_client/resources/program/mixins/publishable_mixin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from mpt_api_client.models import ResourceData


class PublishableMixin[Model]:
"""Publishable mixin adds the ability to publish and unpublish."""

def publish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
"""Update state to Published.

Args:
resource_id: Resource ID
resource_data: Resource data will be updated
"""
return self._resource(resource_id).post("publish", json=resource_data) # type: ignore[attr-defined, no-any-return]

def unpublish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
"""Update state to Unpublished.

Args:
resource_id: Resource ID
resource_data: Resource data will be updated
"""
return self._resource(resource_id).post("unpublish", json=resource_data) # type: ignore[attr-defined, no-any-return]


class AsyncPublishableMixin[Model]:
"""Publishable mixin adds the ability to publish and unpublish."""

async def publish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
"""Update state to Published.

Args:
resource_id: Resource ID
resource_data: Resource data will be updated
"""
return await self._resource(resource_id).post("publish", json=resource_data) # type: ignore[attr-defined, no-any-return]

async def unpublish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
"""Update state to Unpublished.

Args:
resource_id: Resource ID
resource_data: Resource data will be updated
"""
return await self._resource(resource_id).post("unpublish", json=resource_data) # type: ignore[attr-defined, no-any-return]
26 changes: 26 additions & 0 deletions mpt_api_client/resources/program/program.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from mpt_api_client.http import AsyncHTTPClient, HTTPClient
from mpt_api_client.resources.program.programs import AsyncProgramsService, ProgramsService


class Program:
"""Program MPT API Module."""

def __init__(self, *, http_client: HTTPClient):
self.http_client = http_client

@property
def programs(self) -> ProgramsService:
"""Programs service."""
return ProgramsService(http_client=self.http_client)


class AsyncProgram:
"""Program MPT API Module."""

def __init__(self, *, http_client: AsyncHTTPClient):
self.http_client = http_client

@property
def programs(self) -> AsyncProgramsService:
"""Programs service."""
return AsyncProgramsService(http_client=self.http_client)
94 changes: 94 additions & 0 deletions mpt_api_client/resources/program/programs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from mpt_api_client.http import AsyncService, Service
from mpt_api_client.http.mixins.collection_mixin import AsyncCollectionMixin, CollectionMixin
from mpt_api_client.http.mixins.create_file_mixin import AsyncCreateFileMixin, CreateFileMixin
from mpt_api_client.http.mixins.delete_mixin import AsyncDeleteMixin, DeleteMixin
from mpt_api_client.http.mixins.get_mixin import AsyncGetMixin, GetMixin
from mpt_api_client.http.mixins.update_file_mixin import AsyncUpdateFileMixin, UpdateFileMixin
from mpt_api_client.models import Model
from mpt_api_client.models.model import BaseModel, ResourceData
from mpt_api_client.resources.program.mixins import (
AsyncPublishableMixin,
PublishableMixin,
)


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

Attributes:
name: Program name.
website: Program website.
eligibility: Eligibility criteria for the program.
applicable_to: Applicable products or services for the program.
icon: Program icon URL.
status: Program status.
vendor: Reference to the vendor account associated with the program.
settings: Program settings.
statistics: Program statistics and performance metrics.
audit: Audit information related to the program (created, updated events).
"""

name: str | None
website: str | None
eligibility: BaseModel | None
applicable_to: str | None
icon: str | None
status: str | None
vendor: BaseModel | None
settings: BaseModel | None
statistics: BaseModel | None
audit: BaseModel | None


class ProgramsServiceConfig:
"""Programs service configuration."""

_endpoint = "/public/v1/program/programs"
_model_class = Program
_collection_key = "data"
_upload_file_key = "icon"
_upload_data_key = "program"


class ProgramsService(
GetMixin[Program],
CreateFileMixin[Program],
UpdateFileMixin[Program],
DeleteMixin,
PublishableMixin[Program],
CollectionMixin[Program],
Service[Program],
ProgramsServiceConfig,
):
"""Programs service."""

def update_settings(self, program_id: str, settings: ResourceData) -> Program:
"""Update program settings.

Args:
program_id: Program ID
settings: Settings data to be updated
"""
return self._resource(program_id).put("settings", json=settings)


class AsyncProgramsService(
AsyncGetMixin[Program],
AsyncCreateFileMixin[Program],
AsyncUpdateFileMixin[Program],
AsyncDeleteMixin,
AsyncPublishableMixin[Program],
AsyncCollectionMixin[Program],
AsyncService[Program],
ProgramsServiceConfig,
):
"""Async programs service."""

async def update_settings(self, program_id: str, settings: ResourceData) -> Program:
"""Update program settings.

Args:
program_id: Program ID
settings: Settings data to be updated
"""
return await self._resource(program_id).put("settings", json=settings)
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ per-file-ignores = [
"tests/unit/resources/integration/*.py: WPS202 WPS210 WPS218 WPS453",
"tests/unit/resources/integration/mixins/*.py: WPS453 WPS202",
"tests/unit/resources/commerce/*.py: WPS202 WPS204",
"tests/unit/resources/program/*.py: WPS202 WPS210 WPS218",
Comment thread
robcsegal marked this conversation as resolved.
"tests/unit/test_mpt_client.py: WPS235",
"tests/*: WPS432 WPS202",
"tests/unit/resources/exchange/*.py: WPS202 WPS204 WPS210",
Expand Down
Empty file added tests/e2e/program/__init__.py
Empty file.
6 changes: 6 additions & 0 deletions tests/e2e/program/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import pytest


@pytest.fixture
def program_id(e2e_config):
return e2e_config.get("program.program.id")
16 changes: 16 additions & 0 deletions tests/e2e/program/program/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import pytest


@pytest.fixture
def program_data():
return {
"name": "E2E Created Program",
"website": "www.example.com",
"eligibility": {"client": True, "partner": True},
"applicableTo": "Licensee",
}


@pytest.fixture
def invalid_program_id():
return "PRG-0000-0000"
74 changes: 74 additions & 0 deletions tests/e2e/program/program/test_async_program.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import pytest

from mpt_api_client import RQLQuery
from mpt_api_client.exceptions import MPTAPIError

pytestmark = [pytest.mark.flaky]


@pytest.fixture
async def created_program(async_mpt_vendor, program_data, logo_fd):
program = await async_mpt_vendor.program.programs.create(program_data, file=logo_fd)

yield program

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


def test_create_program(created_program, program_data):
result = created_program.name == program_data["name"]

assert result is True


async def test_update_program(async_mpt_vendor, created_program):
update_data = {"name": "E2E Updated Program"}

result = await async_mpt_vendor.program.programs.update(created_program.id, update_data)

assert result.name == update_data["name"]


async def test_get_program(async_mpt_vendor, program_id):
result = await async_mpt_vendor.program.programs.get(program_id)

assert result.id == program_id


async def test_filter_and_select_programs(async_mpt_vendor, program_id):
select_fields = ["-icon", "-revision", "-audit"]
filtered_programs = (
async_mpt_vendor.program.programs
.filter(RQLQuery(id=program_id))
.filter(RQLQuery(name="E2E Seeded Program"))
.select(*select_fields)
)

result = [program async for program in filtered_programs.iterate()]

assert len(result) == 1


async def test_delete_program(async_mpt_vendor, created_program):
program_data = created_program

result = async_mpt_vendor.program.programs

await result.delete(program_data.id)


async def test_publish_program(async_mpt_vendor, created_program):
result = await async_mpt_vendor.program.programs.publish(created_program.id)

assert result is not None


async def test_unpublish_program(async_mpt_vendor, created_program):
await async_mpt_vendor.program.programs.publish(created_program.id)

result = await async_mpt_vendor.program.programs.unpublish(created_program.id)

assert result is not None
Loading