Skip to content

Commit d8c8b2a

Browse files
authored
Merge pull request #39 from planetlabs/latest
expose latest file functionality in client
2 parents 7840761 + 3f87b8c commit d8c8b2a

File tree

5 files changed

+130
-31
lines changed

5 files changed

+130
-31
lines changed

datalake/archive.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,15 @@ def list(self, what, start=None, end=None, where=None, work_id=None):
117117
else:
118118
break
119119

120+
def latest(self, what, where, lookback=None):
121+
url = self.http_url + '/v0/archive/latest/{}/{}'.format(what, where)
122+
params = dict(
123+
lookback=lookback,
124+
)
125+
response = self._requests_get(url, params=params)
126+
self._check_http_response(response)
127+
return response.json()
128+
120129
@property
121130
def http_url(self):
122131
self._http_url = self._http_url or environ.get('DATALAKE_HTTP_URL')
@@ -125,7 +134,7 @@ def http_url(self):
125134
return self._http_url.rstrip('/')
126135

127136
def _check_http_response(self, response):
128-
if response.status_code == 400:
137+
if response.status_code in (400, 404):
129138
err = response.json()
130139
msg = '{} ({})'.format(err['message'], err['code'])
131140
raise DatalakeHttpError(msg)

datalake/scripts/cli.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,23 @@ def _cat(url):
297297
f = archive.fetch(url)
298298
out.write(f.read())
299299
out.close()
300+
301+
302+
@cli.command()
303+
@click.option('--lookback', type=int)
304+
@click.option('--format', type=click.Choice(_list_result_formats),
305+
default='url')
306+
@click.argument('what')
307+
@click.argument('where')
308+
def latest(**kwargs):
309+
_prepare_archive_or_fail()
310+
_latest(**kwargs)
311+
312+
313+
@clean_up_datalake_errors
314+
def _latest(**kwargs):
315+
format = kwargs.pop('format')
316+
what = kwargs.pop('what')
317+
where = kwargs.pop('where')
318+
result = archive.latest(what, where, **kwargs)
319+
_print_list_results([result], format)

test/conftest.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import os
2121
from click.testing import CliRunner
2222
import stat
23+
import responses
2324

2425
from datalake.scripts.cli import cli
2526
from datalake import Archive
@@ -103,3 +104,12 @@ def maker(metadata=random_metadata, content=b''):
103104
if crtime_available:
104105
s = os.stat(crtime)
105106
crtime_setuid = s.st_mode & stat.S_ISUID and s.st_uid == 0
107+
108+
109+
def prepare_response(response, status=200, url=None, **query_params):
110+
url = url or 'http://datalake.example.com/v0/archive/files/'
111+
if len(query_params):
112+
q = ['{}={}'.format(k, query_params[k]) for k in query_params.keys()]
113+
url = url + '?' + '&'.join(q)
114+
responses.add(responses.GET, url, json=response, status=status,
115+
match_querystring=True)

test/test_latest.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import pytest
2+
import responses
3+
from conftest import prepare_response
4+
from datalake import DatalakeHttpError
5+
6+
7+
@responses.activate
8+
def test_latest(archive, random_metadata):
9+
r = {
10+
'url': 's3://bucket/file',
11+
'metadata': random_metadata,
12+
}
13+
url = 'http://datalake.example.com/v0/archive/latest/{}/{}'
14+
url = url.format(random_metadata['what'], random_metadata['where'])
15+
prepare_response(r, url=url)
16+
l = archive.latest(random_metadata['what'], random_metadata['where'])
17+
assert l['url'] == 's3://bucket/file'
18+
assert l['metadata'] == random_metadata
19+
20+
21+
@responses.activate
22+
def test_latest_cli(cli_tester, random_metadata):
23+
r = {
24+
'url': 's3://bucket/file',
25+
'metadata': random_metadata,
26+
}
27+
url = 'http://datalake.example.com/v0/archive/latest/{}/{}'
28+
url = url.format(random_metadata['what'], random_metadata['where'])
29+
prepare_response(r, url=url)
30+
31+
cmd = 'latest {what} {where}'
32+
cmd = cmd.format(**random_metadata)
33+
output = cli_tester(cmd)
34+
assert output == 's3://bucket/file\n'
35+
36+
37+
@responses.activate
38+
def test_latest_with_lookback_cli(cli_tester, random_metadata):
39+
r = {
40+
'url': 's3://bucket/file',
41+
'metadata': random_metadata,
42+
}
43+
url = 'http://datalake.example.com/v0/archive/latest/{}/{}?lookback=42'
44+
url = url.format(random_metadata['what'], random_metadata['where'])
45+
prepare_response(r, url=url)
46+
47+
cmd = 'latest {what} {where} --lookback 42'
48+
cmd = cmd.format(**random_metadata)
49+
output = cli_tester(cmd)
50+
assert output == 's3://bucket/file\n'
51+
52+
53+
@responses.activate
54+
def test_no_such_latest(archive):
55+
r = {
56+
'message': 'not found',
57+
'code': 'NoSuchDatalakeFile',
58+
}
59+
url = 'http://datalake.example.com/v0/archive/latest/not/here'
60+
prepare_response(r, status=404, url=url)
61+
with pytest.raises(DatalakeHttpError):
62+
archive.latest('not', 'here')
63+
64+
65+
@responses.activate
66+
def test_latest_cli_bad_lookback(cli_tester, random_metadata):
67+
cmd = 'latest foo bar --lookback nine'
68+
cli_tester(cmd, expected_exit=2)

