Skip to content

Commit 1763fc2

Browse files
authored
Merge pull request #322 from splitio/custom-wrapper-validation
added validation and test
2 parents 9f2e0a4 + 246b209 commit 1763fc2

File tree

2 files changed

+137
-1
lines changed

2 files changed

+137
-1
lines changed

splitio/client/input_validator.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import logging
44
import re
55
import math
6+
import inspect
67

78
from splitio.api import APIException
89
from splitio.api.commons import FetchOptions
@@ -517,3 +518,51 @@ def valid_properties(properties):
517518
_LOGGER.warning('Event has more than 300 properties. Some of them will be trimmed' +
518519
' when processed')
519520
return True, valid_properties if len(valid_properties) else None, size
521+
522+
def validate_pluggable_adapter(config):
523+
"""
524+
Check if pluggable adapter contains the expected method signature
525+
526+
:param config: config parameters
527+
:type config: Dict
528+
529+
:return: True if no issue found otherwise False
530+
:rtype: bool
531+
"""
532+
if config.get('storageType') != 'PLUGGABLE':
533+
return True
534+
535+
if config.get('storageWrapper') is None:
536+
_LOGGER.error("Expecting custom storage `wrapper` in options, but no valid wrapper instance was provided.")
537+
return False
538+
539+
if config.get('storagePrefix') is not None:
540+
if not isinstance(config.get('storagePrefix'), str):
541+
_LOGGER.error("Custom storage prefix should be string type only")
542+
return False
543+
544+
pluggable_adapter = config.get('storageWrapper')
545+
if not isinstance(pluggable_adapter, object):
546+
_LOGGER.error("Custom storage instance is not inherted from object class")
547+
return False
548+
549+
expected_methods = {'get': 1, 'get_items': 1, 'get_many': 1, 'set': 2, 'push_items': 2,
550+
'delete': 1, 'increment': 2, 'decrement': 2, 'get_keys_by_prefix': 1,
551+
'get_many': 1, 'add_items' : 2, 'remove_items': 2, 'item_contains': 2,
552+
'get_items_count': 1, 'expire': 2}
553+
methods = inspect.getmembers(pluggable_adapter, predicate=inspect.ismethod)
554+
for exp_method in expected_methods:
555+
method_found = False
556+
get_method_args = set()
557+
for method in methods:
558+
if exp_method == method[0]:
559+
method_found = True
560+
get_method_args = inspect.signature(method[1]).parameters
561+
break
562+
if not method_found:
563+
_LOGGER.error("Pluggable adapter does not have required method: %s" % exp_method)
564+
return False
565+
if len(get_method_args) < expected_methods[exp_method]:
566+
_LOGGER.error("Pluggable adapter method %s has less than required arguments count: %s : " % (exp_method, len(get_method_args)))
567+
return False
568+
return True

tests/client/test_input_validator.py

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1177,4 +1177,91 @@ def test_input_validation_factory(self, mocker):
11771177
except:
11781178
pass
11791179
assert logger.error.mock_calls == []
1180-
f.destroy()
1180+
f.destroy()
1181+
1182+
class PluggableInputValidationTests(object): #pylint: disable=too-few-public-methods
1183+
"""Pluggable adapter instance validation test cases."""
1184+
1185+
class mock_adapter0():
1186+
def set(self, key, value):
1187+
print(key)
1188+
1189+
class mock_adapter1(object):
1190+
def set(self, key, value):
1191+
print(key)
1192+
1193+
class mock_adapter2(mock_adapter1):
1194+
def get(self, key):
1195+
print(key)
1196+
1197+
def get_items(self, key):
1198+
print(key)
1199+
1200+
def get_many(self, keys):
1201+
print(keys)
1202+
1203+
def push_items(self, key, *value):
1204+
print(key)
1205+
1206+
def delete(self, key):
1207+
print(key)
1208+
1209+
def increment(self, key, value):
1210+
print(key)
1211+
1212+
def decrement(self, key, value):
1213+
print(key)
1214+
1215+
def get_keys_by_prefix(self, prefix):
1216+
print(prefix)
1217+
1218+
def get_many(self, keys):
1219+
print(keys)
1220+
1221+
def add_items(self, key, added_items):
1222+
print(key)
1223+
1224+
def remove_items(self, key, removed_items):
1225+
print(key)
1226+
1227+
def item_contains(self, key, item):
1228+
print(key)
1229+
1230+
def get_items_count(self, key):
1231+
print(key)
1232+
1233+
class mock_adapter3(mock_adapter2):
1234+
def expire(self, key):
1235+
print(key)
1236+
1237+
class mock_adapter4(mock_adapter2):
1238+
def expire(self, key, value, till):
1239+
print(key)
1240+
1241+
def test_validate_pluggable_adapter(self):
1242+
# missing storageWrapper config parameter
1243+
assert(not input_validator.validate_pluggable_adapter({'storageType': 'PLUGGABLE'}))
1244+
1245+
# ignore if storage type is not pluggable
1246+
assert(input_validator.validate_pluggable_adapter({'storageType': 'memory'}))
1247+
1248+
# mock adapter is not derived from object class
1249+
assert(not input_validator.validate_pluggable_adapter({'storageType': 'PLUGGABLE', 'storageWrapper': self.mock_adapter0()}))
1250+
1251+
# mock adapter missing many functions
1252+
assert(not input_validator.validate_pluggable_adapter({'storageType': 'PLUGGABLE', 'storageWrapper': self.mock_adapter1()}))
1253+
1254+
# mock adapter missing expire function
1255+
assert(not input_validator.validate_pluggable_adapter({'storageType': 'PLUGGABLE', 'storageWrapper': self.mock_adapter2()}))
1256+
1257+
# mock adapter expire function has incrrect args count
1258+
assert(not input_validator.validate_pluggable_adapter({'storageType': 'PLUGGABLE', 'storageWrapper': self.mock_adapter3()}))
1259+
1260+
# expected mock adapter should pass
1261+
assert(input_validator.validate_pluggable_adapter({'storageType': 'PLUGGABLE', 'storageWrapper': self.mock_adapter4()}))
1262+
1263+
# using string type prefix should pass
1264+
assert(input_validator.validate_pluggable_adapter({'storageType': 'PLUGGABLE', 'storagePrefix': 'myprefix', 'storageWrapper': self.mock_adapter4()}))
1265+
1266+
# using non-string type prefix should not pass
1267+
assert(not input_validator.validate_pluggable_adapter({'storageType': 'PLUGGABLE', 'storagePrefix': 'myprefix', 123: self.mock_adapter4()}))

0 commit comments

Comments
 (0)