Skip to content

Commit

Permalink
Add put support
Browse files Browse the repository at this point in the history
  • Loading branch information
ruslux committed Jun 1, 2019
1 parent 14f507f commit 9c188e8
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 56 deletions.
35 changes: 28 additions & 7 deletions perestroika/db_layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,38 @@ def get(bundle, method):
bundle["queryset"] = bundle["queryset"].only(*_project)

bundle["items"] = bundle["queryset"].values()
bundle["total_count"] = bundle["queryset"].count()

if method.count_total:
bundle["total"] = bundle["queryset"].count()

@staticmethod
def post(bundle, method):
_objects = bundle.get("items")
items = bundle.get("items")

if not items:
raise BadRequest(message="Empty data for resource")

items = [bundle["queryset"].model(**data) for data in items]

bundle["queryset"].model.objects.bulk_create(items)
bundle["created"] = len(items)

if not _objects:
if method.count_total:
bundle["total"] = bundle["queryset"].count()

@staticmethod
def put(bundle, method):
items = bundle.get("items")

if not items:
raise BadRequest(message="Empty data for resource")

_objects = [bundle["queryset"].model(**data) for data in _objects]
updated = 0

for item in items:
updated += bundle["queryset"].update(**item)

bundle["updated"] = updated

bundle["queryset"].model.objects.bulk_create(
_objects
)
if method.count_total:
bundle["total"] = bundle["queryset"].count()
3 changes: 0 additions & 3 deletions perestroika/deserializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ def deserialize(self, request, method_handler, **kwargs):
"order": _data.get("order", {}),
"filter": _data.get("filter", {}),
"items": _items,
"limit": 20,
"skip": 0,
"total_count": 0,
"queryset": method_handler.queryset,
"project": _data.get("project", []),
"meta": _data.get("meta", {}),
Expand Down
36 changes: 13 additions & 23 deletions perestroika/methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@


class DenyAll:
def __init__(self, **kwargs) -> None:
def __call__(self, data) -> None:
raise TypeError("Deny all types")


class AllowAll:
def __call__(self, data) -> None:
return data


@attr.s(auto_attribs=True)
class Method:
mode: str = 'django'
Expand All @@ -29,6 +34,7 @@ class Method:
deserializer: Optional[Deserializer] = None

skip_query_db: bool = False
count_total: bool = False

input_validator: Callable = DenyAll
output_validator: Callable = DenyAll
Expand Down Expand Up @@ -69,8 +75,8 @@ def schema(self):
"output_schema": repr(self.output_validator)
}

def get_client_data(self, request, *args, **kwargs):
return self.deserializer.deserialize(request, self, *args, **kwargs)
def get_client_data(self, request, **kwargs):
return self.deserializer.deserialize(request, self, **kwargs)

def query_db(self, bundle):
raise NotImplementedError()
Expand Down Expand Up @@ -122,8 +128,8 @@ def apply_request_hooks(self, request, bundle):
def apply_response_hooks(self, request, bundle):
self.apply_hooks(self.response_hooks, request, bundle)

def handle(self, request, *args, **kwargs):
bundle = self.get_client_data(request, *args, **kwargs)
def handle(self, request, **kwargs):
bundle = self.get_client_data(request, **kwargs)

self.set_default_success_code(bundle)
self.apply_request_hooks(request, bundle)
Expand Down Expand Up @@ -199,28 +205,12 @@ def set_default_success_code(self, bundle):
@attr.s(auto_attribs=True)
class Put(CanFilterAndExclude):
def query_db(self, bundle):
raise NotImplementedError()

input_validator: Callable = DenyAll

def set_default_success_code(self, bundle):
raise NotImplementedError()

def schema(self):
_schema = super().schema()
_schema["input_schema"] = repr(self.input_validator)
return _schema
self.db_layer.put(bundle, self)


@attr.s(auto_attribs=True)
class Patch(CanFilterAndExclude):
input_validator: Callable = DenyAll

def query_db(self, bundle):
raise NotImplementedError()

def set_default_success_code(self, bundle):
raise NotImplementedError()
bundle["status_code"] = 200

