# Copyright (C) 2018-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 ick2 def wrap(kind, name, as_dict): return { 'kind': kind, 'name': name, 'res': copy.deepcopy(as_dict), } def unwrap(obj): return obj['kind'], obj['name'], copy.deepcopy(obj['res']) def find_by_name(store, token, kind, name): rids = store.search(token, None) # FIXME for real searches for rid in rids: obj, rev = store.show(token, rid) if obj is not None: okind, oname, as_dict = unwrap(obj) if okind == kind and oname == name: return as_dict, rid, rev raise ick2.NotFound(kind=kind, name=name) def find_names_by_kind(store, token, kind): rids = store.search(token, None) # FIXME for real searches for rid in rids: obj, rev = store.show(token, rid) if obj is not None: okind, oname, as_dict = unwrap(obj) if okind == kind: yield oname class TransactionalResource: def __init__(self, new, token, store, kind, name): self.token = token self.store = store self.kind = kind self.name = name self.rid = None self.rev = None self.resource = None if new: try: find_by_name(self.store, token, kind, name) except ick2.NotFound: as_dict = {} else: raise ick2.ExistsAlready(name) else: try: as_dict, self.rid, self.rev = find_by_name( self.store, token, kind, name) except ick2.NotFound: raise self.resource = ick2.resource_from_dict(as_dict) assert ((new and self.rid is None) or (not new and self.rid is not None)) methods = [ 'as_dict', '__getitem__', '__setitem__', '__contains__', '__len__', ] for method in methods: setattr(self, method, getattr(self.resource, method)) def __enter__(self): return self.resource def __exit__(self, exc_type, value, traceback): if exc_type is None: as_dict = self.resource.as_dict() obj = wrap(self.kind, self.name, as_dict) if self.rid is None: self.store.create(self.token, obj) else: self.store.update(self.token, self.rid, obj, self.rev) class TransactionalState: def __init__(self, state): self.state = state def new(self, token, kind, name): return TransactionalResource(True, token, self.state, kind, name) def modify(self, token, kind, name): return TransactionalResource(False, token, self.state, kind, name) def get_resource(self, token, kind, name): as_dict, rid, rev = find_by_name(self.state, token, kind, name) return ick2.resource_from_dict(as_dict) def get_resources(self, token, kind): return [ self.get_resource(token, kind, name) for name in find_names_by_kind(self.state, token, kind) ] def remove_resource(self, token, kind, name): as_dict, rid, rev = find_by_name(self.state, token, kind, name) self.state.delete(token, rid)