diff --git a/e2e_config.test.json b/e2e_config.test.json index d42cea2b..32cf582e 100644 --- a/e2e_config.test.json +++ b/e2e_config.test.json @@ -71,6 +71,7 @@ "integration.extension.id": "EXT-6587-4477", "integration.term.id": "ETC-6587-4477-0062", "program.media.id": "PMD-9643-3741-0001", + "program.parameter.group.id": "PPG-9643-3741-0002", "program.program.id": "PRG-9643-3741", "program.document.file.id": "PDM-9643-3741-0001" } diff --git a/mpt_api_client/resources/program/programs.py b/mpt_api_client/resources/program/programs.py index 8b8eb754..3066a853 100644 --- a/mpt_api_client/resources/program/programs.py +++ b/mpt_api_client/resources/program/programs.py @@ -18,6 +18,10 @@ AsyncMediaService, MediaService, ) +from mpt_api_client.resources.program.programs_parameter_groups import ( + AsyncParameterGroupsService, + ParameterGroupsService, +) class Program(Model): @@ -91,6 +95,12 @@ def media(self, program_id: str) -> MediaService: http_client=self.http_client, endpoint_params={"program_id": program_id} ) + def parameter_groups(self, program_id: str) -> ParameterGroupsService: + """Return program parameter groups service.""" + return ParameterGroupsService( + http_client=self.http_client, endpoint_params={"program_id": program_id} + ) + class AsyncProgramsService( AsyncGetMixin[Program], @@ -124,3 +134,9 @@ def media(self, program_id: str) -> AsyncMediaService: return AsyncMediaService( http_client=self.http_client, endpoint_params={"program_id": program_id} ) + + def parameter_groups(self, program_id: str) -> AsyncParameterGroupsService: + """Return async program parameter groups service.""" + return AsyncParameterGroupsService( + http_client=self.http_client, endpoint_params={"program_id": program_id} + ) diff --git a/mpt_api_client/resources/program/programs_parameter_groups.py b/mpt_api_client/resources/program/programs_parameter_groups.py new file mode 100644 index 00000000..123e8f29 --- /dev/null +++ b/mpt_api_client/resources/program/programs_parameter_groups.py @@ -0,0 +1,59 @@ +from mpt_api_client.http import AsyncService, Service +from mpt_api_client.http.mixins import ( + AsyncCollectionMixin, + AsyncManagedResourceMixin, + CollectionMixin, + ManagedResourceMixin, +) +from mpt_api_client.models import Model +from mpt_api_client.models.model import BaseModel + + +class ParameterGroup(Model): + """Parameter Group resource. + + Attributes: + name: Parameter group name. + label: Display label for the parameter group. + description: Parameter group description. + display_order: Display order of the group. + default: Whether this is the default parameter group. + parameter_count: Number of parameters in this group. + program: Reference to the program this group belongs to. + audit: Audit information (created, updated events). + """ + + name: str | None + label: str | None + description: str | None + display_order: int | None + default: bool | None + parameter_count: int | None + program: BaseModel | None + audit: BaseModel | None + + +class ParameterGroupsServiceConfig: + """Parameter Groups service configuration.""" + + _endpoint = "/public/v1/program/programs/{program_id}/parameter-groups" + _model_class = ParameterGroup + _collection_key = "data" + + +class ParameterGroupsService( + ManagedResourceMixin[ParameterGroup], + CollectionMixin[ParameterGroup], + Service[ParameterGroup], + ParameterGroupsServiceConfig, +): + """Parameter Groups service.""" + + +class AsyncParameterGroupsService( + AsyncManagedResourceMixin[ParameterGroup], + AsyncCollectionMixin[ParameterGroup], + AsyncService[ParameterGroup], + ParameterGroupsServiceConfig, +): + """Parameter Groups service.""" diff --git a/tests/e2e/program/program/parameter_group/conftest.py b/tests/e2e/program/program/parameter_group/conftest.py new file mode 100644 index 00000000..1c7f131f --- /dev/null +++ b/tests/e2e/program/program/parameter_group/conftest.py @@ -0,0 +1,22 @@ +import pytest + + +@pytest.fixture +def parameter_group_id(e2e_config): + return e2e_config["program.parameter.group.id"] + + +@pytest.fixture +def invalid_parameter_group_id(): + return "PPG-0000-0000-0000" + + +@pytest.fixture +def parameter_group_data(): + return { + "name": "E2E Created Program Parameter Group", + "description": "E2E Created Program Parameter Group", + "label": "E2E Created Program Parameter Group", + "default": False, + "displayOrder": 100, + } diff --git a/tests/e2e/program/program/parameter_group/test_async_parameter_group.py b/tests/e2e/program/program/parameter_group/test_async_parameter_group.py new file mode 100644 index 00000000..ee063056 --- /dev/null +++ b/tests/e2e/program/program/parameter_group/test_async_parameter_group.py @@ -0,0 +1,63 @@ +import pytest + +from mpt_api_client.exceptions import MPTAPIError +from mpt_api_client.rql.query_builder import RQLQuery + +pytestmark = [pytest.mark.flaky] + + +@pytest.fixture +async def created_parameter_group(async_mpt_vendor, program_id, parameter_group_data): + service = async_mpt_vendor.program.programs.parameter_groups(program_id) + group = await service.create(parameter_group_data) + yield group + try: + await service.delete(group.id) + except MPTAPIError as error: + print(f"TEARDOWN - Unable to delete parameter group {group.id}: {error.title}") # noqa: WPS421 + + +def test_create_parameter_group(created_parameter_group): + result = created_parameter_group.name == "E2E Created Program Parameter Group" + + assert result is True + + +async def test_update_parameter_group(async_mpt_vendor, program_id, created_parameter_group): + service = async_mpt_vendor.program.programs.parameter_groups(program_id) + update_data = {"name": "E2E Updated Program Parameter Group"} + + result = await service.update(created_parameter_group.id, update_data) + + assert result.name == update_data["name"] + + +async def test_get_parameter_group(async_mpt_vendor, program_id, parameter_group_id): + service = async_mpt_vendor.program.programs.parameter_groups(program_id) + + result = await service.get(parameter_group_id) + + assert result.id == parameter_group_id + + +async def test_delete_parameter_group(async_mpt_vendor, program_id, created_parameter_group): + parameter_group_data = created_parameter_group + + result = async_mpt_vendor.program.programs.parameter_groups(program_id) + + await result.delete(parameter_group_data.id) + + +async def test_filter_and_select_parameter_groups(async_mpt_vendor, program_id, parameter_group_id): + select_fields = ["-description", "-audit"] + filtered_groups = ( + async_mpt_vendor.program.programs + .parameter_groups(program_id) + .filter(RQLQuery(id=parameter_group_id)) + .filter(RQLQuery(name="E2E Seeded Program Parameter Group")) + .select(*select_fields) + ) + + result = [parameter_group async for parameter_group in filtered_groups.iterate()] + + assert len(result) == 1 diff --git a/tests/e2e/program/program/parameter_group/test_sync_parameter_group.py b/tests/e2e/program/program/parameter_group/test_sync_parameter_group.py new file mode 100644 index 00000000..4bf85c90 --- /dev/null +++ b/tests/e2e/program/program/parameter_group/test_sync_parameter_group.py @@ -0,0 +1,63 @@ +import pytest + +from mpt_api_client.exceptions import MPTAPIError +from mpt_api_client.rql.query_builder import RQLQuery + +pytestmark = [pytest.mark.flaky] + + +@pytest.fixture +def created_parameter_group(mpt_vendor, program_id, parameter_group_data): + service = mpt_vendor.program.programs.parameter_groups(program_id) + group = service.create(parameter_group_data) + yield group + try: + service.delete(group.id) + except MPTAPIError as error: + print(f"TEARDOWN - Unable to delete parameter group {group.id}: {error.title}") # noqa: WPS421 + + +def test_create_parameter_group(created_parameter_group): + result = created_parameter_group.name == "E2E Created Program Parameter Group" + + assert result is True + + +def test_update_parameter_group(mpt_vendor, program_id, created_parameter_group): + service = mpt_vendor.program.programs.parameter_groups(program_id) + update_data = {"name": "E2E Updated Program Parameter Group"} + + result = service.update(created_parameter_group.id, update_data) + + assert result.name == update_data["name"] + + +def test_get_parameter_group(mpt_vendor, program_id, parameter_group_id): + service = mpt_vendor.program.programs.parameter_groups(program_id) + + result = service.get(parameter_group_id) + + assert result.id == parameter_group_id + + +def test_delete_parameter_group(mpt_vendor, program_id, created_parameter_group): + parameter_group_data = created_parameter_group + + result = mpt_vendor.program.programs.parameter_groups(program_id) + + result.delete(parameter_group_data.id) + + +def test_filter_and_select_parameter_groups(mpt_vendor, program_id, parameter_group_id): + select_fields = ["-description", "-audit"] + filtered_groups = ( + mpt_vendor.program.programs + .parameter_groups(program_id) + .filter(RQLQuery(id=parameter_group_id)) + .filter(RQLQuery(name="E2E Seeded Program Parameter Group")) + .select(*select_fields) + ) + + result = list(filtered_groups.iterate()) + + assert len(result) == 1 diff --git a/tests/unit/resources/program/test_programs.py b/tests/unit/resources/program/test_programs.py index 51d8d13b..0c68d7d0 100644 --- a/tests/unit/resources/program/test_programs.py +++ b/tests/unit/resources/program/test_programs.py @@ -12,6 +12,10 @@ AsyncMediaService, MediaService, ) +from mpt_api_client.resources.program.programs_parameter_groups import ( + AsyncParameterGroupsService, + ParameterGroupsService, +) @pytest.fixture @@ -89,6 +93,7 @@ def test_async_mixins_present(async_programs_service, method): [ ("documents", DocumentService), ("media", MediaService), + ("parameter_groups", ParameterGroupsService), ], ) def test_property_services(programs_service, service_method, expected_service_class): @@ -103,6 +108,7 @@ def test_property_services(programs_service, service_method, expected_service_cl [ ("documents", AsyncDocumentService), ("media", AsyncMediaService), + ("parameter_groups", AsyncParameterGroupsService), ], ) def test_async_property_services(async_programs_service, service_method, expected_service_class): diff --git a/tests/unit/resources/program/test_programs_parameter_groups.py b/tests/unit/resources/program/test_programs_parameter_groups.py new file mode 100644 index 00000000..87a3c650 --- /dev/null +++ b/tests/unit/resources/program/test_programs_parameter_groups.py @@ -0,0 +1,93 @@ +import pytest + +from mpt_api_client.models.model import BaseModel +from mpt_api_client.resources.program.programs_parameter_groups import ( + AsyncParameterGroupsService, + ParameterGroup, + ParameterGroupsService, +) + + +@pytest.fixture +def parameter_groups_service(http_client): + return ParameterGroupsService( + http_client=http_client, endpoint_params={"program_id": "PPG-001"} + ) + + +@pytest.fixture +def async_parameter_groups_service(async_http_client): + return AsyncParameterGroupsService( + http_client=async_http_client, endpoint_params={"program_id": "PPG-001"} + ) + + +@pytest.fixture +def parameter_group_data(): + return { + "id": "GRP-001", + "name": "Program Parameter Group", + "label": "Program Parameter Group", + "description": "Program Parameter Group", + "displayOrder": 1, + "default": True, + "parameterCount": 5, + "program": {"id": "PPG-001", "name": "Program"}, + "audit": { + "created": {"at": "2024-01-01T00:00:00Z"}, + "updated": {"at": "2024-01-02T00:00:00Z"}, + }, + } + + +def test_endpoint(parameter_groups_service): + result = parameter_groups_service.path == "/public/v1/program/programs/PPG-001/parameter-groups" + + assert result is True + + +def test_async_endpoint(async_parameter_groups_service): + result = ( + async_parameter_groups_service.path + == "/public/v1/program/programs/PPG-001/parameter-groups" + ) + + assert result is True + + +@pytest.mark.parametrize("method", ["get", "create", "delete", "update", "iterate"]) +def test_methods_present(parameter_groups_service, method): + result = hasattr(parameter_groups_service, method) + + assert result is True + + +@pytest.mark.parametrize("method", ["get", "create", "delete", "update", "iterate"]) +def test_async_methods_present(async_parameter_groups_service, method): + result = hasattr(async_parameter_groups_service, method) + + assert result is True + + +def test_parameter_group_primitive_fields(parameter_group_data): + result = ParameterGroup(parameter_group_data) + + assert result.to_dict() == parameter_group_data + + +def test_parameter_group_nested_field_types(parameter_group_data): + result = ParameterGroup(parameter_group_data) + + assert isinstance(result.program, BaseModel) + assert isinstance(result.audit, BaseModel) + + +def test_parameter_group_optional_fields_absent(): + result = ParameterGroup({"id": "PPG-001"}) + + assert result.id == "PPG-001" + assert not hasattr(result, "name") + assert not hasattr(result, "label") + assert not hasattr(result, "description") + assert not hasattr(result, "program") + assert not hasattr(result, "audit")