Skip to content

Commit fb10a35

Browse files
tox fixes for advanced filters
1 parent fc787a0 commit fb10a35

File tree

3 files changed

+68
-21
lines changed

3 files changed

+68
-21
lines changed

SoftLayer/CLI/call_api.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"""Call arbitrary API endpoints."""
2-
import click
32
import json
43

4+
import click
5+
56
from SoftLayer.CLI import environment
67
from SoftLayer.CLI import exceptions
78
from SoftLayer.CLI import formatting
@@ -28,7 +29,7 @@ def _build_filters(_filters):
2829
if len(top_parts) == 2:
2930
break
3031
else:
31-
raise exceptions.CLIAbort('Failed to find valid operation for: %s' % _filter)
32+
raise exceptions.CLIAbort('Failed to find valid operation for: %s' % _filter)
3233

3334
key, value = top_parts
3435
current = root
@@ -67,37 +68,40 @@ def _build_python_example(args, kwargs):
6768

6869
return call_str
6970

70-
def _validate_filter(ctx, param, value):
71+
72+
def _validate_filter(ctx, param, value): # pylint: disable=unused-argument
7173
"""Validates a JSON style object filter"""
7274
_filter = None
73-
75+
# print("VALUE: {}".format(value))
7476
if value:
7577
try:
7678
_filter = json.loads(value)
7779
if not isinstance(_filter, dict):
7880
raise exceptions.CLIAbort("\"{}\" should be a JSON object, but is a {} instead.".
7981
format(_filter, type(_filter)))
80-
except json.JSONDecodeError as e:
81-
raise exceptions.CLIAbort("\"{}\" is not valid JSON. {}".format(value, e))
82+
except json.JSONDecodeError as error:
83+
raise exceptions.CLIAbort("\"{}\" is not valid JSON. {}".format(value, error))
8284

8385
return _filter
8486

85-
def _validate_parameters(ctx, param, value):
87+
88+
def _validate_parameters(ctx, param, value): # pylint: disable=unused-argument
8689
"""Checks if value is a JSON string, and converts it to a datastructure if that is true"""
8790

8891
validated_values = []
89-
for i, parameter in enumerate(value):
92+
for parameter in value:
9093
if isinstance(parameter, str):
9194
# looks like a JSON string...
9295
if '{' in parameter or '[' in parameter:
9396
try:
9497
parameter = json.loads(parameter)
95-
except json.JSONDecodeError as e:
96-
click.secho("{} looked like json, but wasn't valid, passing to API as is. {}".format(parameter, e),
97-
fg='red')
98+
except json.JSONDecodeError as error:
99+
click.secho("{} looked like json, but was invalid, passing to API as is. {}".
100+
format(parameter, error), fg='red')
98101
validated_values.append(parameter)
99102
return validated_values
100103

