Skip to content

Commit cff9506

Browse files
committed
pass object instead of parameters
1 parent 4f5a888 commit cff9506

File tree

5 files changed

+120
-76
lines changed

5 files changed

+120
-76
lines changed

tools/src/main/python/opengrok_tools/utils/commandsequence.py

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,59 @@
3636
import re
3737

3838

39+
API_TIMEOUT_PROPERTY = "api_timeout"
40+
ASYNC_API_TIMEOUT_PROPERTY = "async_api_timeout"
41+
HEADERS_PROPERTY = "headers"
42+
METHOD_PROPERTY = "method"
43+
URI_PROPERTY = "uri"
44+
45+
3946
class CommandConfigurationException(Exception):
4047
pass
4148

4249

50+
def check_call_config(call):
51+
"""
52+
:param call: dictionary with API call configuration
53+
"""
54+
if not isinstance(call, dict):
55+
raise CommandConfigurationException("call value not a dictionary: {}".
56+
format(call))
57+
58+
uri = call.get(URI_PROPERTY)
59+
if not uri:
60+
raise CommandConfigurationException(f"no '{URI_PROPERTY}' key present in {call}")
61+
62+
method = call.get(METHOD_PROPERTY)
63+
if method and method.upper() not in ['GET', 'POST', 'PUT', 'DELETE']:
64+
raise CommandConfigurationException(f"invalid HTTP method: {method}")
65+
66+
headers = call.get(HEADERS_PROPERTY)
67+
if headers and not isinstance(headers, dict):
68+
raise CommandConfigurationException("headers must be a dictionary")
69+
70+
call_timeout = call.get(API_TIMEOUT_PROPERTY)
71+
if call_timeout:
72+
try:
73+
int(call_timeout)
74+
except ValueError as exc:
75+
raise CommandConfigurationException(f"{API_TIMEOUT_PROPERTY} not an integer", exc)
76+
77+
call_api_timeout = call.get(ASYNC_API_TIMEOUT_PROPERTY)
78+
if call_api_timeout:
79+
try:
80+
int(call_api_timeout)
81+
except ValueError as exc:
82+
raise CommandConfigurationException(f"{ASYNC_API_TIMEOUT_PROPERTY} not an integer", exc)
83+
84+
4385
def check_command_property(command):
4486
"""
4587
Check if the 'commands' parameter of CommandSequenceBase() has the right structure
4688
w.r.t. individual commands.
4789
:param command: command element
4890
"""
91+
4992
if not isinstance(command, dict):
5093
raise CommandConfigurationException("command '{}' is not a dictionary".format(command))
5194

@@ -57,9 +100,42 @@ def check_command_property(command):
57100
if command_value and not isinstance(command_value, list):
58101
raise CommandConfigurationException("command value not a list: {}".
59102
format(command_value))
60-
if call_value and not isinstance(call_value, dict):
61-
raise CommandConfigurationException("call value not a dictionary: {}".
62-
format(call_value))
103+
if call_value:
104+
check_call_config(call_value)
105+
106+
107+
class ApiCall:
108+
"""
109+
Container class to store properties of API call.
110+
"""
111+
def __init__(self, call_dict):
112+
"""
113+
Initialize the object from a dictionary.
114+
:param call_dict: dictionary
115+
"""
116+
if not isinstance(call_dict, dict):
117+
raise CommandConfigurationException(f"not a dictionary: {call_dict}")
118+
119+
self.uri = call_dict.get(URI_PROPERTY)
120+
self.method = call_dict.get(METHOD_PROPERTY)
121+
if not self.method:
122+
self.method = "GET"
123+
124+
self.data = call_dict.get("data")
125+
126+
self.headers = call_dict.get(HEADERS_PROPERTY)
127+
if not self.headers:
128+
self.headers = {}
129+
130+
self.api_timeout = None
131+
call_timeout = call_dict.get(API_TIMEOUT_PROPERTY)
132+
if call_timeout:
133+
self.api_timeout = call_timeout
134+
135+
self.async_api_timeout = None
136+
call_api_timeout = call_dict.get(ASYNC_API_TIMEOUT_PROPERTY)
137+
if call_api_timeout:
138+
self.async_api_timeout = call_api_timeout
63139

64140

