diff --git a/HISTORY.rst b/HISTORY.rst index 4a6bc22..f707936 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,14 @@ History ------- +2.11.0 ++++++++++++++++++++ + +* Updated the validation for the Report Transactions API to make the + ``ip_address`` parameter optional. Now the ``tag`` and at least one of the + following parameters must be supplied: ``ip_address``, ``maxmind_id``, + ``minfraud_id``, ``transaction_id``. + 2.10.0 (2024-04-16) +++++++++++++++++++ diff --git a/README.rst b/README.rst index e4b8bc2..144761d 100644 --- a/README.rst +++ b/README.rst @@ -98,7 +98,8 @@ The method takes a dictionary representing the report to be sent to the web service. The structure of this dictionary should be in `the format specified in the REST API documentation `__. The -``ip_address`` and ``tag`` fields are required. All other fields are optional. +required fields are ``tag`` and one or more of the following: ``ip_address``, +``maxmind_id``, ``minfraud_id``, ``transaction_id``. Request Validation (for all request methods) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/minfraud/validation.py b/minfraud/validation.py index 7116787..296b660 100644 --- a/minfraud/validation.py +++ b/minfraud/validation.py @@ -379,14 +379,33 @@ def _uuid(s: str) -> str: raise ValueError -validate_report = Schema( +def _transaction_id(s: Optional[str]) -> str: + if isinstance(s, str) and len(s) > 0: + return s + raise ValueError + + +validate_report_schema = Schema( { "chargeback_code": str, - Required("ip_address"): _ip_address, + "ip_address": _ip_address, "maxmind_id": _maxmind_id, "minfraud_id": _uuid, "notes": str, Required("tag"): _tag, - "transaction_id": str, + "transaction_id": _transaction_id, }, ) + + +def validate_at_least_one_identifier_field(report): + optional_fields = ["ip_address", "maxmind_id", "minfraud_id", "transaction_id"] + if not any(field in report for field in optional_fields): + raise ValueError("The report must contain at least one of the following fields: 'ip_address', 'maxmind_id', 'minfraud_id', 'transaction_id'.") + return True + + +def validate_report(report): + validate_report_schema(report) + validate_at_least_one_identifier_field(report) + return True diff --git a/tests/test_validation.py b/tests/test_validation.py index 62a8953..87084ae 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -51,11 +51,17 @@ def setup_report(self, report): def check_invalid_report(self, report): self.setup_report(report) + self.check_invalid_report_no_setup(report) + + def check_invalid_report_no_setup(self, report): with self.assertRaises(MultipleInvalid, msg=f"{report} is invalid"): validate_report(report) def check_report(self, report): self.setup_report(report) + self.check_report_no_setup(report) + + def check_report_no_setup(self, report): try: validate_report(report) except MultipleInvalid as e: @@ -431,3 +437,9 @@ def test_tag(self): self.check_report({"tag": good}) for bad in ("risky_business", "", None): self.check_invalid_report({"tag": bad}) + + def test_report_valid_identifier(self): + self.check_report_no_setup({"tag": "chargeback", "ip_address": "1.1.1.1"}) + self.check_report_no_setup({"tag": "chargeback", "minfraud_id": "58fa38d8-4b87-458b-a22b-f00eda1aa20d"}) + self.check_report_no_setup({"tag": "chargeback", "maxmind_id": "12345678"}) + self.check_report_no_setup({"tag": "chargeback", "transaction_id": "abc123"})