Skip to content

Commit 570765a

Browse files
ayeshalshukri1-nhsKarthikeyannhsdependabot[bot]shweta-nhs
authored
ELI-150: campaign config validation (#264)
* validations - wip * iteration validation * iteration rules * campaign config validation * made BUC tests bit more clear * Renaming for clarity. * lint and formatting fixes. * wip * Integration Rules Test * Actions mapper validator * Iterations BUC * available_actions tests * lint fixes * lint fixes * Bump asgiref from 3.8.1 to 3.9.1 Bumps [asgiref](https://github.com/django/asgiref) from 3.8.1 to 3.9.1. - [Changelog](https://github.com/django/asgiref/blob/main/CHANGELOG.txt) - [Commits](django/asgiref@3.8.1...3.9.1) --- updated-dependencies: - dependency-name: asgiref dependency-version: 3.9.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> * Bump gitpython from 3.1.44 to 3.1.45 Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.44 to 3.1.45. - [Release notes](https://github.com/gitpython-developers/GitPython/releases) - [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES) - [Commits](gitpython-developers/GitPython@3.1.44...3.1.45) --- updated-dependencies: - dependency-name: gitpython dependency-version: 3.1.45 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> * Bump pyright from 1.1.402 to 1.1.403 Bumps [pyright](https://github.com/RobertCraigie/pyright-python) from 1.1.402 to 1.1.403. - [Release notes](https://github.com/RobertCraigie/pyright-python/releases) - [Commits](RobertCraigie/pyright-python@v1.1.402...v1.1.403) --- updated-dependencies: - dependency-name: pyright dependency-version: 1.1.403 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> * ELI-351: Moves/deletes tests after refactoring (#265) * ELI-351: Moves/deletes tests after refactoring * ELI-351: Extracts EligibilityResultBuilder and adds tests * ELI-351: De-extracts EligibilityResultBuilder and moves tests to Eligibility Calculator tests * ELI-351: Removes duplicated tests * ELI-351: Removes duplicated tests #2 * ELI-351: Adds validation and audit layer to Readme * wip - has failing tests * test fixed and lint error fixed * warning fixed * rules validation added * test commit * tests updated w.r.t to datatype changes from main * updated output message * arguments added to app.py * sonar fix * sonar fix * sonar fix --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: karthikeyannhs <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Shweta <[email protected]>
1 parent 81bb071 commit 570765a

19 files changed

+982
-5
lines changed

src/rules_validation_api/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# 🧪 Campaign-config Validation
2+
3+
This Python script is designed to validate a campaign configuration JSON file.
4+
5+
## 🛠 Requirements
6+
7+
- Python 3.13
8+
- `rules_validation_api` must be installed and accessible
9+
- Campaign configuration JSON file to verify
10+
11+
## Steps to verify
12+
13+
- Get to the `rules_validation_api` folder
14+
- Run `python app.py --config_path <path_to_config>`
15+
16+
## Results
17+
18+
- `On success`:
19+
20+
```text
21+
"Valid config" is printed
22+
23+
- `On Failure`:
24+
25+
```text
26+
"Errors" is printed

src/rules_validation_api/__init__.py

Whitespace-only changes.

src/rules_validation_api/app.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import argparse
2+
import json
3+
import sys
4+
from pathlib import Path
5+
6+
from rules_validation_api.validators.rules_validator import RulesValidation
7+
8+
GREEN = "\033[92m" # pragma: no cover
9+
RESET = "\033[0m" # pragma: no cover
10+
YELLOW = "\033[93m" # pragma: no cover
11+
RED = "\033[91m" # pragma: no cover
12+
13+
14+
def main() -> None: # pragma: no cover
15+
parser = argparse.ArgumentParser(description="Validate campaign configuration.")
16+
parser.add_argument("--config_path", required=True, help="Path to the campaign config JSON file")
17+
args = parser.parse_args()
18+
19+
try:
20+
with Path(args.config_path).open() as file:
21+
json_data = json.load(file)
22+
RulesValidation(**json_data)
23+
sys.stdout.write(f"{GREEN}Valid Config{RESET}\n")
24+
except ValueError as e:
25+
sys.stderr.write(f"{YELLOW}Validation Error:{RESET} {RED}{e}{RESET}\n")
26+
27+
28+
if __name__ == "__main__": # pragma: no cover
29+
main()

src/rules_validation_api/validators/__init__.py

Whitespace-only changes.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from pydantic import model_validator
2+
3+
from eligibility_signposting_api.model.campaign_config import ActionsMapper
4+
5+
6+
class ActionsMapperValidator(ActionsMapper):
7+
@model_validator(mode="after")
8+
def validate_keys(self) -> "ActionsMapperValidator":
9+
invalid_keys = [key for key in self.root if key is None or key == ""]
10+
if invalid_keys:
11+
msg = f"Invalid keys found in ActionsMapper: {invalid_keys}"
12+
raise ValueError(msg)
13+
return self
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from eligibility_signposting_api.model.campaign_config import AvailableAction
2+
3+
4+
class AvailableActionValidation(AvailableAction):
5+
pass
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from pydantic import field_validator
2+
3+
from eligibility_signposting_api.model.campaign_config import CampaignConfig, Iteration
4+
from rules_validation_api.validators.iteration_validator import IterationValidation
5+
6+
7+
class CampaignConfigValidation(CampaignConfig):
8+
@classmethod
9+
@field_validator("iterations")
10+
def validate_iterations(cls, iterations: list[Iteration]) -> list[IterationValidation]:
11+
return [IterationValidation(**i.model_dump()) for i in iterations]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from eligibility_signposting_api.model.campaign_config import IterationRule
2+
3+
4+
class IterationRuleValidation(IterationRule):
5+
pass
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import typing
2+
3+
from pydantic import ValidationError, field_validator, model_validator
4+
from pydantic_core import InitErrorDetails
5+
6+
from eligibility_signposting_api.model.campaign_config import ActionsMapper, Iteration, IterationRule
7+
from rules_validation_api.validators.actions_mapper_validator import ActionsMapperValidator
8+
from rules_validation_api.validators.iteration_rules_validator import IterationRuleValidation
9+
10+
11+
class IterationValidation(Iteration):
12+
@classmethod
13+
@field_validator("iteration_rules")
14+
def validate_iterations(cls, iteration_rules: list[IterationRule]) -> list[IterationRuleValidation]:
15+
return [IterationRuleValidation(**i.model_dump()) for i in iteration_rules]
16+
17+
@classmethod
18+
@field_validator("actions_mapper", mode="after")
19+
def transform_actions_mapper(cls, action_mapper: ActionsMapper) -> ActionsMapper:
20+
ActionsMapperValidator.model_validate(action_mapper.model_dump())
21+
return action_mapper
22+
23+
@model_validator(mode="after")
24+
def validate_default_comms_routing_in_actions_mapper(self) -> typing.Self:
25+
default_routing = self.default_comms_routing
26+
actions_mapper = self.actions_mapper.root.keys()
27+
28+
if default_routing and (not actions_mapper or default_routing not in actions_mapper):
29+
error = InitErrorDetails(
30+
type="value_error",
31+
loc=("actions_mapper",),
32+
input=actions_mapper,
33+
ctx={"error": f"Missing entry for DefaultCommsRouting '{default_routing}' in ActionsMapper"},
34+
)
35+
raise ValidationError.from_exception_data(title="IterationValidation", line_errors=[error])
36+
37+
return self
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from pydantic import field_validator
2+
3+
from eligibility_signposting_api.model.campaign_config import CampaignConfig, Rules
4+
from rules_validation_api.validators.campaign_config_validator import CampaignConfigValidation
5+
6+
7+
class RulesValidation(Rules):
8+
@classmethod
9+
@field_validator("campaign_config")
10+
def validate_campaign_config(cls, campaign_config: CampaignConfig) -> CampaignConfig:
11+
return CampaignConfigValidation(**campaign_config.model_dump())

0 commit comments

Comments
 (0)