# Copyright (C) 2019 Lars Wirzenius # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import copy import json import uuid import requests import ick2 class StoreInterface: # pragma: no cover def create(self, token, obj): raise NotImplementedError() def update(self, token, rid, obj, revision): raise NotImplementedError() def show(self, token, rid): raise NotImplementedError() def delete(self, token, rid): raise NotImplementedError() def search(self, token, cond): raise NotImplementedError() class MemoryStore(StoreInterface): def __init__(self): self._objs = {} def search(self, token, cond): return list(self._objs.keys()) def _new_id(self): return str(uuid.uuid4()) def _set(self, rid, rev, obj): self._objs[rid] = (rev, copy.deepcopy(obj)) def create(self, token, obj): rid = self._new_id() rev = self._new_id() self._set(rid, rev, obj) return rid, rev def show(self, token, rid): if rid not in self._objs: raise ick2.NotFound('unknown.type', rid) rev, obj = self._objs[rid] return copy.deepcopy(obj), rev def update(self, token, rid, obj, revision): old_obj, old_rev = self.show(token, rid) if old_rev != revision: raise ick2.Conflict(rid, old_rev, revision) new_rev = self._new_id() self._set(rid, new_rev, obj) return new_rev def delete(self, token, rid): del self._objs[rid] class MuckStore(StoreInterface): # pragma: no cover def __init__(self, muck_url): self._url = muck_url ick2.log.log('info', msg_text='MuckStore created', muck_url=muck_url) def _request(self, func, path, token, headers=None, body=None): url = '{}{}'.format(self._url, path) if headers is None: headers = {} headers['Authorization'] = 'Bearer {}'.format(token) r = func(url, headers=headers, data=body) ick2.log.log( 'trace', msg_text='Accessing Muck', func=repr(func), url=url, path=path, headers=headers, data=body, status=r.status_code, text=r.text) if not r.ok: raise ick2.StoreError(r.text) return r def search(self, token, cond): headers = { 'Content-Type': 'application/json', } cond = { 'cond': [ { 'where': 'meta', 'field': 'id', 'op': '>=', 'pattern': '', } ], } body = json.dumps(cond) r = self._request( requests.get, '/search', token, headers=headers, body=body) obj = r.json() return obj['resources'] def create(self, token, obj): headers = { 'Content-Type': 'application/json', } body = json.dumps(obj) r = self._request( requests.post, '/res', token, headers=headers, body=body) rid = r.headers['Muck-Id'] rev = r.headers['Muck-Revision'] return rid, rev def show(self, token, rid): headers = { 'Muck-Id': rid, } r = self._request(requests.get, '/res', token, headers=headers) rev = r.headers['Muck-Revision'] as_dict = r.json() return as_dict, rev def update(self, token, rid, obj, revision): headers = { 'Content-Type': 'application/json', 'Muck-Id': rid, 'Muck-Revision': revision, } body = json.dumps(obj) r = self._request( requests.put, '/res', token, headers=headers, body=body) rev = r.headers['Muck-Revision'] return rev def delete(self, token, rid): headers = { 'Muck-Id': rid, } self._request(requests.delete, '/res', token, headers=headers)