Skip to content

Commit 1a2f1b1

Browse files
committed
WIP
1 parent 038f993 commit 1a2f1b1

File tree

12 files changed

+109
-310
lines changed

12 files changed

+109
-310
lines changed

reana_jupyterlab/client.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import os
2+
import requests
3+
4+
class ReanaAPIClient:
5+
def __init__(self):
6+
self.server_url = os.getenv('REANA_SERVER_URL', '')
7+
self.access_token = os.getenv('REANA_ACCESS_TOKEN', '')
8+
9+
def _get_headers(self):
10+
return {
11+
'Authorization': f'Bearer {self.access_token}'
12+
}
13+
14+
def get(self, endpoint, params=None):
15+
if params is None:
16+
params = {}
17+
18+
headers = self._get_headers()
19+
response = requests.get(
20+
f"{self.server_url}/api/{endpoint}",
21+
headers=headers,
22+
params=params
23+
)
24+
return response
25+
26+
def post(self, endpoint, json=None, params=None):
27+
if params is None:
28+
params = {}
29+
30+
headers = self._get_headers()
31+
response = requests.post(
32+
f"{self.server_url}/api/{endpoint}",
33+
headers=headers,
34+
json=json,
35+
params=params
36+
)
37+
return response
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from .handlers import *
1+
from .handlers import *

reana_jupyterlab/handlers/connection.py

Lines changed: 0 additions & 50 deletions
This file was deleted.
Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
1-
from jupyter_server.utils import url_path_join
21
from jupyter_server.serverapp import ServerApp
3-
from .connection import EnvVariablesHandler
2+
from jupyter_server.utils import url_path_join
43
from .files import FileBrowserHandler
4+
55
from .workflows import (
66
WorkflowsHandler,
7-
WorkflowLogsHandler,
8-
WorkflowWorkspaceHandler,
7+
WorkflowLogsHandler,
8+
WorkflowWorkspaceHandler,
99
WorkflowSpecificationHandler,
1010
WorkspaceFilesHandler,
1111
WorkflowCreateHandler,
12-
WorkflowValidateHandler,
12+
WorkflowValidateHandler
1313
)
1414

1515
def setup_handlers(server_app: ServerApp) -> None:
1616
web_app = server_app.web_app
1717
host_pattern = ".*$"
1818
base_url = url_path_join(web_app.settings["base_url"], "reana_jupyterlab")
1919
handlers = [
20-
(url_path_join(base_url, "env"), EnvVariablesHandler),
2120
(url_path_join(base_url, "workflows", "([^/]+)", "workspace", "([^/]+)"), WorkspaceFilesHandler),
2221
(url_path_join(base_url, "workflows", "([^/]+)", "workspace"), WorkflowWorkspaceHandler),
2322
(url_path_join(base_url, "workflows", "([^/]+)", "logs"), WorkflowLogsHandler),
@@ -27,4 +26,4 @@ def setup_handlers(server_app: ServerApp) -> None:
2726
(url_path_join(base_url, "validate"), WorkflowValidateHandler),
2827
(url_path_join(base_url, "files"), FileBrowserHandler),
2928
]
30-
web_app.add_handlers(host_pattern, handlers)
29+
web_app.add_handlers(host_pattern, handlers)

reana_jupyterlab/handlers/workflows.py

Lines changed: 46 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
import os
33
import json
44
import re
5-
import requests
65
import subprocess
76
from urllib.parse import quote_plus, urlencode
7+
from ..client import ReanaAPIClient
88

9-
# import ../const.py file
109
from ..const import (
1110
WORKFLOWS_PAGE_SIZE,
1211
WORKFLOWS_TYPE,
@@ -16,6 +15,10 @@
1615
endpoint = 'workflows'
1716

1817
class WorkflowsHandler(APIHandler):
18+
def __init__(self, *args, **kwargs):
19+
super().__init__(*args, **kwargs)
20+
self.client = ReanaAPIClient()
21+
1922
def _parse_workflow(self, workflow):
2023
parsed_workflow = {}
2124

@@ -31,7 +34,7 @@ def _parse_workflow(self, workflow):
3134
parsed_workflow['totalJobs'] = workflow.get('progress').get('total', {}).get('total', 0)
3235

3336
return parsed_workflow
34-
37+
3538
def _parse_workflows(self, workflows):
3639
parsed_workflows = []
3740

@@ -53,7 +56,6 @@ def _parse_params(self, params):
5356
if 'search' in params:
5457
params['search'] = json.dumps({'name': [params['search']]})
5558

56-
params['access_token'] = os.getenv('REANA_ACCESS_TOKEN', '')
5759
params['type'] = WORKFLOWS_TYPE
5860
params['size'] = WORKFLOWS_PAGE_SIZE
5961
params['include_progress'] = True
@@ -65,10 +67,9 @@ def _parse_params(self, params):
6567
def get(self):
6668
params = self.request.query_arguments
6769
string_params = self._parse_params(params)
68-
server_url = os.getenv('REANA_SERVER_URL', '')
6970

7071
try:
71-
response = requests.get(f"{server_url}/api/{endpoint}?{string_params}")
72+
response = self.client.get(endpoint, params=string_params)
7273
workflows = self._parse_workflows(response)
7374
self.finish(json.dumps(workflows))
7475
except Exception as e:
@@ -78,6 +79,10 @@ def get(self):
7879
}))
7980

8081
class WorkflowLogsHandler(APIHandler):
82+
def __init__(self, *args, **kwargs):
83+
super().__init__(*args, **kwargs)
84+
self.client = ReanaAPIClient()
85+
8186
def _parse_logs(self, workflow):
8287
wf = workflow.json()
8388
logs = json.loads(wf.get('logs', ''))
@@ -87,23 +92,23 @@ def _parse_logs(self, workflow):
8792
'jobLogs': logs['job_logs']
8893
}
8994
)
90-
91-
def get(self, workflow_id):
92-
server_url = os.getenv('REANA_SERVER_URL', '')
93-
access_token = os.getenv('REANA_ACCESS_TOKEN', '')
9495

96+
def get(self, workflow_id):
9597
try:
96-
response = requests.get(f"{server_url}/api/{endpoint}/{workflow_id}/logs?access_token={access_token}")
98+
response = self.client.get(f"{endpoint}/{workflow_id}/logs")
9799
logs = self._parse_logs(response)
98100
self.finish(logs)
99-
100101
except Exception as e:
101102
self.finish(json.dumps({
102103
'status': 'error',
103104
'message': str(e)
104105
}))
105106

106107
class WorkflowWorkspaceHandler(APIHandler):
108+
def __init__(self, *args, **kwargs):
109+
super().__init__(*args, **kwargs)
110+
self.client = ReanaAPIClient()
111+
107112
def _parse_files(self, files):
108113
parsed_files = []
109114

@@ -117,32 +122,30 @@ def _parse_files(self, files):
117122
)
118123

119124
return parsed_files
120-
125+
121126
def _parse_params(self, params):
122127
params = {key: params[key][0].decode('utf-8') for key in params if not key.isdigit()}
123128

124129
if 'search' in params:
125130
params['search'] = json.dumps({'name': [params['search']]})
126131

127-
params['access_token'] = os.getenv('REANA_ACCESS_TOKEN', '')
128132
params['size'] = WORKSPACE_PAGE_SIZE
129133

130134
string_params = urlencode(params, quote_via=quote_plus)
131135

132136
return string_params
133-
137+
134138
def get(self, workflow_id):
135139
params = self.request.query_arguments
136140
string_params = self._parse_params(params)
137-
server_url = os.getenv('REANA_SERVER_URL', '')
138141

139142
try:
140-
response = requests.get(f"{server_url}/api/{endpoint}/{workflow_id}/workspace?{string_params}")
143+
response = self.client.get(f"{endpoint}/{workflow_id}/workspace", params=string_params)
141144
data = response.json()
142145

143146
if response.status_code != 200:
144147
raise Exception(data.get('message', 'Error getting workspace files'))
145-
148+
146149
data['files'] = self._parse_files(data.pop('items'))
147150
self.finish(data)
148151
except Exception as e:
@@ -152,12 +155,13 @@ def get(self, workflow_id):
152155
}))
153156

154157
class WorkflowSpecificationHandler(APIHandler):
155-
def get(self, workflow_id):
156-
server_url = os.getenv('REANA_SERVER_URL', '')
157-
access_token = os.getenv('REANA_ACCESS_TOKEN', '')
158+
def __init__(self, *args, **kwargs):
159+
super().__init__(*args, **kwargs)
160+
self.client = ReanaAPIClient()
158161

