Source code for yarhp.dynamo

import logging

from pyramid.httpexceptions import (
   HTTPOk, HTTPCreated,
   HTTPBadRequest, HTTPNotFound, HTTPConflict,
)

from boto.dynamodb.exceptions import DynamoDBKeyNotFoundError
from dynamodb_mapper.model import OverwriteError

from yarhp.view import BaseView

log = logging.getLogger(__name__)


[docs]class DynamoDBView(BaseView): """Simple REST view used to serve dymamodb-mapper objects at the top level. For hash_key-indexed classes, subclass this and fill in the __model_class__ attribute with the class you want to serve. That's it! The default __model_class__ value is "your_project.model.resource_name.ResourceName" Example usage:: class UserView(DynamoDBView): #__model_class__ = 'your_project.model.user.User' pass """ _default_actions = frozenset('create update index show delete'.split()) def validation_schema(self): self.__validation_schema__ =\ {n: dict(type=v, required=False) for n,v in self.__model_class__.__schema__.items()} return self.__validation_schema__ def _get_or_404(self, id): """Load the item with hash_key == hash_key_type(id) if it exists in the DB. Otherwise, raise HTTP 404. """ # id is always a string -- convert it the hash_key's real type schema = self.__model_class__.__schema__ hash_key_name = self.__model_class__.__hash_key__ hash_key = schema[hash_key_name](id) try: return self.__model_class__.get(hash_key) except DynamoDBKeyNotFoundError: log.info( "Class=%s: id=%s not found", self.__model_class__, id) raise HTTPNotFound()
[docs] def default_create(self, **kw): """POST /items: create a new item.""" try: data = { key: self.request.params[key] for key in self.__model_class__.__schema__ } except KeyError as e: log.info( "Create class=%s: missing key=%s (params=%s)", self.__model_class__, e, self.request.params) raise HTTPBadRequest() item = self.__model_class__.from_dict(data) hash_key = getattr(item, item.__hash_key__) try: item.save(allow_overwrite=False) except OverwriteError: log.info( "Create class=%s: hash_key=%s already exists (params=%s).", self.__model_class__, hash_key, self.request.params) raise HTTPConflict() # Provide the newly-created resource's URI. new_uri = self.request.route_url( self.request.matched_route.name, hash_key) return HTTPCreated(headers={'Location': new_uri})
[docs] def default_update(self, **kw): """PUT /items/hash_key: update an existing item.""" # Check that the item exists, but since we'll overwrite it # entirely, we don't actually need it. id = kw['id'] self._get_or_404(id) hash_key_name = self.__model_class__.__hash_key__ if self.request.params.get(hash_key_name) not in [id, None]: log.info( "Update class=%s: can't change id=%s to id=%s (params=%s)", id, self.request.params[hash_key_name], self.request.params) raise HTTPBadRequest() try: data = { key: self.request.params[key] for key in self.__model_class__.__schema__ if key != hash_key_name } except KeyError as e: log.info( "Update class=%s: missing key=%s (params=%s)", self.__model_class__, e, self.request.params) raise HTTPBadRequest() data[hash_key_name] = id # Easier to recreate a new item entirely. self.__model_class__.from_dict(data).save() return HTTPOk()
[docs] def default_delete(self, **kw): """DELETE /items/hash_key: delete a single item.""" item = self._get_or_404(kw['id']) item.delete() return HTTPOk()
[docs] def default_index(self, **kw): """GET /items: list all items in the collection. Warning: Uses the scan operation. Please avoid calling this on large collections. """ return [i.to_json_dict() for i in self.__model_class__.scan()]
[docs] def default_show(self, **kw): """GET /items/hash_key: get a single item.""" item = self._get_or_404(kw['id']) return item.to_json_dict()