Skip to content

Commit b5c7d3b

Browse files
committed
Add new validator that can conditionally validate based on data passed in
1 parent 0cc516a commit b5c7d3b

1 file changed

Lines changed: 51 additions & 8 deletions

File tree

spoke/__init__.py

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def passthrough(v):
2727

2828
class Validator(object):
2929
is_required = True
30+
is_conditional = False
3031

3132
def __init__(self, inner=None):
3233
if inner is None:
@@ -51,6 +52,35 @@ def __call__(self, value):
5152
class Required(Validator):
5253
pass
5354

55+
class RequiredOnlyIfNot(Required):
56+
""" This validator will require the key ONLY IF other keys are NOT present in
57+
the payload.
58+
59+
This validator was added because threadless.com payloads use "ShippingMethod" whereas
60+
Artist Shops payloads use "ShippingAccount" and "ShippingMethodId"
61+
62+
An example would be that SomeKey is only required if SomeOtherKey is not present in the payload:
63+
"SomeKey" = RequiredOnlyIfNot(['SomeOtherKey'])
64+
65+
"""
66+
is_required = True
67+
is_conditional = True
68+
other_keys = []
69+
70+
def __init__(self, other_keys=[], inner=None):
71+
if not isinstance(other_keys, (tuple, list)):
72+
other_keys = [other_keys]
73+
self.other_keys = other_keys
74+
75+
super(RequiredOnlyIfNot, self).__init__(inner)
76+
77+
def __call__(self, value, d):
78+
# if all of other_keys are present in the payload,
79+
# then require don't require this field
80+
if all([key in d.keys() for key in self.other_keys]):
81+
self.is_required = False
82+
83+
return super(RequiredOnlyIfNot, self).__call__(value)
5484

5585
class Optional(Validator):
5686
is_required = False
@@ -81,7 +111,17 @@ def _validate(d, **validation_spec):
81111
validator = validation_spec.pop(k, None)
82112
if validator is None:
83113
raise ValidationError('parameter "%s" not allowed' % k)
84-
d[k] = validator(v)
114+
if validator.is_conditional: # conditional validators need the whole dictionary to look at other keys
115+
d[k] = validator(v, d)
116+
else:
117+
d[k] = validator(v)
118+
119+
# it's possible that there's some conditional validators still in the validation_spec
120+
# because their corresponding key isn't in the payload, so look over them and if all
121+
# of their other_keys are present in the payload, then this conditional validator isn't required
122+
for k, v in validation_spec.items():
123+
if v.is_conditional and all([key in d.keys() for key in v.other_keys]):
124+
v.is_required = False
85125

86126
validation_spec = dict((k, v) for k, v in validation_spec.items() if v.is_required)
87127
if validation_spec:
@@ -442,14 +482,17 @@ def new(self, **kwargs):
442482
Overnight = 'ON',
443483
)
444484
_validate(kwargs,
445-
OrderId = Required(), # XXX number
446-
ShippingMethod = Required(Enum('FirstClass', 'PriorityMail', 'TrackedDelivery', 'SecondDay', 'Overnight')),
447-
PackSlip = Optional(Image),
448-
Comments = Optional(Array(Comment)),
449-
OrderInfo = Required(OrderInfo),
450-
Cases = Required(Array(Case)),
485+
OrderId = Required(), # XXX number
486+
ShippingMethod = RequiredOnlyIfNot(['ShippingAccount', 'ShippingMethodId'], Enum('FirstClass', 'PriorityMail', 'TrackedDelivery', 'SecondDay', 'Overnight')),
487+
ShippingMethodId = RequiredOnlyIfNot(['ShippingMethod']),
488+
ShippingAccount = RequiredOnlyIfNot(['ShippingMethod']),
489+
PackSlip = Optional(Image),
490+
Comments = Optional(Array(Comment)),
491+
OrderInfo = Required(OrderInfo),
492+
Cases = Required(Array(Case)),
451493
)
452-
kwargs['ShippingMethod'] = shipping_method_map[ kwargs['ShippingMethod'] ]
494+
if "ShippingMethod" in kwargs:
495+
kwargs['ShippingMethod'] = shipping_method_map[ kwargs['ShippingMethod'] ]
453496
# XXX OrderDate (date or datetime?)
454497

455498
request = self._generate_request(

0 commit comments

Comments
 (0)