Skip to content

Commit 78f1d5a

Browse files
committed
Add refresh_credentials method to plugin
1 parent 9041dbd commit 78f1d5a

File tree

3 files changed

+218
-91
lines changed

3 files changed

+218
-91
lines changed

src/galaxy/api/jsonrpc.py

+109-55
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def __init__(self, data=None):
6464
super().__init__(0, "Unknown error", data)
6565

6666
Request = namedtuple("Request", ["method", "params", "id"], defaults=[{}, None])
67+
Response = namedtuple("Response", ["id", "result", "error"], defaults=[None, {}, {}])
6768
Method = namedtuple("Method", ["callback", "signature", "immediate", "sensitive_params"])
6869

6970

@@ -79,7 +80,7 @@ def anonymise_sensitive_params(params, sensitive_params):
7980

8081
return params
8182

82-
class Server():
83+
class Connection():
8384
def __init__(self, reader, writer, encoder=json.JSONEncoder()):
8485
self._active = True
8586
self._reader = StreamLineReader(reader)
@@ -89,6 +90,8 @@ def __init__(self, reader, writer, encoder=json.JSONEncoder()):
8990
self._notifications = {}
9091
self._task_manager = TaskManager("jsonrpc server")
9192
self._write_lock = asyncio.Lock()
93+
self._last_request_id = 0
94+
self._requests_futures = {}
9295

9396
def register_method(self, name, callback, immediate, sensitive_params=False):
9497
"""
@@ -114,6 +117,47 @@ def register_notification(self, name, callback, immediate, sensitive_params=Fals
114117
"""
115118
self._notifications[name] = Method(callback, inspect.signature(callback), immediate, sensitive_params)
116119

120+
async def send_request(self, method, params, sensitive_params):
121+
"""
122+
Send request
123+
124+
:param method:
125+
:param params:
126+
:param sensitive_params: list of parameters that are anonymized before logging; \
127+
if False - no params are considered sensitive, if True - all params are considered sensitive
128+
"""
129+
self._last_request_id += 1
130+
request_id = str(self._last_request_id)
131+
132+
loop = asyncio.get_running_loop()
133+
future = loop.create_future()
134+
self._requests_futures[self._last_request_id] = (future, sensitive_params)
135+
136+
logging.info(
137+
"Sending request: id=%s, method=%s, params=%s",
138+
request_id, method, anonymise_sensitive_params(params, sensitive_params)
139+
)
140+
141+
self._send_request(request_id, method, params)
142+
return await future
143+
144+
def send_notification(self, method, params, sensitive_params=False):
145+
"""
146+
Send notification
147+
148+
:param method:
149+
:param params:
150+
:param sensitive_params: list of parameters that are anonymized before logging; \
151+
if False - no params are considered sensitive, if True - all params are considered sensitive
152+
"""
153+
154+
logging.info(
155+
"Sending notification: method=%s, params=%s",
156+
method, anonymise_sensitive_params(params, sensitive_params)
157+
)
158+
159+
self._send_notification(method, params)
160+
117161
async def run(self):
118162
while self._active:
119163
try:
@@ -143,15 +187,40 @@ def _eof(self):
143187

144188
def _handle_input(self, data):
145189
try:
146-
request = self._parse_request(data)
190+
message = self._parse_message(data)
147191
except JsonRpcError as error:
148192
self._send_error(None, error)
149193
return
150194

151-
if request.id is not None:
152-
self._handle_request(request)
153-
else:
154-
self._handle_notification(request)
195+
if isinstance(message, Request):
196+
if message.id is not None:
197+
self._handle_request(message)
198+
else:
199+
self._handle_notification(message)
200+
elif isinstance(message, Response):
201+
self._handle_response(message)
202+
203+
def _handle_response(self, response):
204+
request_future = self._requests_futures.get(int(response.id))
205+
if request_future is None:
206+
response_type = "response" if response.result is not None else "error"
207+
logging.warning("Received %s for unknown request: %s", response_type, response.id)
208+
return
209+
210+
future, sensitive_params = request_future
211+
212+
if response.error:
213+
error = JsonRpcError(
214+
response.error.setdefault("code", 0),
215+
response.error.setdefault("message", ""),
216+
response.error.setdefault("data", None)
217+
)
218+
self._log_error(response, error, sensitive_params)
219+
future.set_exception(error)
220+
return
221+
222+
self._log_response(response, sensitive_params)
223+
future.set_result(response.result)
155224

