Bug Fix: Properly verify and check required nested parameters

This commit is contained in:
Kyle Hornberg
2019-01-24 08:55:40 -06:00
parent a679d08585
commit c590f27169
3 changed files with 68 additions and 7 deletions
+19 -6
View File
@@ -24,22 +24,35 @@ class Base(object):
def _get_headers(self, method_headers):
return dict(ChainMap(method_headers, self.headers))
def _get_required_params(self, params, cached_kwargs):
all_required = [k for k, v in params.items() if v.get('required')]
required_params = []
for p in all_required:
param = p.replace('[]', '')
if utils.verify_path(cached_kwargs, param.split('.')):
required_params.append(param)
return required_params
def _validate(self, kwargs, params):
cached_kwargs = dict(ChainMap(kwargs, self._attribute_cache['url']))
required_params = [k for k, v in params.items() if v.get('required')]
required_params = self._get_required_params(params, cached_kwargs)
self._validate_required_params(required_params, cached_kwargs)
for kwarg, value in kwargs.items():
param_value = params.get(kwarg)
self._validate_params(param_value, kwarg, value, required_params)
def _raise_required_parameter(self, param):
message = '{} is a required parameter'.format(param)
raise errors.OctokitParameterError(message)
def _validate_required_params(self, required_params, cached_kwargs):
for p in required_params:
if '.' in p:
try:
utils.walk_path(cached_kwargs, p.split('.'))
continue
if p not in cached_kwargs: # has all required
message = '{} is a required parameter'.format(p)
raise errors.OctokitParameterError(message)
except KeyError:
self._raise_required_parameter(p)
except AssertionError:
self._raise_required_parameter(p)
def _validate_params(self, param_value, kwarg, value, required_params):
if not param_value: # is a valid param but not necessarily required
+11 -1
View File
@@ -19,6 +19,16 @@ def parameter_transform(params):
def walk_path(obj, path):
if len(path) == 1:
assert obj or obj[path[0]]
assert path[0] in obj
else:
walk_path(obj[path[0]], path[1:])
def verify_path(obj, path):
if not obj and len(path) == 1:
return True
if len(path) <= 2:
return path[0] in obj
if path[0] in obj:
verify_path(obj[path[0]], path[1:])
return False
+38
View File
@@ -66,6 +66,44 @@ class TestClientMethods(object):
Octokit().oauth_authorizations.create_authorization(**data)
assert 'None is not a valid parameter for gist_id' == str(e.value)
def test_must_include_required_dictionary_sub_parameters_when_used(self, mocker):
mocker.patch('requests.get')
data = {
'owner': 'owner',
'repo': 'repo',
'name': 'name',
'head_sha': 'master',
'output': {}
}
with pytest.raises(errors.OctokitParameterError) as e:
Octokit().checks.create(**data)
assert 'output.title is a required parameter' == str(e.value)
data.update({'output': {'title': 'blah'}})
with pytest.raises(errors.OctokitParameterError) as e:
Octokit().checks.create(**data)
assert 'output.summary is a required parameter' == str(e.value)
data.update({'output': {'title': 'blah', 'summary': 'that'}})
Octokit().checks.create(**data)
def test_must_include_required_list_sub_parameters_when_used(self, mocker):
mocker.patch('requests.get')
data = {
'owner': 'owner',
'repo': 'repo',
'name': 'name',
'head_sha': 'master',
'actions': []
}
with pytest.raises(errors.OctokitParameterError) as e:
Octokit().checks.create(**data)
assert 'actions.label is a required parameter' == str(e.value)
data.update({'actions': {'label': 'blah'}})
with pytest.raises(errors.OctokitParameterError) as e:
Octokit().checks.create(**data)
assert 'actions.description is a required parameter' == str(e.value)
data.update({'actions': {'label': 'blah', 'description': 'x', 'identifier': 'a'}})
Octokit().checks.create(**data)
def test_use_default_parameter_values(self, mocker):
mocker.patch('requests.get')
headers = {'Content-Type': 'application/json', 'accept': 'application/vnd.github.v3+json'}