Skip to content

Commit d439ebf

Browse files
Iterations BUC
1 parent 050da51 commit d439ebf

4 files changed

Lines changed: 80 additions & 16 deletions

File tree

src/rules_validation_api/validators/campaign_config_validator.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
class CampaignConfigValidation(CampaignConfig):
88
iterations: list[IterationValidation] = Field(..., min_length=1, alias="Iterations")
99

10-
@field_validator("id")
11-
def validate_name(cls, value: str) -> str:
12-
if not value.strip():
13-
raise ValueError("campaign ID must not be empty")
14-
return value
15-
16-
@field_validator("type")
17-
def validate_type(cls, value: str) -> str:
18-
allowed_values = {"V", "S"}
19-
if value not in allowed_values:
20-
raise ValueError(f"type must be one of {allowed_values}")
21-
return value
10+
# @field_validator("id")
11+
# def validate_name(cls, value: str) -> str:
12+
# if not value.strip():
13+
# raise ValueError("campaign ID must not be empty")
14+
# return value
15+
#
16+
# @field_validator("type")
17+
# def validate_type(cls, value: str) -> str:
18+
# allowed_values = {"V", "S"}
19+
# if value not in allowed_values:
20+
# raise ValueError(f"type must be one of {allowed_values}")
21+
# return value

src/rules_validation_api/validators/iteration_validator.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from pydantic import Field
1+
from pydantic import Field, ValidationError, model_validator
2+
from pydantic_core import InitErrorDetails
23

34
from eligibility_signposting_api.model.campaign_config import Iteration
45
from rules_validation_api.validators.actions_mapper_validator import ActionsMapperValidator
@@ -9,3 +10,22 @@ class IterationValidation(Iteration):
910
iteration_rules: list[IterationRuleValidation] = Field(..., alias="IterationRules")
1011
actions_mapper: ActionsMapperValidator = Field(..., alias="ActionsMapper")
1112

13+
@model_validator(mode="after")
14+
def validate_default_comms_routing_in_actions_mapper(self):
15+
default_routing = self.default_comms_routing
16+
actions_mapper = self.actions_mapper.root.keys()
17+
18+
if default_routing:
19+
if not actions_mapper or default_routing not in actions_mapper:
20+
error = InitErrorDetails(
21+
type='value_error',
22+
loc=('actions_mapper',),
23+
input=actions_mapper,
24+
ctx={'error': f"Missing entry for DefaultCommsRouting '{default_routing}' in ActionsMapper"}
25+
)
26+
raise ValidationError.from_exception_data(
27+
title='IterationValidation',
28+
line_errors=[error]
29+
)
30+
31+
return self

tests/unit/validation/conftest.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,22 @@ def valid_campaign_config_with_only_mandatory_fields():
2323
"ApprovalMinimum": 10,
2424
"ApprovalMaximum": 100,
2525
"Type": "A",
26-
"DefaultCommsRouting": "RouteA",
26+
"DefaultCommsRouting": "BOOK_NBS",
2727
"DefaultNotEligibleRouting": "RouteB",
2828
"DefaultNotActionableRouting": "RouteC",
2929
"IterationCohorts": [],
3030
"IterationRules": [],
31-
"ActionsMapper": {},
31+
"ActionsMapper":
32+
{
33+
"BOOK_NBS":
34+
{
35+
"ExternalRoutingCode": "BookNBS",
36+
"ActionDescription": "",
37+
"ActionType": "ButtonWithAuthLink",
38+
"UrlLink": "http://www.nhs.uk/book-rsv",
39+
"UrlLabel": "Continue to booking"
40+
}
41+
}
3242
}
3343
],
3444
}

tests/unit/validation/test_iteration_validator.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def test_invalid_type(self, type_value, valid_campaign_config_with_only_mandator
8181
IterationValidation(**data)
8282

8383
# DefaultCommsRouting
84-
@pytest.mark.parametrize("routing_value", ["RouteA", "R1", "MainComm"])
84+
@pytest.mark.parametrize("routing_value", ["BOOK_NBS"])
8585
def test_valid_default_comms_routing(self, routing_value, valid_campaign_config_with_only_mandatory_fields):
8686
data = {
8787
**valid_campaign_config_with_only_mandatory_fields["Iterations"][0],
@@ -140,3 +140,37 @@ def test_approval_maximum(self, approval_maximum, valid_campaign_config_with_onl
140140
}
141141
model = IterationValidation(**data)
142142
assert model.approval_maximum == approval_maximum
143+
144+
145+
class TestIterationCohortsSchemaValidations:
146+
def test_valid_iteration_if_actions_mapper_has_entry_for_the_provided_default_routing_key(self, valid_campaign_config_with_only_mandatory_fields):
147+
expected_action = {
148+
"ExternalRoutingCode": "BookLocal",
149+
"ActionDescription": "##Getting the vaccine\n"
150+
"You can get an RSV vaccination at your GP surgery.\n"
151+
"Your GP surgery may contact you about getting the RSV vaccine. "
152+
"This may be by letter, text, phone call, email or through the NHS App. "
153+
"You do not need to wait to be contacted before booking your vaccination.",
154+
"ActionType": "InfoText"
155+
}
156+
157+
data = {**valid_campaign_config_with_only_mandatory_fields["Iterations"][0],
158+
"DefaultCommsRouting": "BOOK_LOCAL", "ActionsMapper": {
159+
"BOOK_LOCAL": expected_action
160+
}}
161+
IterationValidation(**data)
162+
163+
def test_invalid_iteration_if_actions_mapper_has_no_entry_for_the_provided_default_routing_key(self, valid_campaign_config_with_only_mandatory_fields):
164+
data = {**valid_campaign_config_with_only_mandatory_fields["Iterations"][0],
165+
"DefaultCommsRouting": "BOOK_LOCAL", "ActionsMapper": {}} # Missing BOOK_LOCAL in ActionsMapper
166+
167+
with pytest.raises(ValidationError) as error:
168+
IterationValidation(**data)
169+
170+
errors = error.value.errors()
171+
assert any(
172+
e["loc"][-1] == "actions_mapper" and "BOOK_LOCAL" in str(e["msg"])
173+
for e in errors
174+
), "Expected validation error for missing BOOK_LOCAL entry in ActionsMapper"
175+
176+

0 commit comments

Comments
 (0)