156225
def _handle_notification(self, request):
157226
method = self._notifications.get(request.method)
@@ -211,13 +280,17 @@ async def handle():
211280
self._task_manager.create_task(handle(), request.method)
212281

213282
@staticmethod
214-
def _parse_request(data):
283+
def _parse_message(data):
215284
try:
216-
jsonrpc_request = json.loads(data, encoding="utf-8")
217-
if jsonrpc_request.get("jsonrpc") != "2.0":
285+
jsonrpc_message = json.loads(data, encoding="utf-8")
286+
if jsonrpc_message.get("jsonrpc") != "2.0":
218287
raise InvalidRequest()
219-
del jsonrpc_request["jsonrpc"]
220-
return Request(**jsonrpc_request)
288+
del jsonrpc_message["jsonrpc"]
289+
if "result" in jsonrpc_message.keys() or "error" in jsonrpc_message.keys():
290+
return Response(**jsonrpc_message)
291+
else:
292+
return Request(**jsonrpc_message)
293+
221294
except json.JSONDecodeError:
222295
raise ParseError()
223296
except TypeError:
@@ -254,58 +327,39 @@ def _send_error(self, request_id, error):
254327

255328
self._send(response)
256329

257-
@staticmethod
258-
def _log_request(request, sensitive_params):
259-
params = anonymise_sensitive_params(request.params, sensitive_params)
260-
if request.id is not None:
261-
logging.info("Handling request: id=%s, method=%s, params=%s", request.id, request.method, params)
262-
else:
263-
logging.info("Handling notification: method=%s, params=%s", request.method, params)
264-
265-
class NotificationClient():
266-
def __init__(self, writer, encoder=json.JSONEncoder()):
267-
self._writer = writer
268-
self._encoder = encoder
269-
self._methods = {}
270-
self._task_manager = TaskManager("notification client")
271-
self._write_lock = asyncio.Lock()
272-
273-
def notify(self, method, params, sensitive_params=False):
274-
"""
275-
Send notification
330+
def _send_request(self, request_id, method, params):
331+
request = {
332+
"jsonrpc": "2.0",
333+
"method": method,
334+
"id": request_id,
335+
"params": params
336+
}
337+
self._send(request)
276338

277-
:param method:
278-
:param params:
279-
:param sensitive_params: list of parameters that are anonymized before logging; \
280-
if False - no params are considered sensitive, if True - all params are considered sensitive
281-
"""
339+
def _send_notification(self, method, params):
282340
notification = {
283341
"jsonrpc": "2.0",
284342
"method": method,
285343
"params": params
286344
}
287-
self._log(method, params, sensitive_params)
288345
self._send(notification)
289346

290-
async def close(self):
291-
self._task_manager.cancel()
292-
await self._task_manager.wait()
293-
294-
def _send(self, data):
295-
async def send_task(data_):
296-
async with self._write_lock:
297-
self._writer.write(data_)
298-
await self._writer.drain()
347+
@staticmethod
348+
def _log_request(request, sensitive_params):
349+
params = anonymise_sensitive_params(request.params, sensitive_params)
350+
if request.id is not None:
351+
logging.info("Handling request: id=%s, method=%s, params=%s", request.id, request.method, params)
352+
else:
353+
logging.info("Handling notification: method=%s, params=%s", request.method, params)
299354

300-
try:
301-
line = self._encoder.encode(data)
302-
data = (line + "\n").encode("utf-8")
303-
logging.debug("Sending %d byte of data", len(data))
304-
self._task_manager.create_task(send_task(data), "send")
305-
except TypeError as error:
306-
logging.error("Failed to parse outgoing message: %s", str(error))
355+
@staticmethod
356+
def _log_response(response, sensitive_params):
357+
result = anonymise_sensitive_params(response.result, sensitive_params)
358+
logging.info("Handling response: id=%s, result=%s", response.id, result)
307359

308360
@staticmethod
309-
def _log(method, params, sensitive_params):
310-
params = anonymise_sensitive_params(params, sensitive_params)
311-
logging.info("Sending notification: method=%s, params=%s", method, params)
361+
def _log_error(response, error, sensitive_params):
362+
data = anonymise_sensitive_params(error.data, sensitive_params)
363+
logging.info("Handling error: id=%s, code=%s, description=%s, data=%s",
364+
response.id, error.code, error.message, data
365+
)

0 commit comments

Comments
 (0)