def schema(self):
_schema = super().schema()
Expand Down
21 changes: 15 additions & 6 deletions perestroika/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,7 @@ def apply_project(self, bundle):
self.clean_item(item, project)

def get_data(self, bundle):
_data = {
"limit": bundle.get("limit", 0),
"skip": bundle.get("skip", 0),
"total_count": bundle.get("total_count", 0),
}
_data = {}

self.apply_project(bundle)

Expand All @@ -41,7 +37,20 @@ def get_data(self, bundle):
else:
_data["items"] = _items

for item in ['filter', 'order', 'project', 'error_code', 'error_message', 'status_code']:
for item in [
'filter',
'order',
'project',
'error_code',
'error_message',
'status_code',
'limit',
'skip',
'total',
'created',
'updated',
'deleted',
]:
if bundle.get(item) is not None:
_data[item] = bundle[item]

Expand Down
25 changes: 14 additions & 11 deletions tests/test_django/resources.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,39 @@
import attr
from django.contrib.auth.models import User

from perestroika.methods import Get, Post, Put, Patch, Delete
from perestroika.methods import Get, Post, Put, Delete, AllowAll
from perestroika.resource import DjangoResource


class EmptyResource(DjangoResource):
pass


def out_user_validator(item: dict):
return {"username": item["username"]}
class OutUser:
def __call__(self, item):
return {"username": item["username"]}


class FullResource(DjangoResource):
cache_control = dict(max_age=0, no_cache=True, no_store=True, must_revalidate=True)

get = Get(
queryset=User.objects.all(),
output_validator=out_user_validator
output_validator=OutUser(),
count_total=True
)

post = Post(
queryset=User.objects.all()
queryset=User.objects.all(),
input_validator=AllowAll(),
output_validator=OutUser(),
count_total=True
)

put = Put(
queryset=User.objects.all()
)

patch = Patch(
queryset=User.objects.all()
queryset=User.objects.all(),
input_validator=AllowAll(),
output_validator=OutUser(),
count_total=True
)

delete = Delete(
Expand Down
37 changes: 31 additions & 6 deletions tests/test_django/test_django_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ class DjangoTest(TestCase):
def make_post(self, url, data):
return self.client.post(url, dumps(data), content_type='application/json')

def make_put(self, url, data):
return self.client.put(url, dumps(data), content_type='application/json')

def make_empty_post(self, url):
return self.client.post(url, content_type='application/json')

Expand All @@ -33,13 +36,11 @@ def test_empty_get(self):
assert _response.status_code == 200
assert _response.json() == {
'item': {'username': "first"},
'limit': 20,
'skip': 0,
'project': [],
'order': {},
'filter': {},
'status_code': 200,
'total_count': 1
'total': 1
}

User(username="second").save()
Expand All @@ -50,19 +51,43 @@ def test_empty_get(self):
assert _response.status_code == 200
assert _response.json() == {
'items': [{'username': "first"}, {"username": "second"}],
'limit': 20,
'skip': 0,
'project': [],
'order': {},
'filter': {},
'status_code': 200,
'total_count': 2
'total': 2
}

def test_json_validation_no_items(self):
with self.assertRaises(BadRequest):
_response = self.make_empty_post("/test/full/")

def test_post(self):
assert User.objects.count() == 0

_response = self.make_post("/test/full/", {'item': {'username': "third"}})
assert _response.status_code == 201
assert _response.json() == {
'item': {"username": "third"},
'project': [],
'order': {},
'filter': {},
'status_code': 201,
'created': 1,
'total': 1
}

def test_put(self):
assert User.objects.count() == 0

_response = self.make_post("/test/full/", {'item': {'username': "third"}})
assert User.objects.count() == 1

_response = self.make_put("/test/full/", {'item': {'username': "fourth"}})
assert User.objects.count() == 1

assert User.objects.all().first().username == "fourth"

def test_admin(self):
_response = self.make_get("/admin/", {})
assert _response.status_code in [200, 302]

0 comments on commit 9c188e8

Please sign in to comment.