Gitlab Community Edition Instance

Commit 51eac033 authored by Marcel Hellkamp's avatar Marcel Hellkamp
Browse files

Changed CDStar.GET()/_rest() to CDStar.raw() and CDStar.rest().

parent e1274821
......@@ -34,25 +34,48 @@ class CDStar:
"""
return CDStar(self.url, auth=self.auth, _session=self._session)
def _rest(self, method, *path, _expect_status=None, headers=None, **options):
def raw(self, method, *path, expect_status=None, **options):
""" Send a raw HTTP request to the cdstar server.
Authentication and transaction headers are added automatically.
Error responses are thrown as `ApiError`, unless the status code is
explicitly accepted as valid via `expect_status`.
Disclaimer: Avoid using this method if there is a more specific implementation available. If you find a feature missing from this
class, please submit a feature request instead of over-using this
method.
"""
if self.auth:
options['auth'] = self.auth
headers = headers or {}
if self.tx:
headers['X-Transaction'] = self.tx['id']
options['headers'] = headers
options.setdefault("headers", {})['X-Transaction'] = self.tx['id']
path = [requests.utils.quote(p) for p in path]
rs = self._session.request(
method, self.url + '/'.join(path), **options)
if rs.ok or (_expect_status and rs.status_code in _expect_status):
if rs.ok or (expect_status and rs.status_code in expect_status):
return rs
# TODO: handle hard errors (JSONDecodeError)
raise ApiError(rs.json())
def rest(self, method, *path, expect_status=None, **options) -> dict:
""" Just like `raw()`, but expects the response to be JSON and returns
the parsed result instead of the raw response. Non-JSON responses
are errors.
Disclaimer: Avoid using this method if there is a more specific implementation available. If you find a feature missing from this
class, please submit a feature request instead of over-using this
method.
"""
# TODO: Expect json errors or non-json responses and
# throw a better error message
return self.raw(method, *path, expect_status=expect_status, **options).json()
def begin(self, autocommit=False, readonly=False):
""" Start a new transaction and return self.
......@@ -74,7 +97,7 @@ class CDStar:
"""
self.rollback()
self._autocommit = autocommit
self._tx = self._rest("POST", "_tx", data={"readonly": readonly}).json()
self._tx = self.rest("POST", "_tx", data={"readonly": readonly})
return self
@property
......@@ -88,7 +111,7 @@ class CDStar:
raise RuntimeError("No transaction running")
try:
self._rest("POST", "_tx", self._tx['id'])
self.rest("POST", "_tx", self._tx['id'])
self._tx = None
except Exception:
self.rollback()
......@@ -97,14 +120,14 @@ class CDStar:
def rollback(self) -> None:
""" Rollback the current transaction, if any. Do nothing otherwise. """
if self._tx:
self._rest("DELETE", "_tx", self._tx['id'])
self.rest("DELETE", "_tx", self._tx['id'])
self._tx = None
def keepalive(self):
""" If a transaction is running, keep it alive. Otherwise, do nothing. """
if not self._tx:
raise RuntimeError("No transaction running")
self._tx = self._rest("GET", "_tx", self._tx['id']).json()
self._tx = self.rest("GET", "_tx", self._tx['id'])
def __enter__(self):
""" Expect a transaction to be already running. """
......@@ -119,31 +142,22 @@ class CDStar:
else:
self.rollback()
def GET(self, *path, **kw):
"""
Public shortcut, e.g. to retrieve full archive metadata via
>>> api.GET(vault, archive, params=[('with', 'meta'), ('with', 'files')])
"""
kw.setdefault('_expect_status', [200])
return self._rest('GET', *path, **kw).json()
def service_info(self):
return self.GET()
return self.rest('GET')
def vault_info(self, vault: str):
return self.GET(vault)
return self.rest('GET', vault)
def create_archive(self, vault, form: "FormUpdate" = None):
if form:
return self._rest("POST", vault, data=form.body,
headers={'Content-Type': form.content_type}).json()
return self.rest("POST", vault, data=form.body,
headers={'Content-Type': form.content_type})
else:
return self._rest("POST", vault).json()
return self.rest("POST", vault)
def update_archive(self, vault, archive, form: "FormUpdate"):
return self._rest("POST", vault, archive, data=form.body,
headers={'Content-Type': form.content_type}).json()
return self.rest("POST", vault, archive, data=form.body,
headers={'Content-Type': form.content_type})
def list_files(self, vault, archive, offset=0, limit=100):
""" Request a FileList for an archive.
......@@ -153,7 +167,7 @@ class CDStar:
"""
query = {"files": "true", "offset": offset, "limit": limit}
return self._rest("GET", vault, archive, params=query).json()
return self.rest("GET", vault, archive, params=query)
def iter_files(self, vault, archive, offset=0, **args):
""" Yield all FileInfo entries of an archive.
......@@ -172,20 +186,23 @@ class CDStar:
def put_file(self, vault, archive, name, source, type=None):
if isinstance(source, PATH_TYPES):
raise ValueError("Source must be a file-like object, byte string or iterator yielding byte strings.")
return self._rest("PUT", vault, archive, _fix_filename(name), data=source,
headers={'Content-Type': type or "application/x-autodetect"}).json()
return self.rest("PUT", vault, archive, _fix_filename(name), data=source,
headers={'Content-Type': type or "application/x-autodetect"})
def get_file(self, vault, archive, name, offset=0):
""" Return a stream-able response object representing the requested file. """
# TODO: Return a wrapper class with convenience properties for size,
# save_as, __iter__
headers = {'Range': "bytes={}-".format(offset)} if offset > 0 else {}
return self._rest("GET", vault, archive, _fix_filename(name), stream=True, headers=headers)
return self.raw("GET", vault, archive, _fix_filename(name), stream=True, headers=headers)
def get_fileinfo(self, vault, archive, name, meta=True):
""" Get information about a file """
query = {"info": "true"}
if meta:
query['with'] = "meta"
return self._rest("GET", vault, archive, _fix_filename(name), params=query).json()
return self.rest("GET", vault, archive, _fix_filename(name), params=query)
def search(self, vault, q, order=None, limit=0, scroll=None, groups=None):
""" Perform a search and return the SearchResults document.
......@@ -200,7 +217,7 @@ class CDStar:
query['scroll'] = scroll
if groups:
query['groups'] = groups
return self._rest("GET", vault, query=query).json()
return self.rest("GET", vault, query=query)
def iter_search(self, vault, q, scroll=None, **args):
""" Yield all SearchHit entries of a search.
......
......@@ -129,7 +129,7 @@ def test_tx2(mockserver):
def test_error(mockserver):
with pytest.raises(ApiError) as e:
mockserver.GET('_raise')
mockserver.rest('GET', '_raise')
msg = e.value.pretty()
assert 'e (s)' in msg
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment