Skip to content

Commit ad10087

Browse files
committed
Add methods for the /api/v0/files/ endpoints
1 parent 81ce4d6 commit ad10087

1 file changed

Lines changed: 141 additions & 2 deletions

File tree

missioncontrol_client/__init__.py

Lines changed: 141 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,38 @@
11
import argparse
2-
import json
2+
import datetime
33
import os
4+
import socket
45
from urllib.parse import urljoin
6+
from uuid import uuid4
57

8+
import pytz
69
import requests
10+
from datalake import GzippingFile
11+
12+
13+
class UTC(object):
14+
# TODO factor this out into a proper library
15+
16+
def __init__(self, d="now"):
17+
if d == "now":
18+
self._d = datetime.datetime.utcnow()
19+
elif isinstance(d, datetime.datetime):
20+
if not d.tzinfo:
21+
# naive, assume UTC
22+
d = d.replace(tzinfo=pytz.utc)
23+
elif d.tzinfo == pytz.utc:
24+
pass
25+
else:
26+
d = d.astimezone(pytz.utc)
27+
self._d = d
28+
else:
29+
# TODO convert strings, etc
30+
raise NotImplementedError()
31+
32+
@property
33+
def iso(self):
34+
d = self._d.isoformat('T', 'microseconds')
35+
return d.replace("+00:00", "Z")
736

837

938
class MCAPI(object):
@@ -13,6 +42,17 @@ def __init__(self, mc_base, jwt=None):
1342
self.s = requests.session()
1443
self.jwt = None
1544

45+
@classmethod
46+
def from_environ(cls, ignore_ssl=False):
47+
mc_api = cls(os.environ['MC_BASE'])
48+
if ignore_ssl:
49+
mc_api.s.verify = False
50+
if os.environ.get('MC_JWT'):
51+
mc_api.login(jwt=os.environ['MC_JWT'])
52+
else:
53+
mc_api.login(username=os.environ['MC_USERNAME'], password=os.environ['MC_PASSWORD'])
54+
return mc_api
55+
1656
def get(self, path, *args, **kwargs):
1757
r = self.s.get(urljoin(self.mc_base, path), *args, **kwargs)
1858
r.raise_for_status()
@@ -22,6 +62,16 @@ def getj(self, path, *args, **kwargs):
2262
r = self.get(path, *args, **kwargs)
2363
return r.json()
2464

65+
def post(self, path, *args, **kwargs):
66+
r = self.s.post(urljoin(self.mc_base, path), *args, **kwargs)
67+
r.raise_for_status()
68+
return r
69+
70+
def postj(self, path, *args, **kwargs):
71+
ret = self.s.post(urljoin(self.mc_base, path), *args, **kwargs)
72+
ret.raise_for_status()
73+
return ret.json()
74+
2575
def put(self, path, *args, **kwargs):
2676
r = self.s.put(urljoin(self.mc_base, path), *args, **kwargs)
2777
r.raise_for_status()
@@ -145,6 +195,94 @@ def get_pass_task_stack(self, uuid, **kwargs):
145195
json=kwargs
146196
)
147197

198+
def get_latest_file(self, what, where, **kwargs):
199+
return self.getj(
200+
f'/api/v0/files/latest/{what}/{where}/',
201+
params=kwargs
202+
)
203+
204+
def get_files(self, what, **kwargs):
205+
kwargs.update({"what": what})
206+
return self.getj(
207+
f'/api/v0/files/search/',
208+
params=kwargs
209+
)
210+
211+
def get_files_by_cid(self, cid, **kwargs):
212+
return self.getj(
213+
f'/api/v0/files/cid/{cid}/',
214+
params=kwargs
215+
)
216+
217+
def get_files_by_work_id(self, work_id, **kwargs):
218+
return self.getj(
219+
f'/api/v0/files/work-id/{work_id}/',
220+
params=kwargs
221+
)
222+
223+
def get_file(self, uuid):
224+
return self.getj(
225+
f'/api/v0/files/{uuid}/'
226+
)
227+
228+
def download_file(self, uuid):
229+
return self.get(
230+
f'/api/v0/files/{uuid}/data/'
231+
)
232+
233+
def download_cid(self, cid):
234+
return self.get(
235+
f'/api/v0/raw-file/{cid}/data/'
236+
)
237+
238+
def upload_file(self, path, what, uuid=None, where=None, start=None,
239+
end=None, work_id=None, content_type=None):
240+
241+
if uuid is None:
242+
uuid = str(uuid4())
243+
244+
if start is None:
245+
start = UTC("now").iso
246+
else:
247+
start = UTC(start).iso
248+
249+
if where is None:
250+
where = socket.getfqdn()
251+
252+
f = GzippingFile.from_filename(
253+
path,
254+
what=what,
255+
where=where,
256+
start=start,
257+
work_id=work_id
258+
)
259+
260+
# get signed upload
261+
signed = self.postj(
262+
f'/api/v0/files/presign/',
263+
json=f.metadata
264+
)
265+
266+
file_tuple = ("file", f)
267+
if content_type is not None:
268+
file_tuple += (content_type,)
269+
270+
# upload file
271+
if "url" in signed:
272+
signed["fields"]["Content-Encoding"] = "gzip"
273+
resp = requests.post(
274+
signed["url"],
275+
data=signed["fields"],
276+
files=[file_tuple]
277+
)
278+
resp.raise_for_status()
279+
280+
# upload metadata
281+
return self.putj(
282+
f'/api/v0/files/{uuid}/',
283+
json=f.metadata
284+
)
285+
148286
def login(self, username=None, password=None, jwt=None):
149287
if username is not None and jwt is not None:
150288
raise ValueError("Can't give both a username and a jwt")
@@ -213,6 +351,7 @@ def handle_default_args(args):
213351
args.mc_api.login(username=args.username, password=args.password)
214352

215353
def from_environ(ignore_ssl=False):
354+
# deprecated, use MCAPI.from_environ()
216355
mc_api = MCAPI(os.environ['MC_BASE'])
217356
if ignore_ssl:
218357
mc_api.s.verify = False
@@ -221,4 +360,4 @@ def from_environ(ignore_ssl=False):
221360
else:
222361
mc_api.login(username=os.environ['MC_USERNAME'], password=os.environ['MC_PASSWORD'])
223362

224-
return mc_api
363+
return mc_api

0 commit comments

Comments
 (0)