Skip to content

Commit acd48db

Browse files
authored
MPT-20326: Added endpoints and e2e tests for program parameter groups (#307)
This pull request adds support for managing program parameter groups in the API client. It introduces new service classes for both synchronous and asynchronous operations on parameter groups, updates the main `Program` services to expose these new capabilities, and provides comprehensive unit and end-to-end tests to verify the new functionality. **New Program Parameter Groups Support:** * Added `ParameterGroup` model and `ParameterGroupsService` / `AsyncParameterGroupsService` classes to `programs_parameter_groups.py`, including endpoint configuration and resource mixins. * Updated `Program` and `AsyncProgramsService` classes in `programs.py` to provide `.parameter_groups(program_id)` methods for accessing the new services. [[1]](diffhunk://#diff-05ce5b7611ffd8756a39d2e4c9eacda5896e22ddb7801af98e35ec0cd0e9dc4aR21-R24) [[2]](diffhunk://#diff-05ce5b7611ffd8756a39d2e4c9eacda5896e22ddb7801af98e35ec0cd0e9dc4aR98-R103) [[3]](diffhunk://#diff-05ce5b7611ffd8756a39d2e4c9eacda5896e22ddb7801af98e35ec0cd0e9dc4aR137-R142) **Testing Enhancements:** * Added unit tests for the new services and model in `test_programs_parameter_groups.py`, covering endpoints, method presence, and model field parsing. * Added end-to-end tests for both sync and async parameter group operations (create, update, get, delete, filter/select) in `test_sync_parameter_group.py` and `test_async_parameter_group.py`, with supporting fixtures in `conftest.py`. [[1]](diffhunk://#diff-09b72919a13c1ba833eff2e9ac4d8725bbe83505ea0e8066f3daa5efc9ab33bfR1-R63) [[2]](diffhunk://#diff-a65325b4113e9a1e84cedac95f5a941d909c3c3670ec91e56c5aaa35425a3886R1-R63) [[3]](diffhunk://#diff-fdc8528b24c659ceb6d9a1a44ff78517855d5379d4d454c5c2f66f454f578ce0R1-R22) * Updated unit tests for `Program` services to check for the presence of the new `.parameter_groups` methods. [[1]](diffhunk://#diff-57c1fc2a8ee6b316dfadf82a52e52ba29deeaabc135475c62652eb4df81b90aaR15-R18) [[2]](diffhunk://#diff-57c1fc2a8ee6b316dfadf82a52e52ba29deeaabc135475c62652eb4df81b90aaR96) [[3]](diffhunk://#diff-57c1fc2a8ee6b316dfadf82a52e52ba29deeaabc135475c62652eb4df81b90aaR111) **Configuration Updates:** * Added a seeded parameter group ID to `e2e_config.test.json` for use in tests. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> Closes [MPT-20326](https://softwareone.atlassian.net/browse/MPT-20326) - Added `ParameterGroup` model with typed fields for metadata (name, label, description, display_order, default), counts (parameter_count), and relationships (program, audit) - Introduced `ParameterGroupsService` and `AsyncParameterGroupsService` with endpoint configuration at `/public/v1/program/programs/{program_id}/parameter-groups` and support for managed resource operations (create, read, update, delete, list, filter, select) - Extended `ProgramsService` and `AsyncProgramsService` with `parameter_groups(program_id)` factory methods for accessing parameter group services - Added comprehensive end-to-end tests for synchronous and asynchronous parameter group operations including create, update, get, delete, and filtered listing with field selection - Added unit tests validating service configuration, endpoint paths, public method availability, and model field parsing - Added pytest fixtures for parameter group e2e tests with test data and configuration <!-- end of auto-generated comment: release notes by coderabbit.ai --> [MPT-20326]: https://softwareone.atlassian.net/browse/MPT-20326?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2 parents 7c4e679 + 78664e1 commit acd48db

8 files changed

Lines changed: 323 additions & 0 deletions

File tree

e2e_config.test.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
"integration.extension.id": "EXT-6587-4477",
7272
"integration.term.id": "ETC-6587-4477-0062",
7373
"program.media.id": "PMD-9643-3741-0001",
74+
"program.parameter.group.id": "PPG-9643-3741-0002",
7475
"program.program.id": "PRG-9643-3741",
7576
"program.document.file.id": "PDM-9643-3741-0001"
7677
}

mpt_api_client/resources/program/programs.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
AsyncMediaService,
1919
MediaService,
2020
)
21+
from mpt_api_client.resources.program.programs_parameter_groups import (
22+
AsyncParameterGroupsService,
23+
ParameterGroupsService,
24+
)
2125