162+
def get(self, workflow_id):
159163
try:
160-
response = requests.get(f"{server_url}/api/{endpoint}/{workflow_id}/specification?access_token={access_token}")
164+
response = self.client.get(f"{endpoint}/{workflow_id}/specification")
161165
self.finish(response.json())
162166
except Exception as e:
163167
self.finish(json.dumps({
@@ -166,17 +170,18 @@ def get(self, workflow_id):
166170
}))
167171

168172
class WorkspaceFilesHandler(APIHandler):
169-
def get(self, workflow_name, file_name):
170-
server_url = os.getenv('REANA_SERVER_URL', '')
171-
access_token = os.getenv('REANA_ACCESS_TOKEN', '')
173+
def __init__(self, *args, **kwargs):
174+
super().__init__(*args, **kwargs)
175+
self.client = ReanaAPIClient()
172176

177+
def get(self, workflow_name, file_name):
173178
try:
174179
file = quote_plus(file_name)
175-
response = requests.get(f"{server_url}/api/{endpoint}/{workflow_name}/workspace/{file}?access_token={access_token}")
180+
response = self.client.get(f"{endpoint}/{workflow_name}/workspace/{file}")
176181

177182
path = file_name.rsplit('/', 1)
178183
path = path[0] if len(path) > 1 else ''
179-
184+
180185
os.makedirs(workflow_name + '/' + path, exist_ok=True)
181186

182187
with open(workflow_name + '/' + file_name, 'wb') as f:
@@ -193,6 +198,10 @@ def get(self, workflow_name, file_name):
193198

194199

195200
class WorkflowCreateHandler(APIHandler):
201+
def __init__(self, *args, **kwargs):
202+
super().__init__(*args, **kwargs)
203+
self.client = ReanaAPIClient()
204+
196205
def post(self):
197206
try:
198207
body = json.loads(self.request.body)
@@ -205,16 +214,16 @@ def post(self):
205214

206215
if '..' in path or not os.path.isdir(workspace) or not yaml_file.endswith('.yaml'):
207216
raise Exception('Invalid path')
208-
217+
209218
# Check that the workflow name does not have characters that may cause issues
210219
if re.fullmatch(r'\w+', wf_name) is None:
211220
raise Exception('Invalid workflow name')
212-
221+
213222
result = subprocess.run(['reana-client', 'run', '-w', wf_name, '-f', yaml_file], cwd=workspace, capture_output=True)
214223

215224
if result.returncode != 0:
216225
raise Exception(result.stderr.decode('utf-8'))
217-
226+
218227
self.finish(json.dumps({
219228
'status': 'success',
220229
'message': 'Workflow created'
@@ -227,6 +236,10 @@ def post(self):
227236
}))
228237

229238
class WorkflowValidateHandler(APIHandler):
239+
def __init__(self, *args, **kwargs):
240+
super().__init__(*args, **kwargs)
241+
self.client = ReanaAPIClient()
242+
230243
def post(self):
231244
try:
232245
body = json.loads(self.request.body)
@@ -237,12 +250,12 @@ def post(self):
237250

238251
if '..' in path or not os.path.isdir(workspace) or not yaml_file.endswith('.yaml'):
239252
raise Exception('Invalid path')
240-
253+
241254
result = subprocess.run(['reana-client', 'validate', '-f', yaml_file], cwd=workspace, capture_output=True)
242255

243256
if result.returncode != 0:
244257
raise Exception(result.stderr.decode('utf-8'))
245-
258+
246259
self.finish(json.dumps({
247260
'status': 'success',
248261
'message': result.stdout.decode('utf-8')
@@ -252,4 +265,4 @@ def post(self):
252265
self.finish(json.dumps({
253266
'status': 'error',
254267
'message': str(e)
255-
}))
268+
}))

reana_jupyterlab/server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ def _load_jupyter_server_extension(lab_app): # pragma: no cover
1919
JupyterLab application instance
2020
"""
2121
setup_handlers(lab_app)
22-
lab_app.log.info("Registered Reana JupyterLab extension at URL path /reana_jupyterlab")
22+
lab_app.log.info("Registered Reana JupyterLab extension at URL path /reana_jupyterlab")

0 commit comments

Comments
 (0)