104+
101105
@click.command('call', short_help="Call arbitrary API endpoints.")
102106
@click.argument('service')
103107
@click.argument('method')
@@ -138,6 +142,7 @@ def cli(env, service, method, parameters, _id, _filters, mask, limit, offset,
138142
slcli -v call-api SoftLayer_User_Customer addBulkPortalPermission --id=1234567 \\
139143
'[{"keyName": "NETWORK_MESSAGE_DELIVERY_MANAGE"}]'
140144
"""
145+
141146
if _filters and json_filter:
142147
raise exceptions.CLIAbort("--filter and --json-filter cannot be used together.")
143148

docs/cli/commands.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
Call API
44
========
55

6+
This function allows you to easily call any API. The format is
7+
8+
`slcli call-api SoftLayer_Service method param1 param2 --id=1234 --mask="mask[id,name]"`
9+
10+
Parameters should be in the order they are presented on sldn.softlayer.com.
11+
Any complex parameters (those that link to other datatypes) should be presented as JSON strings. They need to be enclosed in single quotes (`'`), and variables and strings enclosed in double quotes (`"`).
12+
13+
For example: `{"hostname":"test",ssh_keys:[{"id":1234}]}`
614

715
.. click:: SoftLayer.CLI.call_api:cli
816
:prog: call-api

tests/CLI/modules/call_api_tests.py

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,7 @@ def test_object_table(self):
158158
'None': None,
159159
'Bool': True}
160160

161-
result = self.run_command(['call-api', 'Service', 'method'],
162-
fmt='table')
161+
result = self.run_command(['call-api', 'Service', 'method'], fmt='table')
163162

164163
self.assert_no_fail(result)
165164
# NOTE(kmcdonald): Order is not guaranteed
@@ -179,8 +178,7 @@ def test_object_nested(self):
179178
result = self.run_command(['call-api', 'Service', 'method'])
180179

181180
self.assert_no_fail(result)
182-
self.assertEqual(json.loads(result.output),
183-
{'this': {'is': [{'pretty': 'nested'}]}})
181+
self.assertEqual(json.loads(result.output), {'this': {'is': [{'pretty': 'nested'}]}})
184182

185183
def test_list(self):
186184
mock = self.set_mock('SoftLayer_Service', 'method')
@@ -208,8 +206,7 @@ def test_list_table(self):
208206
'None': None,
209207
'Bool': True}]
210208

211-
result = self.run_command(['call-api', 'Service', 'method'],
212-
fmt='table')
209+
result = self.run_command(['call-api', 'Service', 'method'], fmt='table')
213210

214211
self.assert_no_fail(result)
215212
self.assertEqual(result.output,
@@ -224,12 +221,10 @@ def test_parameters(self):
224221
mock = self.set_mock('SoftLayer_Service', 'method')
225222
mock.return_value = {}
226223

227-
result = self.run_command(['call-api', 'Service', 'method',
228-
'arg1', '1234'])
224+
result = self.run_command(['call-api', 'Service', 'method', 'arg1', '1234'])
229225

230226
self.assert_no_fail(result)
231-
self.assert_called_with('SoftLayer_Service', 'method',
232-
args=('arg1', '1234'))
227+
self.assert_called_with('SoftLayer_Service', 'method', args=('arg1', '1234'))
233228

234229
def test_fixture_not_implemented(self):
235230
service = 'SoftLayer_Test'
@@ -264,3 +259,42 @@ def test_fixture_exception(self):
264259
self.assertIsInstance(result.exception, SoftLayerAPIError)
265260
output = '%s::%s fixture is not implemented' % (call_service, call_method)
266261
self.assertIn(output, result.exception.faultString)
262+
263+
def test_json_filter_validation(self):
264+
json_filter = '{"test":"something"}'
265+
result = call_api._validate_filter(None, None, json_filter)
266+
self.assertEqual(result['test'], 'something')
267+
268+
# Valid JSON, but we expect objects, not simple types
269+
with pytest.raises(exceptions.CLIAbort):
270+
call_api._validate_filter(None, None, '"test"')
271+
272+
# Invalid JSON
273+
with pytest.raises(exceptions.CLIAbort):
274+
call_api._validate_filter(None, None, 'test')
275+
276+
# Empty Request
277+
result = call_api._validate_filter(None, None, None)
278+
self.assertEqual(None, result)
279+
280+
def test_json_parameters_validation(self):
281+
json_params = ('{"test":"something"}', 'String', 1234, '[{"a":"b"}]', '{funky non [ Json')
282+
result = call_api._validate_parameters(None, None, json_params)
283+
self.assertEqual(result[0], {"test": "something"})
284+
self.assertEqual(result[1], "String")
285+
self.assertEqual(result[2], 1234)
286+
self.assertEqual(result[3], [{"a": "b"}])
287+
self.assertEqual(result[4], "{funky non [ Json")
288+
289+
def test_filter_with_filter(self):
290+
result = self.run_command(['call-api', 'Account', 'getObject', '--filter=nested.property=5432',
291+
'--json-filter={"test":"something"}'])
292+
self.assertEqual(2, result.exit_code)
293+
self.assertEqual(result.exception.message, "--filter and --json-filter cannot be used together.")
294+
self.assertIsInstance(result.exception, exceptions.CLIAbort)
295+
296+
def test_json_filter(self):
297+
pass
298+
result = self.run_command(['call-api', 'Account', 'getObject', '--json-filter={"test":"something"}'])
299+
self.assert_no_fail(result)
300+
self.assert_called_with('SoftLayer_Account', 'getObject', filter={"test": "something"})

0 commit comments

Comments
 (0)