diff options
-rw-r--r-- | ick2/__init__.py | 1 | ||||
-rw-r--r-- | ick2/apibase.py | 2 | ||||
-rw-r--r-- | ick2/persistent.py | 61 | ||||
-rw-r--r-- | ick2/persistent_tests.py | 9 | ||||
-rw-r--r-- | ick2/projectapi_tests.py | 9 | ||||
-rw-r--r-- | ick2/trans.py | 98 | ||||
-rw-r--r-- | ick2/workapi_tests.py | 9 |
7 files changed, 110 insertions, 79 deletions
diff --git a/ick2/__init__.py b/ick2/__init__.py index 7229a5f..e21f666 100644 --- a/ick2/__init__.py +++ b/ick2/__init__.py @@ -20,6 +20,7 @@ from .store import ( Conflict, ) from .persistent import ( + MemoryPersistentState, NotFound, Resource, resource_from_dict, diff --git a/ick2/apibase.py b/ick2/apibase.py index 3827f6e..19f70e4 100644 --- a/ick2/apibase.py +++ b/ick2/apibase.py @@ -23,7 +23,7 @@ class APIbase: def __init__(self, state): assert (state is None or - isinstance(state, ick2.MemoryStore)) + isinstance(state, ick2.MemoryPersistentState)) self._trans = ick2.TransactionalState(state) def get_routes(self, path): diff --git a/ick2/persistent.py b/ick2/persistent.py index 33903fe..e225b88 100644 --- a/ick2/persistent.py +++ b/ick2/persistent.py @@ -25,7 +25,66 @@ import yaml import ick2 -class NotFound(Exception): # pragma: no cover +class PersistentStateInterface: # pragma: no cover + + def get_resource_names(self, token, kind): + raise NotImplementedError() + + def has_resource(self, token, kind, name): + raise NotImplementedError() + + def get_resource(self, token, kind, name): + raise NotImplementedError() + + def get_resources(self, token, kind): + return [ + self.get_resource(token, kind, name) + for name in self.get_resource_names(token, kind) + ] + + def write_resource(self, token, kind, name, resource): + raise NotImplementedError() + + def update_resource(self, token, kind, name, resource): + raise NotImplementedError() + + def remove_resource(self, token, kind, name): + raise NotImplementedError() + + +class MemoryPersistentState(PersistentStateInterface): + + def __init__(self): + self._res = {} + + def get_resource_names(self, token, kind): + if kind not in self._res: + return [] + return list(self._res[kind].keys()) + + def has_resource(self, token, kind, name): + return kind in self._res and name in self._res[kind] + + def get_resource(self, token, kind, name): + if kind not in self._res or name not in self._res[kind]: + raise ick2.NotFound(kind=kind, name=name) + return self._res[kind][name] + + def write_resource(self, token, kind, name, resource): + if kind not in self._res: + self._res[kind] = {} + self._res[kind][name] = resource + + def update_resource(self, token, kind, name, resource): + self.write_resource(token, kind, name, resource) + + def remove_resource(self, token, kind, name): + if kind not in self._res or name not in self._res[kind]: + raise ick2.NotFound(kind=kind, name=name) + del self._res[kind][name] + + +class NotFound(Exception): def __init__(self, kind, name): super().__init__( diff --git a/ick2/persistent_tests.py b/ick2/persistent_tests.py index 961742f..18b5c5c 100644 --- a/ick2/persistent_tests.py +++ b/ick2/persistent_tests.py @@ -90,3 +90,12 @@ class PersistentStateTestsMixIn: self.state.write_resource(token, 'silly', '#1', r) with self.assertRaises(ick2.NotFound): self.state.remove_resource(token, 'silly', '#2') + + +class MemoryPersistentStateTests(unittest.TestCase, PersistentStateTestsMixIn): + + def get_token(self): + return 'DUMMY-TOKEN' + + def setUp(self): + self.state = ick2.MemoryPersistentState() diff --git a/ick2/projectapi_tests.py b/ick2/projectapi_tests.py index f8b3c2a..5be2a58 100644 --- a/ick2/projectapi_tests.py +++ b/ick2/projectapi_tests.py @@ -14,6 +14,8 @@ import os +import shutil +import tempfile import unittest @@ -23,7 +25,12 @@ import ick2 class ProjectAPITests(unittest.TestCase): def setUp(self): - self.state = ick2.MemoryStore() + self.tempdir = tempfile.mkdtemp() + self.statedir = os.path.join(self.tempdir, 'state/dir') + self.state = ick2.MemoryPersistentState() + + def tearDown(self): + shutil.rmtree(self.tempdir) def create_api(self): return ick2.ProjectAPI(self.state) diff --git a/ick2/trans.py b/ick2/trans.py index da2f80d..119f277 100644 --- a/ick2/trans.py +++ b/ick2/trans.py @@ -14,75 +14,22 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. -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): + def __init__(self, token, state, kind, name): self.token = token - self.store = store + self.state = state 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) + if state.has_resource(self.token, kind, name): + self.new = False + self.resource = state.get_resource(self.token, kind, 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)) + self.new = True + self.resource = ick2.resource_from_dict({}) methods = [ 'as_dict', @@ -99,12 +46,12 @@ class TransactionalResource: 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) + if self.new: + self.state.write_resource( + self.token, self.kind, self.name, self.resource) else: - self.store.update(self.token, self.rid, obj, self.rev) + self.state.update_resource( + self.token, self.kind, self.name, self.resource) class TransactionalState: @@ -113,21 +60,22 @@ class TransactionalState: self.state = state def new(self, token, kind, name): - return TransactionalResource(True, token, self.state, kind, name) + if self.state.has_resource(token, kind, name): + raise ick2.ExistsAlaready(name) + return TransactionalResource(token, self.state, kind, name) def modify(self, token, kind, name): - return TransactionalResource(False, token, self.state, kind, name) + if not self.state.has_resource(token, kind, name): + raise ick2.NotFound(kind=kind, name=name) + return TransactionalResource(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) + return self.state.get_resource(token, kind, name) def get_resources(self, token, kind): - return [ - self.get_resource(token, kind, name) - for name in find_names_by_kind(self.state, token, kind) - ] + return self.state.get_resources(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) + if not self.state.has_resource(token, kind, name): + raise ick2.NotFound(kind=kind, name=name) + self.state.remove_resource(token, kind, name) diff --git a/ick2/workapi_tests.py b/ick2/workapi_tests.py index 72bcaf1..8652110 100644 --- a/ick2/workapi_tests.py +++ b/ick2/workapi_tests.py @@ -15,6 +15,8 @@ import copy import os +import shutil +import tempfile import unittest @@ -24,9 +26,14 @@ import ick2 class WorkAPITests(unittest.TestCase): def setUp(self): - self.state = ick2.MemoryStore() + self.tempdir = tempfile.mkdtemp() + self.statedir = os.path.join(self.tempdir, 'state/dir') + self.state = ick2.MemoryPersistentState() self.claims = None + def tearDown(self): + shutil.rmtree(self.tempdir) + def create_project_api(self): pipeline = { 'pipeline': 'build', |