# Copyright (C) 2017-2018 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 ick2 class APIbase: def __init__(self, state): self._state = state def get_state(self): return self._state def get_routes(self, path): return [ { 'method': 'POST', 'path': path, 'callback': self.POST(self.create), }, { 'method': 'GET', 'path': path, 'callback': self.GET(self.list), }, { 'method': 'GET', 'path': '{}/'.format(path), 'callback': self.GET(self.show), }, { 'method': 'PUT', 'path': '{}/'.format(path), 'callback': self.PUT(self.update), }, { 'method': 'DELETE', 'path': '{}/'.format(path), 'callback': self.DELETE(self.delete), }, ] def GET(self, callback): def wrapper(content_type, body, **kwargs): ick2.log.log( 'trace', msg_text='GET called', kwargs=kwargs, content_type=content_type, body=body) try: if 'raw_uri_path' in kwargs: del kwargs['raw_uri_path'] body = callback(**kwargs) except ick2.NotFound as e: ick2.log.log( 'error', msg_text='GET Not found', kwargs=kwargs, exc_info=True) return ick2.not_found(e) if isinstance(body, dict): return ick2.OK(body) elif isinstance(body, str): return ick2.text_plain(body) raise Exception('this must not happen') return wrapper def POST(self, callback): def wrapper(content_type, body, **kwargs): ick2.log.log( 'trace', msg_text='POST called', kwargs=kwargs, content_type=content_type, body=body) try: body = callback(body, **kwargs) except ick2.ExistsAlready as e: ick2.log.log('error', msg_text=str(e), kwargs=kwargs) return ick2.conflict(str(e)) return ick2.created(body) return wrapper def PUT(self, callback): def wrapper(content_type, body, **kwargs): ick2.log.log( 'trace', msg_text='PUT called', kwargs=kwargs, content_type=content_type, body=body) if 'raw_uri_path' in kwargs: del kwargs['raw_uri_path'] try: body = callback(body, **kwargs) except ick2.NotFound as e: ick2.log.log( 'warning', msg_text='PUT Not found', kwargs=kwargs) return ick2.not_found(e) except ick2.WrongProjectStatus as e: ick2.log.log( 'error', msg_text='Wrong state for project', exception=str(e)) return ick2.bad_request(e) ick2.log.log('trace', msg_text='returned body', body=repr(body)) return ick2.OK(body) return wrapper def DELETE(self, callback): def wrapper(content_type, body, **kwargs): ick2.log.log( 'trace', msg_text='DELETE called', kwargs=kwargs, content_type=content_type, body=body) try: if 'raw_uri_path' in kwargs: del kwargs['raw_uri_path'] body = callback(**kwargs) except ick2.NotFound as e: ick2.log.log( 'warning', msg_text='DELETE Not found', kwargs=kwargs) return ick2.not_found(e) return ick2.OK(body) return wrapper def create(self, body, **kwargs): raise NotImplementedError() def update(self, body, name, **kwargs): raise NotImplementedError() def delete(self, name, **kwargs): raise NotImplementedError() def list(self, **kwargs): raise NotImplementedError() def show(self, name, **kwargs): raise NotImplementedError() class ResourceApiBase(APIbase): def __init__(self, type_name, state): super().__init__(state) self._type_name = type_name def list(self, **kwargs): return { self._type_name: self._state.get_resources(self._type_name), } def show(self, name, **kwargs): return self._state.get_resource(self._type_name, name) def create(self, body, **kwargs): resource = self.mangle_new_resource(body) name = self.get_resource_name(resource) try: self._state.get_resource(self._type_name, name) except ick2.NotFound: return self._state.add_resource(self._type_name, name, resource) else: raise ick2.ExistsAlready(name) def mangle_new_resource(self, resource): # pragma: no cover return resource def get_resource_name(self, resource): # pragma: no cover raise NotImplementedError() def update(self, body, name, **kwargs): name = self.get_resource_name(body) try: old = self._state.get_resource(self._type_name, name) except ick2.NotFound: raise else: resource = self.mangle_updated_resource(old, body) return self._state.update_resource(self._type_name, name, resource) def mangle_updated_resource(self, old, new): # pragma: no cover return new def delete(self, name, **kwargs): self._state.remove_resource(self._type_name, name)