65141
class CommandSequenceBase:
@@ -168,7 +244,7 @@ def run(self):
168244
for command in self.commands:
169245
if command.get(CALL_PROPERTY):
170246
try:
171-
call_rest_api(command.get(CALL_PROPERTY),
247+
call_rest_api(ApiCall(command.get(CALL_PROPERTY)),
172248
{PROJECT_SUBST: self.name,
173249
URL_SUBST: self.url},
174250
self.http_headers,
@@ -230,7 +306,7 @@ def run_cleanup(self):
230306
for cleanup_cmd in self.cleanup:
231307
if cleanup_cmd.get(CALL_PROPERTY):
232308
try:
233-
call_rest_api(cleanup_cmd.get(CALL_PROPERTY),
309+
call_rest_api(ApiCall(cleanup_cmd.get(CALL_PROPERTY)),
234310
{PROJECT_SUBST: self.name,
235311
URL_SUBST: self.url},
236312
self.http_headers,

tools/src/main/python/opengrok_tools/utils/mirror.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@
4141
from .opengrok import get_repos, get_repo_type, get_uri, delete_project_data
4242
from .hook import run_hook
4343
from .command import Command
44-
from .restful import call_rest_api, do_api_call, get_call_props
44+
from .commandsequence import ApiCall
45+
from .restful import call_rest_api, do_api_call
4546

4647
from ..scm.repofactory import get_repository
4748
from ..scm.repository import RepositoryException
@@ -351,27 +352,29 @@ def run_command(cmd, project_name):
351352

352353
def handle_disabled_project(config, project_name, disabled_msg, headers=None,
353354
timeout=None, api_timeout=None):
355+
354356
disabled_command = config.get(DISABLED_CMD_PROPERTY)
355357
if disabled_command:
356358
logger = logging.getLogger(__name__)
357359

358360
if disabled_command.get(CALL_PROPERTY):
359361
call = disabled_command.get(CALL_PROPERTY)
360-
uri, _, data, _ = get_call_props(call)
361-
text = None
362+
api_call = ApiCall(call)
362363

364+
text = None
365+
data = api_call.data
363366
if type(data) is dict:
364367
text = data.get("text")
365368

366369
# Is this perhaps OpenGrok API call to supply a Message for the UI ?
367370
# If so and there was a string supplied, append it to the message text.
368-
if text and uri.find("/api/v1/") > 0 and type(disabled_msg) is str:
371+
if text and api_call.uri.find("/api/v1/") > 0 and type(disabled_msg) is str:
369372
logger.debug("Appending text to message: {}".
370373
format(disabled_msg))
371-
data["text"] = text + ": " + disabled_msg
374+
api_call.data["text"] = text + ": " + disabled_msg
372375

373376
try:
374-
call_rest_api(call, {PROJECT_SUBST: project_name},
377+
call_rest_api(api_call, {PROJECT_SUBST: project_name},
375378
http_headers=headers, timeout=timeout, api_timeout=api_timeout)
376379
except RequestException as e:
377380
logger.error("API call failed for disabled command of "

tools/src/main/python/opengrok_tools/utils/restful.py

Lines changed: 12 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -143,49 +143,14 @@ def subst(src, substitutions):
143143
return src
144144

145145

146-
def get_call_props(call):
147-
"""
148-
Retrieve the basic properties of a call.
149-
:param call: dictionary
150-
:return: URI, HTTP method, data, headers
151-
"""
152-
153-
logger = logging.getLogger(__name__)
154-
155-
uri = call.get("uri")
156-
if not uri:
157-
raise Exception(f"no 'uri' key present in {call}")
158-
if not is_web_uri(uri):
159-
raise Exception(f"not a valid URI: {uri}")
160-
161-
method = call.get("method")
162-
if not method:
163-
logger.debug(f"no 'method' key in {call}, using GET")
164-
method = "GET"
165-
166-
data = call.get("data")
167-
168-
try:
169-
headers = call.get("headers")
170-
if headers and not isinstance(headers, dict):
171-
raise Exception("headers must be a dictionary")
172-
except IndexError:
173-
headers = {}
174-
175-
if headers is None:
176-
headers = {}
177-
178-
return uri, method, data, headers
179-
180-
181146
def call_rest_api(call, substitutions=None, http_headers=None, timeout=None, api_timeout=None):
182147
"""
183148
Make REST API call. Occurrence of the pattern in the URI
184149
(first part of the command) or data payload will be replaced by the name.
185150
186151
Default content type is application/json.
187152
188-
:param call: dictionary describing the properties of the API call
153+
:param call: ApiCall object
189154
:param substitutions: dictionary of pattern:value for command and/or
190155
data substitution
191156
:param http_headers: optional dictionary of HTTP headers to be appended
@@ -196,28 +161,31 @@ def call_rest_api(call, substitutions=None, http_headers=None, timeout=None, api
196161

197162
logger = logging.getLogger(__name__)
198163

199-
uri, verb, data, headers = get_call_props(call)
200-
201-
logger.debug(f"Headers from the call structure: {headers}")
164+
logger.debug(f"Headers from the ApiCall object: {call.headers}")
165+
headers = call.headers
202166
if http_headers:
203-
logger.debug("Updating HTTP headers for call {} with {}".
167+
logger.debug("Updating HTTP headers for API call {} with {}".
204168
format(call, http_headers))
205169
headers.update(http_headers)
206170

207171
logger.debug("Performing URI substitutions")
208-
uri = subst(uri, substitutions)
172+
uri = subst(call.uri, substitutions)
209173
logger.debug(f"URI after the substitutions: {uri}")
210174

211-
call_timeout = call.get("api_timeout")
175+
if not is_web_uri(uri):
176+
raise Exception(f"not a valid URI: {uri}")
177+
178+
call_timeout = call.api_timeout
212179
if call_timeout:
213180
logger.debug(f"Setting connect/read API timeout based on the call to {call_timeout}")
214181
timeout = call_timeout
215182

216-
call_api_timeout = call.get("async_api_timeout")
183+
call_api_timeout = call.async_api_timeout
217184
if call_api_timeout:
218185
logger.debug(f"Setting async API timeout based on the call to {call_api_timeout}")
219186
api_timeout = call_api_timeout
220187

188+
data = call.data
221189
if data:
222190
header_names = [x.lower() for x in headers.keys()]
223191
if CONTENT_TYPE.lower() not in header_names:
@@ -235,4 +203,4 @@ def call_rest_api(call, substitutions=None, http_headers=None, timeout=None, api
235203
data = subst(data, substitutions)
236204
logger.debug("entity data: {}".format(data))
237205

238-
return do_api_call(verb, uri, headers=headers, data=data, timeout=timeout, api_timeout=api_timeout)
206+
return do_api_call(call.method, uri, headers=headers, data=data, timeout=timeout, api_timeout=api_timeout)

tools/src/test/python/test_mirror.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ def mock_call_rest_api(command, b, http_headers=None, timeout=None, api_timeout=
230230
assert mirror_project(config, project_name, False, False,
231231
None, None) == CONTINUE_EXITVAL
232232
verify(opengrok_tools.utils.mirror). \
233-
call_rest_api(config.get(DISABLED_CMD_PROPERTY).get(CALL_PROPERTY),
233+
call_rest_api(ANY,
234234
{PROJECT_SUBST: project_name},
235235
http_headers=None, timeout=None, api_timeout=None)
236236

@@ -242,14 +242,10 @@ def test_disabled_command_api_text_append(monkeypatch):
242242

243243
text_to_append = "foo bar"
244244

245-
def mock_call_rest_api(command, b, http_headers=None, timeout=None, api_timeout=None):
246-
disabled_command = config.get(DISABLED_CMD_PROPERTY)
247-
assert disabled_command
248-
command_args = disabled_command.get(COMMAND_PROPERTY)
249-
assert command_args
250-
data = command_args[2]
245+
def mock_call_rest_api(call, b, http_headers=None, timeout=None, api_timeout=None):
246+
call_data = call.data
251247
assert data
252-
text = data.get("text")
248+
text = call_data.get("text")
253249
assert text
254250
assert text.find(text_to_append)
255251

@@ -265,9 +261,9 @@ def mock_call_rest_api(command, b, http_headers=None, timeout=None, api_timeout=
265261
'text': 'disabled project'}
266262
config = {
267263
DISABLED_CMD_PROPERTY: {
268-
COMMAND_PROPERTY: [
269-
"http://localhost:8080/source/api/v1/foo",
270-
"POST", data]
264+
CALL_PROPERTY: {
265+
"uri": "http://localhost:8080/source/api/v1/foo",
266+
"method": "POST", "data": data}
271267
},
272268
PROJECTS_PROPERTY: {
273269
project_name: {

tools/src/test/python/test_restful.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
from opengrok_tools.utils.restful import call_rest_api,\
3434
CONTENT_TYPE, APPLICATION_JSON, do_api_call
35+
from opengrok_tools.utils.commandsequence import ApiCall
3536

3637

3738
def test_replacement(monkeypatch):
@@ -64,7 +65,7 @@ def mock_do_api_call(verb, uri, **kwargs):
6465
with monkeypatch.context() as m:
6566
m.setattr("opengrok_tools.utils.restful.do_api_call",
6667
mock_do_api_call)
67-
assert call_rest_api(call, {pattern: value}). \
68+
assert call_rest_api(ApiCall(call), {pattern: value}). \
6869
status_code == okay_status
6970

7071

@@ -74,7 +75,7 @@ def test_unknown_method():
7475
pattern = "%FOO%"
7576
value = "BAR"
7677
with pytest.raises(Exception):
77-
call_rest_api(call, {pattern: value})
78+
call_rest_api(ApiCall(call), {pattern: value})
7879

7980

8081
def test_content_type(monkeypatch):
@@ -98,7 +99,7 @@ def mock_response(verb, uri, **kwargs):
9899
with monkeypatch.context() as m:
99100
m.setattr("opengrok_tools.utils.restful.do_api_call",
100101
mock_response)
101-
call_rest_api(call)
102+
call_rest_api(ApiCall(call))
102103

103104

104105
def test_headers_timeout(monkeypatch):
@@ -110,8 +111,8 @@ def test_headers_timeout(monkeypatch):
110111
headers = {'Tatsuo': 'Yasuko'}
111112
expected_timeout = 42
112113
expected_api_timeout = 24
113-
command = {"uri": "http://localhost:8080/source/api/v1/bar",
114-
"method": "GET", "data": "data", "headers": headers}
114+
call = {"uri": "http://localhost:8080/source/api/v1/bar",
115+
"method": "GET", "data": "data", "headers": headers}
115116
extra_headers = {'Mei': 'Totoro'}
116117

117118
def mock_do_api_call(verb, uri, **kwargs):
@@ -124,7 +125,7 @@ def mock_do_api_call(verb, uri, **kwargs):
124125
with monkeypatch.context() as m:
125126
m.setattr("opengrok_tools.utils.restful.do_api_call",
126127
mock_do_api_call)
127-
call_rest_api(command,
128+
call_rest_api(ApiCall(call),
128129
http_headers=extra_headers,
129130
timeout=expected_timeout,
130131
api_timeout=expected_api_timeout)
@@ -171,7 +172,7 @@ def mock_response(uri, headers, data, params, proxies, timeout):
171172
with monkeypatch.context() as m:
172173
m.setattr("requests.put", mock_response)
173174
with pytest.raises(HTTPError):
174-
call_rest_api({"uri": 'http://foo', "method": 'PUT', "data": 'data'})
175+
call_rest_api(ApiCall({"uri": 'http://foo', "method": 'PUT', "data": 'data'}))
175176

176177

177178
def test_invalid_command_none():
@@ -181,14 +182,14 @@ def test_invalid_command_none():
181182

182183
def test_invalid_command_uknown_key():
183184
with pytest.raises(Exception):
184-
call_rest_api({"foo": "bar"})
185+
call_rest_api(ApiCall({"foo": "bar"}))
185186

186187

187188
def test_invalid_command_list():
188189
with pytest.raises(Exception):
189-
call_rest_api(["foo", "bar"])
190+
call_rest_api(ApiCall(["foo", "bar"]))
190191

191192

192193
def test_invalid_command_bad_uri():
193194
with pytest.raises(Exception):
194-
call_rest_api({"uri": "foo", "method": "PUT", "data": "data", "headers": "headers"})
195+
call_rest_api(ApiCall({"uri": "foo", "method": "PUT", "data": "data", "headers": "headers"}))

0 commit comments

Comments
 (0)