test/test_list.py

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,7 @@
66
from datetime import datetime, timedelta
77
from pytz import utc
88
import simplejson as json
9-
10-
11-
def _prepare_response(response, status=200, url=None, **query_params):
12-
url = url or 'http://datalake.example.com/v0/archive/files/'
13-
if len(query_params):
14-
q = ['{}={}'.format(k, query_params[k]) for k in query_params.keys()]
15-
url = url + '?' + '&'.join(q)
16-
responses.add(responses.GET, url, json=response, status=status,
17-
match_querystring=True)
9+
from conftest import prepare_response
1810

1911

2012
@responses.activate
@@ -28,9 +20,9 @@ def test_list_one_page(archive, random_metadata):
2820
],
2921
'next': None,
3022
}
31-
_prepare_response(r, what=random_metadata['what'],
32-
start=random_metadata['start'],
33-
end=random_metadata['end'])
23+
prepare_response(r, what=random_metadata['what'],
24+
start=random_metadata['start'],
25+
end=random_metadata['end'])
3426
l = list(archive.list(random_metadata['what'],
3527
start=random_metadata['start'],
3628
end=random_metadata['end']))
@@ -52,8 +44,8 @@ def test_list_two_pages(archive, random_metadata):
5244
],
5345
'next': 'http://the-next-url/',
5446
}
55-
_prepare_response(r1, what=random_metadata['what'], start=m1['start'],
56-
end=m1['end'])
47+
prepare_response(r1, what=random_metadata['what'], start=m1['start'],
48+
end=m1['end'])
5749

5850
m2 = copy(random_metadata)
5951
m2['id'] = '2'
@@ -66,7 +58,7 @@ def test_list_two_pages(archive, random_metadata):
6658
],
6759
'next': None,
6860
}
69-
_prepare_response(r2, url='http://the-next-url/')
61+
prepare_response(r2, url='http://the-next-url/')
7062
l = list(archive.list(m1['what'],
7163
start=random_metadata['start'],
7264
end=random_metadata['end']))
@@ -84,7 +76,7 @@ def test_bad_request(archive):
8476
"code": "NoWorkInterval",
8577
"message": "You must provide either work_id or start/end"
8678
}
87-
_prepare_response(r, status=400, what='syslog')
79+
prepare_response(r, status=400, what='syslog')
8880

8981
with pytest.raises(DatalakeHttpError):
9082
list(archive.list('syslog'))
@@ -94,7 +86,7 @@ def test_bad_request(archive):
9486
def test_internal_server_error(archive):
9587

9688
r = 'INTERNAL SERVER ERROR'
97-
_prepare_response(r, status=500, what='syslog')
89+
prepare_response(r, status=500, what='syslog')
9890

9991
with pytest.raises(DatalakeHttpError):
10092
list(archive.list('syslog'))
@@ -116,9 +108,9 @@ def tester(start, end):
116108
'next': None,
117109
}
118110

119-
_prepare_response(r, what=random_metadata['what'],
120-
start=random_metadata['start'],
121-
end=random_metadata['end'])
111+
prepare_response(r, what=random_metadata['what'],
112+
start=random_metadata['start'],
113+
end=random_metadata['end'])
122114
l = list(archive.list(random_metadata['what'], start=start, end=end))
123115
assert len(l) == 1
124116
assert l[0]['url'] == 's3://bucket/file'
@@ -152,10 +144,10 @@ def test_with_where(archive, random_metadata):
152144
],
153145
'next': None,
154146
}
155-
_prepare_response(r, what=random_metadata['what'],
156-
where=random_metadata['where'],
157-
start=random_metadata['start'],
158-
end=random_metadata['end'])
147+
prepare_response(r, what=random_metadata['what'],
148+
where=random_metadata['where'],
149+
start=random_metadata['start'],
150+
end=random_metadata['end'])
159151
l = list(archive.list(random_metadata['what'],
160152
where=random_metadata['where'],
161153
start=random_metadata['start'],
@@ -178,8 +170,8 @@ def test_with_work_id(archive, random_metadata):
178170
],
179171
'next': None,
180172
}
181-
_prepare_response(r, what=random_metadata['what'],
182-
work_id=random_metadata['work_id'])
173+
prepare_response(r, what=random_metadata['what'],
174+
work_id=random_metadata['work_id'])
183175
l = list(archive.list(random_metadata['what'],
184176
work_id='foo123'))
185177
assert len(l) == 1
@@ -202,9 +194,9 @@ def test_list_cli_url_format(cli_tester, random_metadata):
202194
],
203195
'next': None,
204196
}
205-
_prepare_response(r, what=random_metadata['what'],
206-
start=random_metadata['start'],
207-
end=random_metadata['end'])
197+
prepare_response(r, what=random_metadata['what'],
198+
start=random_metadata['start'],
199+
end=random_metadata['end'])
208200
cmd = 'list {what} --start={start} --end={end}'
209201
cmd = cmd.format(**random_metadata)
210202
output = cli_tester(cmd)
@@ -232,7 +224,7 @@ def test_list_cli_json_format(cli_tester, random_metadata):
232224
],
233225
'next': None,
234226
}
235-
_prepare_response(r, what=m1['what'], work_id=m1['work_id'])
227+
prepare_response(r, what=m1['what'], work_id=m1['work_id'])
236228
cmd = 'list {what} --work-id={work_id} --format=json'
237229
cmd = cmd.format(**m1)
238230
output_lines = cli_tester(cmd).rstrip('\n').split('\n')

0 commit comments

Comments
 (0)