2226

2327
class Program(Model):
@@ -91,6 +95,12 @@ def media(self, program_id: str) -> MediaService:
9195
http_client=self.http_client, endpoint_params={"program_id": program_id}
9296
)
9397

98+
def parameter_groups(self, program_id: str) -> ParameterGroupsService:
99+
"""Return program parameter groups service."""
100+
return ParameterGroupsService(
101+
http_client=self.http_client, endpoint_params={"program_id": program_id}
102+
)
103+
94104

95105
class AsyncProgramsService(
96106
AsyncGetMixin[Program],
@@ -124,3 +134,9 @@ def media(self, program_id: str) -> AsyncMediaService:
124134
return AsyncMediaService(
125135
http_client=self.http_client, endpoint_params={"program_id": program_id}
126136
)
137+
138+
def parameter_groups(self, program_id: str) -> AsyncParameterGroupsService:
139+
"""Return async program parameter groups service."""
140+
return AsyncParameterGroupsService(
141+
http_client=self.http_client, endpoint_params={"program_id": program_id}
142+
)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from mpt_api_client.http import AsyncService, Service
2+
from mpt_api_client.http.mixins import (
3+
AsyncCollectionMixin,
4+
AsyncManagedResourceMixin,
5+
CollectionMixin,
6+
ManagedResourceMixin,
7+
)
8+
from mpt_api_client.models import Model
9+
from mpt_api_client.models.model import BaseModel
10+
11+
12+
class ParameterGroup(Model):
13+
"""Parameter Group resource.
14+
15+
Attributes:
16+
name: Parameter group name.
17+
label: Display label for the parameter group.
18+
description: Parameter group description.
19+
display_order: Display order of the group.
20+
default: Whether this is the default parameter group.
21+
parameter_count: Number of parameters in this group.
22+
program: Reference to the program this group belongs to.
23+
audit: Audit information (created, updated events).
24+
"""
25+
26+
name: str | None
27+
label: str | None
28+
description: str | None
29+
display_order: int | None
30+
default: bool | None
31+
parameter_count: int | None
32+
program: BaseModel | None
33+
audit: BaseModel | None
34+
35+
36+
class ParameterGroupsServiceConfig:
37+
"""Parameter Groups service configuration."""
38+
39+
_endpoint = "/public/v1/program/programs/{program_id}/parameter-groups"
40+
_model_class = ParameterGroup
41+
_collection_key = "data"
42+
43+
44+
class ParameterGroupsService(
45+
ManagedResourceMixin[ParameterGroup],
46+
CollectionMixin[ParameterGroup],
47+
Service[ParameterGroup],
48+
ParameterGroupsServiceConfig,
49+
):
50+
"""Parameter Groups service."""
51+
52+
53+
class AsyncParameterGroupsService(
54+
AsyncManagedResourceMixin[ParameterGroup],
55+
AsyncCollectionMixin[ParameterGroup],
56+
AsyncService[ParameterGroup],
57+
ParameterGroupsServiceConfig,
58+
):
59+
"""Parameter Groups service."""
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import pytest
2+
3+
4+
@pytest.fixture
5+
def parameter_group_id(e2e_config):
6+
return e2e_config["program.parameter.group.id"]
7+
8+
9+
@pytest.fixture
10+
def invalid_parameter_group_id():
11+
return "PPG-0000-0000-0000"
12+
13+
14+
@pytest.fixture
15+
def parameter_group_data():
16+
return {
17+
"name": "E2E Created Program Parameter Group",
18+
"description": "E2E Created Program Parameter Group",
19+
"label": "E2E Created Program Parameter Group",
20+
"default": False,
21+
"displayOrder": 100,
22+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import pytest
2+
3+
from mpt_api_client.exceptions import MPTAPIError
4+
from mpt_api_client.rql.query_builder import RQLQuery
5+
6+
pytestmark = [pytest.mark.flaky]
7+
8+
9+
@pytest.fixture
10+
async def created_parameter_group(async_mpt_vendor, program_id, parameter_group_data):
11+
service = async_mpt_vendor.program.programs.parameter_groups(program_id)
12+
group = await service.create(parameter_group_data)
13+
yield group
14+
try:
15+
await service.delete(group.id)
16+
except MPTAPIError as error:
17+
print(f"TEARDOWN - Unable to delete parameter group {group.id}: {error.title}") # noqa: WPS421
18+
19+
20+
def test_create_parameter_group(created_parameter_group):
21+
result = created_parameter_group.name == "E2E Created Program Parameter Group"
22+
23+
assert result is True
24+
25+
26+
async def test_update_parameter_group(async_mpt_vendor, program_id, created_parameter_group):
27+
service = async_mpt_vendor.program.programs.parameter_groups(program_id)
28+
update_data = {"name": "E2E Updated Program Parameter Group"}
29+
30+
result = await service.update(created_parameter_group.id, update_data)
31+
32+
assert result.name == update_data["name"]
33+
34+
35+
async def test_get_parameter_group(async_mpt_vendor, program_id, parameter_group_id):
36+
service = async_mpt_vendor.program.programs.parameter_groups(program_id)
37+
38+
result = await service.get(parameter_group_id)
39+
40+
assert result.id == parameter_group_id
41+
42+
43+
async def test_delete_parameter_group(async_mpt_vendor, program_id, created_parameter_group):
44+
parameter_group_data = created_parameter_group
45+
46+
result = async_mpt_vendor.program.programs.parameter_groups(program_id)
47+
48+
await result.delete(parameter_group_data.id)
49+
50+
51+
async def test_filter_and_select_parameter_groups(async_mpt_vendor, program_id, parameter_group_id):
52+
select_fields = ["-description", "-audit"]
53+
filtered_groups = (
54+
async_mpt_vendor.program.programs
55+
.parameter_groups(program_id)
56+
.filter(RQLQuery(id=parameter_group_id))
57+
.filter(RQLQuery(name="E2E Seeded Program Parameter Group"))
58+
.select(*select_fields)
59+
)
60+
61+
result = [parameter_group async for parameter_group in filtered_groups.iterate()]
62+
63+
assert len(result) == 1
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import pytest
2+
3+
from mpt_api_client.exceptions import MPTAPIError
4+
from mpt_api_client.rql.query_builder import RQLQuery
5+
6+
pytestmark = [pytest.mark.flaky]
7+
8+
9+
@pytest.fixture
10+
def created_parameter_group(mpt_vendor, program_id, parameter_group_data):
11+
service = mpt_vendor.program.programs.parameter_groups(program_id)
12+
group = service.create(parameter_group_data)
13+
yield group
14+
try:
15+
service.delete(group.id)
16+
except MPTAPIError as error:
17+
print(f"TEARDOWN - Unable to delete parameter group {group.id}: {error.title}") # noqa: WPS421
18+
19+
20+
def test_create_parameter_group(created_parameter_group):
21+
result = created_parameter_group.name == "E2E Created Program Parameter Group"
22+
23+
assert result is True
24+
25+
26+
def test_update_parameter_group(mpt_vendor, program_id, created_parameter_group):
27+
service = mpt_vendor.program.programs.parameter_groups(program_id)
28+
update_data = {"name": "E2E Updated Program Parameter Group"}
29+
30+
result = service.update(created_parameter_group.id, update_data)
31+
32+
assert result.name == update_data["name"]
33+
34+
35+
def test_get_parameter_group(mpt_vendor, program_id, parameter_group_id):
36+
service = mpt_vendor.program.programs.parameter_groups(program_id)
37+
38+
result = service.get(parameter_group_id)
39+
40+
assert result.id == parameter_group_id
41+
42+
43+
def test_delete_parameter_group(mpt_vendor, program_id, created_parameter_group):
44+
parameter_group_data = created_parameter_group
45+
46+
result = mpt_vendor.program.programs.parameter_groups(program_id)
47+
48+
result.delete(parameter_group_data.id)
49+
50+
51+
def test_filter_and_select_parameter_groups(mpt_vendor, program_id, parameter_group_id):
52+
select_fields = ["-description", "-audit"]
53+
filtered_groups = (
54+
mpt_vendor.program.programs
55+
.parameter_groups(program_id)
56+
.filter(RQLQuery(id=parameter_group_id))
57+
.filter(RQLQuery(name="E2E Seeded Program Parameter Group"))
58+
.select(*select_fields)
59+
)
60+
61+
result = list(filtered_groups.iterate())
62+
63+
assert len(result) == 1

tests/unit/resources/program/test_programs.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
AsyncMediaService,
1313
MediaService,
1414
)
15+
from mpt_api_client.resources.program.programs_parameter_groups import (
16+
AsyncParameterGroupsService,
17+
ParameterGroupsService,
18+
)
1519

1620

1721
@pytest.fixture
@@ -89,6 +93,7 @@ def test_async_mixins_present(async_programs_service, method):
8993
[
9094
("documents", DocumentService),
9195
("media", MediaService),
96+
("parameter_groups", ParameterGroupsService),
9297
],
9398
)
9499
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
103108
[
104109
("documents", AsyncDocumentService),
105110
("media", AsyncMediaService),
111+
("parameter_groups", AsyncParameterGroupsService),
106112
],
107113
)
108114
def test_async_property_services(async_programs_service, service_method, expected_service_class):
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import pytest
2+
3+
from mpt_api_client.models.model import BaseModel
4+
from mpt_api_client.resources.program.programs_parameter_groups import (
5+
AsyncParameterGroupsService,
6+
ParameterGroup,
7+
ParameterGroupsService,
8+
)
9+
10+
11+
@pytest.fixture
12+
def parameter_groups_service(http_client):
13+
return ParameterGroupsService(
14+
http_client=http_client, endpoint_params={"program_id": "PPG-001"}
15+
)
16+
17+
18+
@pytest.fixture
19+
def async_parameter_groups_service(async_http_client):
20+
return AsyncParameterGroupsService(
21+
http_client=async_http_client, endpoint_params={"program_id": "PPG-001"}
22+
)
23+
24+
25+
@pytest.fixture
26+
def parameter_group_data():
27+
return {
28+
"id": "GRP-001",
29+
"name": "Program Parameter Group",
30+
"label": "Program Parameter Group",
31+
"description": "Program Parameter Group",
32+
"displayOrder": 1,
33+
"default": True,
34+
"parameterCount": 5,
35+
"program": {"id": "PPG-001", "name": "Program"},
36+
"audit": {
37+
"created": {"at": "2024-01-01T00:00:00Z"},
38+
"updated": {"at": "2024-01-02T00:00:00Z"},
39+
},
40+
}
41+
42+
43+
def test_endpoint(parameter_groups_service):
44+
result = parameter_groups_service.path == "/public/v1/program/programs/PPG-001/parameter-groups"
45+
46+
assert result is True
47+
48+
49+
def test_async_endpoint(async_parameter_groups_service):
50+
result = (
51+
async_parameter_groups_service.path
52+
== "/public/v1/program/programs/PPG-001/parameter-groups"
53+
)
54+
55+
assert result is True
56+
57+
58+
@pytest.mark.parametrize("method", ["get", "create", "delete", "update", "iterate"])
59+
def test_methods_present(parameter_groups_service, method):
60+
result = hasattr(parameter_groups_service, method)
61+
62+
assert result is True
63+
64+
65+
@pytest.mark.parametrize("method", ["get", "create", "delete", "update", "iterate"])
66+
def test_async_methods_present(async_parameter_groups_service, method):
67+
result = hasattr(async_parameter_groups_service, method)
68+
69+
assert result is True
70+
71+
72+
def test_parameter_group_primitive_fields(parameter_group_data):
73+
result = ParameterGroup(parameter_group_data)
74+
75+
assert result.to_dict() == parameter_group_data
76+
77+
78+
def test_parameter_group_nested_field_types(parameter_group_data):
79+
result = ParameterGroup(parameter_group_data)
80+
81+
assert isinstance(result.program, BaseModel)
82+
assert isinstance(result.audit, BaseModel)
83+
84+
85+
def test_parameter_group_optional_fields_absent():
86+
result = ParameterGroup({"id": "PPG-001"})
87+
88+
assert result.id == "PPG-001"
89+
assert not hasattr(result, "name")
90+
assert not hasattr(result, "label")
91+
assert not hasattr(result, "description")
92+
assert not hasattr(result, "program")
93+
assert not hasattr(result, "audit")

0 commit comments

Comments
 (0)