Skip to content

Commit 7f0bf5f

Browse files
fix: fix postcode type to raise error when postcode submitted without space (#53)
1 parent 2d10832 commit 7f0bf5f

2 files changed

Lines changed: 62 additions & 10 deletions

File tree

src/dve/metadata_parser/domain_types.py

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -173,33 +173,67 @@ def permissive_nhs_number(warn_on_test_numbers: bool = False):
173173
return type("NHSNumber", (NHSNumber, *NHSNumber.__bases__), dict_)
174174

175175

176-
# TODO: Make the spacing configurable. Not all downstream consumers want a single space
177176
class Postcode(types.ConstrainedStr):
178177
"""Postcode constrained string"""
179178

180179
regex: re.Pattern = POSTCODE_REGEX
181180
strip_whitespace = True
181+
apply_normalize = True
182182

183183
@staticmethod
184-
def normalize(postcode: str) -> Optional[str]:
184+
def normalize(_postcode: str) -> Optional[str]:
185185
"""Strips internal and external spaces"""
186-
postcode = postcode.replace(" ", "")
187-
if not postcode or postcode.lower() in NULL_POSTCODES:
186+
_postcode = _postcode.replace(" ", "")
187+
if not _postcode or _postcode.lower() in NULL_POSTCODES:
188188
return None
189-
postcode = postcode.replace(" ", "")
190-
return " ".join((postcode[0:-3], postcode[-3:])).upper()
189+
_postcode = _postcode.replace(" ", "")
190+
return " ".join((_postcode[0:-3], _postcode[-3:])).upper()
191191

192192
@classmethod
193193
def validate(cls, value: str) -> Optional[str]: # type: ignore
194194
"""Validates the given postcode"""
195-
stripped = cls.normalize(value)
196-
if not stripped:
195+
if cls.apply_normalize and value:
196+
value = cls.normalize(value) # type: ignore
197+
198+
if not value:
197199
return None
198200

199-
if not cls.regex.match(stripped):
201+
if not cls.regex.match(value):
200202
raise ValueError("Invalid Postcode submitted")
201203

202-
return stripped
204+
return value
205+
206+
207+
@lru_cache()
208+
@validate_arguments
209+
def postcode(
210+
# pylint: disable=R0913
211+
strip_whitespace: Optional[bool] = True,
212+
to_upper: Optional[bool] = False,
213+
to_lower: Optional[bool] = False,
214+
strict: Optional[bool] = False,
215+
min_length: Optional[int] = None,
216+
max_length: Optional[int] = None,
217+
curtail_length: Optional[int] = None,
218+
regex: Optional[str] = POSTCODE_REGEX, # type: ignore
219+
apply_normalize: Optional[bool] = True,
220+
) -> type[Postcode]:
221+
"""Return a formatted date class with a set date format
222+
and timezone treatment.
223+
224+
"""
225+
dict_ = Postcode.__dict__.copy()
226+
dict_["strip_whitespace"] = strip_whitespace
227+
dict_["to_upper"] = to_upper
228+
dict_["to_lower"] = to_lower
229+
dict_["strict"] = strict
230+
dict_["min_length"] = min_length
231+
dict_["max_length"] = max_length
232+
dict_["curtail_length"] = curtail_length
233+
dict_["regex"] = regex
234+
dict_["apply_normalize"] = apply_normalize
235+
236+
return type("Postcode", (Postcode, *Postcode.__bases__), dict_)
203237

204238

205239
class OrgID(_SimpleRegexValidator):

tests/test_model_generation/test_domain_types.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,24 @@ def test_postcode(postcode, expected):
9898
assert model.postcode == expected
9999

100100

101+
@pytest.mark.parametrize(
102+
("postcode", "should_error"),
103+
[
104+
("LS479AJ", True),
105+
("PostcodeIamNot", True),
106+
("LS47 9AJ", False)
107+
]
108+
)
109+
def test_postcode_errors_with_apply_normalize_disabled(postcode: str, should_error: bool):
110+
postcode_type = hct.postcode(apply_normalize=False)
111+
112+
if should_error:
113+
with pytest.raises(ValueError, match="Invalid Postcode submitted"):
114+
assert postcode_type.validate(postcode)
115+
else:
116+
assert postcode_type.validate(postcode)
117+
118+
101119
@pytest.mark.parametrize(("org_id", "expected"), [("AB123", "AB123"), ("ABCDE", "ABCDE")])
102120
def test_org_id_passes(org_id, expected):
103121
model = ATestModel(org_id=org_id)

0 commit comments

Comments
 (0)