# HG changeset patch # User durandn # Date 1462278748 -7200 # Node ID b9edc1c1538aaa9420d55ae066134078f56771ef # Parent 854a027c80ff9f46b03b2013f18aa5b676dc87b7 reworked views and serializers to pass and take into account the "validation timestamp" to the model + refactored models to have a content validation method + adapted tests diff -r 854a027c80ff -r b9edc1c1538a .settings/org.eclipse.core.resources.prefs --- a/.settings/org.eclipse.core.resources.prefs Wed Apr 27 16:36:30 2016 +0200 +++ b/.settings/org.eclipse.core.resources.prefs Tue May 03 14:32:28 2016 +0200 @@ -10,4 +10,7 @@ encoding//server/python/django2/renkanmanager/migrations/0001_initial.py=utf-8 encoding//server/python/django2/renkanmanager/migrations/0002_alter_models_and_populate_revisions.py=utf-8 encoding//server/python/django2/renkanmanager/migrations/0003_auto_20160105_0954.py=utf-8 +encoding//server/python/django2/renkanmanager/migrations/0004_foreign_key_fields_initial.py=utf-8 +encoding//server/python/django2/renkanmanager/migrations/0005_foreign_key_fields_datamigration.py=utf-8 +encoding//server/python/django2/renkanmanager/migrations/0006_foreign_key_fields_remove_guids_and_set_nonnullables.py=utf-8 encoding//server/python/django2/renkanmanager/utils.py=utf-8 diff -r 854a027c80ff -r b9edc1c1538a server/python/django2/renkanmanager/api/views.py --- a/server/python/django2/renkanmanager/api/views.py Wed Apr 27 16:36:30 2016 +0200 +++ b/server/python/django2/renkanmanager/api/views.py Tue May 03 14:32:28 2016 +0200 @@ -9,6 +9,7 @@ import logging import uuid +from django.db import transaction from django.core.urlresolvers import reverse from django.http import Http404 from django.http.response import HttpResponse, HttpResponseBadRequest @@ -34,7 +35,7 @@ if workspace_guid == '': renkans = self.queryset.all() else: - renkans = self.queryset.filter(workspace_guid=workspace_guid) + renkans = self.queryset.filter(workspace__workspace_guid=workspace_guid) serializer = RenkanSerializer(renkans, many=True) return Response(serializer.data, status=status.HTTP_200_OK, content_type='application/json') @@ -47,7 +48,7 @@ source_renkan=Renkan.objects.get(renkan_guid=source_renkan_guid) except Renkan.DoesNotExist: return Response({'detail': 'Source renkan '+source_renkan_guid+' does not exist'}, status=status.HTTP_404_NOT_FOUND) - source_revision_guid = source_renkan.current_revision_guid + source_revision_guid = source_renkan.current_revision.revision_guid if source_revision_guid is not None: try: source_revision=Revision.objects.get(revision_guid=source_revision_guid) @@ -62,10 +63,9 @@ except Workspace.DoesNotExist: return Response({'detail': 'Workspace '+workspace_guid+' does not exist'}, status=status.HTTP_404_NOT_FOUND) create_data["workspace_id"] = workspace_guid - serializer = RenkanSerializer(data=create_data) if serializer.is_valid(): - serializer.save(creator=request.user) + serializer.save(creator=request.user) return Response(serializer.data, status=status.HTTP_201_CREATED, content_type='application/json') return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @@ -81,7 +81,6 @@ return self.queryset.get(renkan_guid=renkan_guid) def dispatch(self, *args, **kwargs): - logger.debug("TEST 1 21 12 TEST") return super(RenkanDetail, self).dispatch(*args, **kwargs) def get(self, request, renkan_guid, format=None): @@ -89,10 +88,7 @@ renkan = self.get_object(renkan_guid=renkan_guid) except Renkan.DoesNotExist: return Response({'detail': 'Renkan project %r does not exist'.format(renkan_guid)}, status=status.HTTP_404_NOT_FOUND) - logger.debug("RENKAN GET %r : CHECKING OBJECT PERMISSION", renkan_guid) - logger.debug("RENKAN GET: permission? %r", request.user.has_perm("view_renkan", renkan)) self.check_object_permissions(request, renkan) - logger.debug("RENKAN GET: PERMISSION GRANTED") serializer = RenkanSerializer(renkan) if {'true': True, 'false': False, "0": False, "1": True}.get(request.GET.get("content_only", "false").lower()): return Response(json.loads(serializer.data["content"]), status=status.HTTP_200_OK, content_type='application/json') @@ -119,19 +115,13 @@ return Response(serializer.data, status=status.HTTP_200_OK, content_type='application/json') return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + @transaction.atomic def delete(self, request, renkan_guid, format=None): try: to_delete_renkan = self.get_object(renkan_guid=renkan_guid) except Renkan.DoesNotExist: return Response({'detail': 'Renkan project '+renkan_guid+' does not exist'}, status=status.HTTP_404_NOT_FOUND) self.check_object_permissions(request, to_delete_renkan) - renkan_revisions = Revision.objects.filter(parent_renkan_guid = to_delete_renkan.renkan_guid) - for child_revision in renkan_revisions: - # Deleting reference to revision in renkans copied from this revision - for related_renkan in self.queryset.filter(source_revision_guid=child_revision.revision_guid): - related_renkan.source_revision_guid = '' - related_renkan.save() - child_revision.delete() to_delete_renkan.delete() return Response(status=status.HTTP_204_NO_CONTENT) @@ -204,7 +194,7 @@ def get_queryset(self, renkan_guid=""): if renkan_guid: - return Revision.objects.filter(parent_renkan_guid=renkan_guid) + return Revision.objects.filter(parent_renkan__renkan_guid=renkan_guid) else: return Revision.objects @@ -224,7 +214,7 @@ def get_queryset(self, renkan_guid=""): if renkan_guid: - return Revision.objects.filter(parent_renkan_guid=renkan_guid) + return Revision.objects.filter(parent_renkan__renkan_guid=renkan_guid) else: return Revision.objects @@ -253,10 +243,5 @@ return Response({'detail': 'You cannot delete the last remaining revision of a renkan from the Revision API. Try deleting the parent Renkan with the Renkan API'}, status=status.HTTP_400_BAD_REQUEST) if revision.is_current_revision: return Response({'detail': 'You cannot delete the current revision of a renkan from the Revision API.'}, status=status.HTTP_400_BAD_REQUEST) - - # Deleting reference to revision in renkans copied from this revision - for related_renkan in Renkan.objects.filter(source_revision_guid=revision_guid): - related_renkan.source_revision_guid = '' - related_renkan.save() revision.delete() return Response(status=status.HTTP_204_NO_CONTENT) diff -r 854a027c80ff -r b9edc1c1538a server/python/django2/renkanmanager/models.py --- a/server/python/django2/renkanmanager/models.py Wed Apr 27 16:36:30 2016 +0200 +++ b/server/python/django2/renkanmanager/models.py Tue May 03 14:32:28 2016 +0200 @@ -60,10 +60,7 @@ initial_revision.modification_date = initial_revision.creation_date initial_revision.title = new_renkan_title if new_renkan_title else "Untitled Renkan" if new_renkan_content: - try: - new_renkan_content_dict = json.loads(new_renkan_content) - except ValueError: - raise ValidationError("Provided content to create Renkan is not a JSON-serializable") + new_renkan_content_dict = json.loads(new_renkan.validate_json_content(new_renkan_content)) new_renkan_content_dict["created"] = str(initial_revision.creation_date) new_renkan_content_dict["updated"] = str(initial_revision.modification_date) else: @@ -82,8 +79,7 @@ initial_revision.content = json.dumps(new_renkan_content_dict) initial_revision.save() return new_renkan - - + class Renkan(models.Model): renkan_guid = models.CharField(max_length=256, default=uuid.uuid4, unique=True, blank=False, null=False) # typically UUID @@ -142,11 +138,8 @@ else: revision_to_update = Revision.objects.select_for_update().get(revision_guid=self.current_revision.revision_guid) - updated_content = content if content else current_revision.content - try: - updated_content_dict = json.loads(updated_content) - except ValueError: - raise ValidationError(message="Provided content for Renkan is not a JSON-serializable") + updated_content = self.validate_json_content(content) if content else current_revision.content + updated_content_dict = json.loads(updated_content) # If title is passed as arg to the method, update the title in the json if title: @@ -166,6 +159,49 @@ revision_to_update.last_updated_by = updator revision_to_update.save() + def validate_json_content(self, content): + """ + Checks that the json content is valid (keys and structures), raise a ValidationError if format is wrong or value is wrong (for ids), + if a key is missing, autocompletes with the empty default value + + Returns the validated json string + """ + try: + content_to_validate_dict = json.loads(content) + except ValueError: + raise ValidationError("Provided content to create Renkan is not a JSON-serializable") + if "id" not in content_to_validate_dict or content_to_validate_dict["id"] == "" : + content_to_validate_dict["id"] = str(self.renkan_guid) + if "title" not in content_to_validate_dict: + content_to_validate_dict["title"] = "" + if "description" not in content_to_validate_dict: + content_to_validate_dict["description"] = "" + if "created" not in content_to_validate_dict: + content_to_validate_dict["description"] = "" + if "updated" not in content_to_validate_dict: + content_to_validate_dict["description"] = "" + expected_workspace_id = str(self.workspace.workspace_guid) if self.workspace is not None else "" + if "space_id" not in content_to_validate_dict: + content_to_validate_dict["space_id"] = expected_workspace_id + if "nodes" not in content_to_validate_dict: + content_to_validate_dict["nodes"] = [] + if "edges" not in content_to_validate_dict: + content_to_validate_dict["edges"] = [] + if "views" not in content_to_validate_dict: + content_to_validate_dict["views"] = [] + if "users" not in content_to_validate_dict: + content_to_validate_dict["users"] = [] + + if type(content_to_validate_dict["nodes"]) is not list: + raise ValidationError("Provided content has an invalid 'nodes' key: not a list") + if type(content_to_validate_dict["edges"]) is not list: + raise ValidationError("Provided content has an invalid 'edges' key: not a list") + if type(content_to_validate_dict["views"]) is not list: + raise ValidationError("Provided content has an invalid 'views' key: not a list") + if type(content_to_validate_dict["users"]) is not list: + raise ValidationError("Provided content has an invalid 'users' key: not a list") + return json.dumps(content_to_validate_dict) + @transaction.atomic def delete(self): """ diff -r 854a027c80ff -r b9edc1c1538a server/python/django2/renkanmanager/serializers.py --- a/server/python/django2/renkanmanager/serializers.py Wed Apr 27 16:36:30 2016 +0200 +++ b/server/python/django2/renkanmanager/serializers.py Tue May 03 14:32:28 2016 +0200 @@ -1,6 +1,6 @@ -import json -import uuid +import json, uuid, datetime, time +from django.db import transaction from django.contrib.auth import get_user_model from django.conf import settings from renkanmanager.models import Renkan, Workspace, Revision @@ -10,9 +10,9 @@ class RenkanSerializer(serializers.Serializer): id = serializers.ReadOnlyField(source="renkan_guid") - current_revision_id = serializers.ReadOnlyField(source="current_revision_guid") - workspace_id = serializers.CharField(source="workspace_guid", required=False) - source_revision_id = serializers.CharField(source="source_revision_guid", required=False) + current_revision_id = serializers.SlugRelatedField(source="current_revision", slug_field="revision_guid", read_only=True) + workspace_id = serializers.SlugRelatedField(source="workspace", slug_field="workspace_guid", queryset=Workspace.objects, required=False) + source_revision_id = serializers.SlugRelatedField(source="source_revision", slug_field="revision_guid", queryset=Revision.objects, required=False) revision_count = serializers.ReadOnlyField() created_by = serializers.SlugRelatedField(source="creator", slug_field=RENKAN_USER_DISPLAY_FIELD, read_only=True) last_updated_by = serializers.SerializerMethodField("get_current_revision_last_updator") @@ -21,72 +21,59 @@ creation_date = serializers.ReadOnlyField() modification_date = serializers.SerializerMethodField("get_current_revision_modification_date") create_new_revision = serializers.BooleanField(write_only=True, required=False) # only used for updating + validation_timestamp = serializers.CharField(write_only=True, required=False) # only used for updating is a content json is not provided # ADD ERROR HANDLING def get_current_revision_last_updator(self, renkan): - current_revision = Revision.objects.get(revision_guid = renkan.current_revision_guid) - return getattr(current_revision.last_updated_by, RENKAN_USER_DISPLAY_FIELD) + return getattr(renkan.current_revision.last_updated_by, RENKAN_USER_DISPLAY_FIELD) def get_current_revision_modification_date(self, renkan): - current_revision = Revision.objects.get(revision_guid = renkan.current_revision_guid) - return current_revision.modification_date + return renkan.current_revision.modification_date + @transaction.atomic def create(self, validated_data): """ Method to create a new Renkan (and its first revision) """ creator = validated_data.get('creator') - initial_revision = Revision.objects.create( - title = validated_data.get('title', 'Untitled renkan'), - creator = creator, - last_updated_by = creator - ) - renkan = Renkan.objects.create( - current_revision_guid = initial_revision.revision_guid, - workspace_guid = validated_data.get('workspace_guid', ''), - source_revision_guid = validated_data.get('source_revision_guid', ''), - creator = creator - ) - initial_revision.parent_renkan_guid = renkan.renkan_guid - initial_revision.content = validated_data.get('content', json.dumps( - { - "id": str(renkan.renkan_guid), - "title": initial_revision.title, - "description": "", - "created": str(initial_revision.creation_date), - "updated": str(initial_revision.modification_date), - "edges": [], - "nodes": [], - "users": [], - "space_id": str(renkan.workspace_guid), - "views": [] - } - )) - initial_revision.save() - renkan.save() - return renkan + workspace_obj = validated_data.get('workspace', None) + source_revision_obj = validated_data.get('source_revision', None) + try: + return Renkan.objects.create_renkan( + creator=creator, + title=validated_data.get('title', ''), + content=validated_data.get('content', ''), + workspace=workspace_obj, + source_revision=source_revision_obj + ) + except ValidationError as ve: + raise serializers.ValidationError(ve.args[0]) + @transaction.atomic def update(self, renkan, validated_data): """ Method to update a Renkan object. Creates a new revision if needed """ updator = validated_data.get('updator') - current_revision = Revision.objects.get(revision_guid=renkan.current_revision_guid) - if validated_data.get("create_new_revision", False): - revision_to_update = Revision.objects.create(creator=updator) - revision_to_update.parent_renkan_guid = renkan.renkan_guid - renkan.current_revision_guid = revision_to_update.revision_guid + create_new_revision = validated_data.get("create_new_revision", False) + title = validated_data.get('title', renkan.current_revision.title) + content = validated_data.get('content', '') + if not content: + content = renkan.current_revision.content + if not 'validation_timestamp' in validated_data: + raise serializers.ValidationError("No validation timestamp and no content json to extract it from") + validation_timestamp = validated_data.get('validation_timestamp') else: - revision_to_update = current_revision - revision_to_update.title = validated_data.get('title', current_revision.title) - revision_to_update.content = validated_data.get('content', current_revision.content) - revision_to_update.last_updated_by = updator - revision_to_update.save() - if not updator.has_perm('view_revision', revision_to_update): - assign_perm('view_revision', updator, revision_to_update) - assign_perm('delete_revision', updator, revision_to_update) - renkan.save() + validation_timestamp = json.loads(content).get("updated", "") + if title != json.loads(content).get("title", ""): + content_dict = json.loads(content) + content_dict["title"] = title + content = json.dumps(content_dict) + try: + renkan.save_renkan(updator=updator, timestamp=validation_timestamp, title=title, content=content, create_new_revision=create_new_revision) + except ValidationError as ve: + raise serializers.ValidationError(ve.args[0]) return renkan def validate_workspace_id(self, value): @@ -99,8 +86,7 @@ raise serializers.ValidationError("You cannot update source_revision_guid") return value - def validate_content(self, value): - + def validate_content(self, value): try: json.loads(value) except ValueError: @@ -119,27 +105,27 @@ class RevisionSerializer(serializers.Serializer): id = serializers.ReadOnlyField(source="revision_guid") - parent_renkan_id = serializers.ReadOnlyField(source="parent_renkan_guid") + parent_renkan_id = serializers.SlugRelatedField(source="parent_renkan", slug_field='renkan_guid', read_only=True) workspace_id = serializers.SerializerMethodField("get_related_workspace_guid") title = serializers.ReadOnlyField() content = serializers.JSONField(read_only=True) renkan_created_by = serializers.SerializerMethodField("get_related_renkan_creator") renkan_creation_date = serializers.SerializerMethodField("get_related_renkan_creation_date") revision_created_by = serializers.SlugRelatedField(source="creator", slug_field=RENKAN_USER_DISPLAY_FIELD, read_only=True) - revision_last_updated_by = serializers.StringRelatedField(source="last_updated_by") + revision_last_updated_by = serializers.SlugRelatedField(source="last_updated_by", slug_field=RENKAN_USER_DISPLAY_FIELD, read_only=True) revision_modification_date = serializers.ReadOnlyField(source="modification_date") def get_related_workspace_guid(self, revision): - parent_renkan = Renkan.objects.get(renkan_guid = revision.parent_renkan_guid) - return parent_renkan.workspace_guid + if revision.parent_renkan.workspace: + return revision.parent_renkan.workspace.workspace_guid + else: + return '' def get_related_renkan_creator(self, revision): - parent_renkan = Renkan.objects.get(renkan_guid = revision.parent_renkan_guid) - return getattr(parent_renkan.creator, RENKAN_USER_DISPLAY_FIELD) + return getattr(revision.parent_renkan.creator, RENKAN_USER_DISPLAY_FIELD) def get_related_renkan_creation_date(self, revision): - parent_renkan = Renkan.objects.get(renkan_guid = revision.parent_renkan_guid) - return parent_renkan.creation_date + return revision.parent_renkan.creation_date class WorkspaceSerializer(serializers.Serializer): diff -r 854a027c80ff -r b9edc1c1538a server/python/django2/renkanmanager/tests/v1_0/tests_renkan.py --- a/server/python/django2/renkanmanager/tests/v1_0/tests_renkan.py Wed Apr 27 16:36:30 2016 +0200 +++ b/server/python/django2/renkanmanager/tests/v1_0/tests_renkan.py Tue May 03 14:32:28 2016 +0200 @@ -25,44 +25,47 @@ post_response = self.client.post(post_url, data, format="json") self.test_workspace_guid = json.loads(post_response.content.decode()).get("id") self.first_test_title = "test_title_1" - self.first_test_content = json.dumps({ + self.first_test_content_dict = { "id": "", "title": "test_title_1", "description": "test_description_1", - "created": "", - "updated": "", + "created": "2016-03-11 15:10:10.645193+00:00", + "updated": "2016-03-11 15:10:10.645193+00:00", "edges": [], "nodes": [], "users": [], "space_id": "", "views": [] - }) - self.second_test_title = "test_title_1" - self.second_test_content = json.dumps({ + } + self.first_test_content = json.dumps(self.first_test_content_dict) + self.second_test_title = "test_title_2" + self.second_test_content_dict = { "id": "", "title": "test_title_2", "description": "test_description_2", - "created": "", - "updated": "", + "created": "2016-03-11 15:10:10.645193+00:00", + "updated": "2016-03-12 15:10:10.645193+00:00", "edges": [], "nodes": [], "users": [], "space_id": "", "views": [] - }) - self.third_test_title = "test_title_1" - self.third_test_content = json.dumps({ + } + self.second_test_content = json.dumps(self.second_test_content_dict) + self.third_test_title = "test_title_3" + self.third_test_content_dict = { "id": "", "title": "test_title_3", "description": "test_description_3", - "created": "", - "updated": "", + "created": "2016-03-11 15:10:10.645193+00:00", + "updated": "2016-03-13 15:10:10.645193+00:00", "edges": [], "nodes": [], "users": [], "space_id": "", "views": [] - }) + } + self.third_test_content = json.dumps(self.third_test_content_dict) def test_create_renkan(self): @@ -80,6 +83,7 @@ # Checking POSTed Renkan data post_response_dict = json.loads(post_response.content.decode()) + post_response_content_dict = json.loads(post_response_dict.get("content", "{}")) new_renkan_guid = post_response_dict.get("id", "") new_renkan = Renkan.objects.get(renkan_guid=new_renkan_guid) # GUIDs and username @@ -88,7 +92,11 @@ self.assertEqual(getattr(self.user, User.USERNAME_FIELD), getattr(new_renkan.creator, User.USERNAME_FIELD)) # Content and title extracted from revision self.assertEqual(new_renkan.title, self.first_test_title) - self.assertEqual(new_renkan.content, self.first_test_content) + new_renkan_content_dict = json.loads(new_renkan.content) + for key in list(self.first_test_content_dict.keys()) + list(set(new_renkan_content_dict.keys()) - set(self.first_test_content_dict.keys())): + if key != "updated" and key != "created" and key != "id": + self.assertEqual(new_renkan_content_dict.get(key, ""), self.first_test_content_dict.get(key, "")) + # Revision count self.assertEqual(new_renkan.revision_count, 1) self.assertEqual(post_response_dict.get("revision_count", ""), 1) @@ -102,10 +110,16 @@ # Title and content # in the response self.assertEqual(post_response_dict.get("title", ""), self.first_test_title) - self.assertEqual(post_response_dict.get("content", ""), self.first_test_content) + for key in list(self.first_test_content_dict.keys()) + list(set(post_response_content_dict.keys()) - set(self.first_test_content_dict.keys())): + if key != "updated" and key != "created" and key != "id": + self.assertEqual(post_response_content_dict.get(key, ""), self.first_test_content_dict.get(key, "")) + # in the database self.assertEqual(new_renkan_revision.title, self.first_test_title) - self.assertEqual(new_renkan_revision.content, self.first_test_content) + new_revision_content_dict = json.loads(new_renkan_revision.content) + for key in list(self.first_test_content_dict.keys()) + list(set(new_revision_content_dict.keys()) - set(self.first_test_content_dict.keys())): + if key != "updated" and key != "created" and key != "id": + self.assertEqual(new_revision_content_dict.get(key, ""), self.first_test_content_dict.get(key, "")) # Checking permission self.assertTrue(self.user.has_perm("view_renkan", new_renkan)) @@ -128,13 +142,17 @@ new_renkan = Renkan.objects.get(renkan_guid=new_renkan_guid) # GUIDs and username self.assertEqual(self.test_workspace_guid, post_response_dict.get("workspace_id", "")) - self.assertEqual(self.test_workspace_guid, new_renkan.workspace_guid) + self.assertEqual(self.test_workspace_guid, new_renkan.workspace.workspace_guid) self.assertEqual(getattr(self.user, User.USERNAME_FIELD), post_response_dict.get("created_by", "")) self.assertEqual(getattr(self.user, User.USERNAME_FIELD), post_response_dict.get("last_updated_by", "")) self.assertEqual(getattr(self.user, User.USERNAME_FIELD), getattr(new_renkan.creator, User.USERNAME_FIELD)) # Content and title extracted from revision self.assertEqual(new_renkan.title, self.second_test_title) - self.assertEqual(new_renkan.content, self.second_test_content) + new_renkan_content_dict = json.loads(new_renkan.content) + for key in list(self.second_test_content_dict.keys()) + list(set(new_renkan_content_dict.keys()) - set(self.second_test_content_dict.keys())): + if key != "updated" and key != "created" and key != "id" and key != "space_id": + self.assertEqual(new_renkan_content_dict.get(key, ""), self.second_test_content_dict.get(key, "")) + # Revision count self.assertEqual(new_renkan.revision_count, 1) self.assertEqual(post_response_dict.get("revision_count", ""), 1) @@ -161,6 +179,16 @@ self.assertEqual(post_response.status_code, status.HTTP_404_NOT_FOUND) ################################################### + # POSTing with non-JSON serializable content + ################################################### + + post_url = reverse("v1.0:renkan_list") + data = {"title": self.third_test_title, "content": "notJson(Serializable}"} + + post_response = self.client.post(post_url, data, format="json") + self.assertEqual(post_response.status_code, status.HTTP_400_BAD_REQUEST) + + ################################################### # try POSTing with unauthorized user ################################################### @@ -259,6 +287,9 @@ self.assertTrue(isinstance(get_response_content, list)) self.assertEqual(len(get_response_content), 1) + + + def test_update_renkan(self): @@ -270,50 +301,66 @@ data = {} post_response = self.client.post(post_url, data, format="json") post_response_dict = json.loads(post_response.content.decode()) + post_response_ts = json.loads(post_response_dict.get("content"), "{}").get("updated", "") renkan_guid = post_response_dict.get("id", "") revision_guid = post_response_dict.get("current_revision_id", "") - + ################################################### # PUTting by updating current revision ################################################### put_url = reverse("v1.0:renkan_detail", kwargs={"renkan_guid": renkan_guid}) + put_content_dict = json.loads(self.first_test_content) + put_content_dict["updated"] = post_response_ts put_data = { "title" : self.first_test_title, - "content" : self.first_test_content, + "content" : json.dumps(put_content_dict), "create_new_revision": False } put_response = self.client.put(put_url, put_data, format="json") self.assertEqual(put_response.status_code, status.HTTP_200_OK) put_response_dict = json.loads(put_response.content.decode()) + put_response_content_dict = json.loads(put_response_dict.get("content", "{}")) + put_response_ts = put_response_content_dict.get("updated", "") + # checking we"re still on the same revision as the initial one updated_project = Renkan.objects.get(renkan_guid=renkan_guid) self.assertEqual(revision_guid, put_response_dict.get("current_revision_id", "")) self.assertEqual(put_response_dict.get("revision_count", ""), 1) self.assertEqual(updated_project.revision_count, 1) - self.assertEqual(revision_guid, updated_project.current_revision_guid) + self.assertEqual(revision_guid, updated_project.current_revision.revision_guid) # checking data was updated # in the reponse self.assertEqual(put_response_dict.get("title", ""), self.first_test_title) - self.assertEqual(put_response_dict.get("content", ""), self.first_test_content) + for key in list(self.first_test_content_dict.keys()) + list(set(put_response_content_dict.keys()) - set(self.first_test_content_dict.keys())): + if key != "updated" and key!= "id" and key != "space_id": + self.assertEqual(put_response_content_dict.get(key, ""), self.first_test_content_dict.get(key, "")) + # in the database updated_revision = Revision.objects.get(revision_guid=revision_guid) + updated_revision_content_dict = json.loads(updated_revision.content) self.assertEqual(updated_revision.title, self.first_test_title) - self.assertEqual(updated_revision.content, self.first_test_content) + for key in list(self.first_test_content_dict.keys()) + list(set(updated_revision_content_dict.keys()) - set(self.first_test_content_dict.keys())): + if key != "updated" and key!= "id" and key != "space_id": + self.assertEqual(updated_revision_content_dict.get(key, ""), self.first_test_content_dict.get(key, "")) ################################################### # PUTting by creating a new revision ################################################### + put_content_dict = json.loads(self.second_test_content) + put_content_dict["updated"] = put_response_ts put_data = { "title" : self.second_test_title, - "content" : self.second_test_content, + "content" : json.dumps(put_content_dict), "create_new_revision": True } put_response = self.client.put(put_url, put_data, format="json") - self.assertEqual(put_response.status_code, status.HTTP_200_OK) + self.assertEqual(put_response.status_code, status.HTTP_200_OK) put_response_dict = json.loads(put_response.content.decode()) + put_response_content_dict = json.loads(put_response_dict.get("content", "{}")) + put_response_ts = put_response_content_dict.get("updated", "") # checking we created a new revision updated_project = Renkan.objects.get(renkan_guid=renkan_guid) @@ -325,15 +372,20 @@ self.assertEqual(updated_project.revision_count, 2) # checking project now points towards new revision - self.assertEqual(updated_project.current_revision_guid, created_revision_guid) + self.assertEqual(updated_project.current_revision.revision_guid, created_revision_guid) # checking data was updated # in the reponse self.assertEqual(put_response_dict.get("title", ""), self.second_test_title) - self.assertEqual(put_response_dict.get("content", ""), self.second_test_content) + for key in list(self.second_test_content_dict.keys()) + list(set(put_response_content_dict.keys()) - set(self.second_test_content_dict.keys())): + if key != "updated" and key!= "id" and key != "space_id": + self.assertEqual(put_response_content_dict.get(key, ""), self.second_test_content_dict.get(key, "")) # in the database updated_revision = Revision.objects.get(revision_guid=created_revision_guid) + updated_revision_content_dict = json.loads(updated_revision.content) self.assertEqual(updated_revision.title, self.second_test_title) - self.assertEqual(updated_revision.content, self.second_test_content) + for key in list(self.second_test_content_dict.keys()) + list(set(updated_revision_content_dict.keys()) - set(self.second_test_content_dict.keys())): + if key != "updated" and key!= "id" and key != "space_id": + self.assertEqual(updated_revision_content_dict.get(key, ""), self.second_test_content_dict.get(key, "")) ################################################### # try to update source_revision_guid or workspace_guid @@ -347,14 +399,6 @@ put_response = self.client.put(put_url, put_data, format="json") self.assertEqual(put_response.status_code, status.HTTP_400_BAD_REQUEST) - put_data = { - "title" : self.third_test_title, - "content" : self.third_test_content, - "source_revision_id" : "bleh-revision-guid" # should fail without even checking the id - } - put_response = self.client.put(put_url, put_data, format="json") - self.assertEqual(put_response.status_code, status.HTTP_400_BAD_REQUEST) - ################################################### # PUTting with wrong guid ################################################### @@ -398,6 +442,7 @@ first_copy_url = post_url+"?source_renkan_id="+source_renkan_guid first_copy_response = self.client.post(first_copy_url, first_copy_data, format="json") first_copy_response_dict = json.loads(first_copy_response.content.decode()) + first_copy_response_content_dict = json.loads(first_copy_response_dict.get("content", "{}")) self.assertEqual(first_copy_response.status_code, status.HTTP_201_CREATED) self.assertEqual(Renkan.objects.count(), 2) # Checking data @@ -405,16 +450,20 @@ self.assertNotEqual(first_copy_response_dict.get("id"), source_renkan_guid) self.assertEqual(first_copy_response_dict.get("source_revision_id"), source_revision_guid) self.assertEqual(first_copy_response_dict.get("title", ""), "new_title_copy_1") - self.assertEqual(first_copy_response_dict.get("content", ""), self.first_test_content) + for key in list(self.first_test_content_dict.keys()) + list(set(first_copy_response_content_dict.keys()) - set(self.first_test_content_dict.keys())): + if key != "updated" and key != "created" and key !="title" and key!= "id" and key != "space_id": + self.assertEqual(first_copy_response_content_dict.get(key, ""), self.first_test_content_dict.get(key, "")) self.assertEqual(first_copy_response_dict.get("created_by", ""), getattr(self.user, User.USERNAME_FIELD)) self.assertEqual(first_copy_response_dict.get("last_updated_by", ""), getattr(self.user, User.USERNAME_FIELD)) # in the database first_copy = Renkan.objects.get(renkan_guid=first_copy_response_dict.get("id", "")) first_copy_revision = Revision.objects.get(revision_guid=first_copy_response_dict.get("current_revision_id", "")) self.assertEqual(first_copy.title, "new_title_copy_1") - self.assertEqual(first_copy.content, self.first_test_content) self.assertEqual(first_copy_revision.title, "new_title_copy_1") - self.assertEqual(first_copy_revision.content, self.first_test_content) + first_copy_revision_content_dict = json.loads(first_copy_revision.content) + for key in list(self.first_test_content_dict.keys()) + list(set(first_copy_revision_content_dict.keys()) - set(self.first_test_content_dict.keys())): + if key != "updated" and key != "created" and key!= "id" and key != "space_id": + self.assertEqual(first_copy_revision_content_dict.get(key, ""), self.first_test_content_dict.get(key, "")) self.assertEqual(getattr(first_copy.creator, User.USERNAME_FIELD), getattr(self.user, User.USERNAME_FIELD)) self.assertEqual(getattr(first_copy_revision.creator, User.USERNAME_FIELD), getattr(self.user, User.USERNAME_FIELD)) self.assertEqual(getattr(first_copy_revision.last_updated_by, User.USERNAME_FIELD), getattr(self.user, User.USERNAME_FIELD)) @@ -427,6 +476,7 @@ second_copy_url = post_url second_copy_response = self.client.post(second_copy_url, second_copy_data, format="json") second_copy_response_dict = json.loads(second_copy_response.content.decode()) + second_copy__response_content_dict = json.loads(second_copy_response_dict.get("content", "{}")) self.assertEqual(second_copy_response.status_code, status.HTTP_201_CREATED) self.assertEqual(Renkan.objects.count(), 3) # Checking data @@ -434,16 +484,20 @@ self.assertNotEqual(second_copy_response_dict.get("id"), source_renkan_guid) self.assertEqual(second_copy_response_dict.get("source_revision_id"), source_revision_guid) self.assertEqual(second_copy_response_dict.get("title", ""), "new_title_copy_2") - self.assertEqual(second_copy_response_dict.get("content", ""), self.first_test_content) + for key in list(self.first_test_content_dict.keys()) + list(set(second_copy__response_content_dict.keys()) - set(self.first_test_content_dict.keys())): + if key != "updated" and key != "created" and key!= "id" and key != "space_id": + self.assertEqual(second_copy__response_content_dict.get(key, ""), self.first_test_content_dict.get(key, "")) self.assertEqual(second_copy_response_dict.get("created_by", ""), getattr(self.user, User.USERNAME_FIELD)) self.assertEqual(second_copy_response_dict.get("last_updated_by", ""), getattr(self.user, User.USERNAME_FIELD)) # in the database second_copy = Renkan.objects.get(renkan_guid=second_copy_response_dict.get("id", "")) second_copy_revision = Revision.objects.get(revision_guid=second_copy_response_dict.get("current_revision_id", "")) self.assertEqual(second_copy.title, "new_title_copy_2") - self.assertEqual(second_copy.content, self.first_test_content) self.assertEqual(second_copy_revision.title, "new_title_copy_2") - self.assertEqual(second_copy_revision.content, self.first_test_content) + second_copy_revision_content_dict = json.loads(second_copy_revision.content) + for key in list(self.first_test_content_dict.keys()) + list(set(second_copy_revision_content_dict.keys()) - set(self.first_test_content_dict.keys())): + if key != "updated" and key != "created" and key!= "id" and key != "space_id": + self.assertEqual(second_copy_revision_content_dict.get(key, ""), self.first_test_content_dict.get(key, "")) self.assertEqual(getattr(second_copy.creator, User.USERNAME_FIELD), getattr(self.user, User.USERNAME_FIELD)) self.assertEqual(getattr(second_copy_revision.creator, User.USERNAME_FIELD), getattr(self.user, User.USERNAME_FIELD)) self.assertEqual(getattr(second_copy_revision.last_updated_by, User.USERNAME_FIELD), getattr(self.user, User.USERNAME_FIELD)) @@ -456,6 +510,7 @@ third_copy_url = post_url+"?source_revision_id="+source_revision_guid third_copy_response = self.client.post(third_copy_url, third_copy_data, format="json") third_copy_response_dict = json.loads(third_copy_response.content.decode()) + third_copy_response_content_dict = json.loads(third_copy_response_dict.get("content", "{}")) self.assertEqual(third_copy_response.status_code, status.HTTP_201_CREATED) self.assertEqual(Renkan.objects.count(), 4) # Checking data @@ -463,16 +518,20 @@ self.assertNotEqual(third_copy_response_dict.get("id"), source_renkan_guid) self.assertEqual(third_copy_response_dict.get("source_revision_id"), source_revision_guid) self.assertEqual(third_copy_response_dict.get("title", ""), "new_title_copy_3") - self.assertEqual(third_copy_response_dict.get("content", ""), self.first_test_content) + for key in list(self.first_test_content_dict.keys()) + list(set(third_copy_response_content_dict.keys()) - set(self.first_test_content_dict.keys())): + if key != "updated" and key != "created" and key!= "id" and key != "space_id": + self.assertEqual(third_copy_response_content_dict.get(key, ""), self.first_test_content_dict.get(key, "")) self.assertEqual(third_copy_response_dict.get("created_by", ""), getattr(self.user, User.USERNAME_FIELD)) self.assertEqual(third_copy_response_dict.get("last_updated_by", ""), getattr(self.user, User.USERNAME_FIELD)) # in the database third_copy = Renkan.objects.get(renkan_guid=third_copy_response_dict.get("id", "")) third_copy_revision = Revision.objects.get(revision_guid=third_copy_response_dict.get("current_revision_id", "")) + third_copy_revision_content_dict = json.loads(third_copy_revision.content) self.assertEqual(third_copy.title, "new_title_copy_3") - self.assertEqual(third_copy.content, self.first_test_content) self.assertEqual(third_copy_revision.title, "new_title_copy_3") - self.assertEqual(third_copy_revision.content, self.first_test_content) + for key in list(self.first_test_content_dict.keys()) + list(set(third_copy_revision_content_dict.keys()) - set(self.first_test_content_dict.keys())): + if key != "updated" and key != "created" and key!= "id" and key != "space_id": + self.assertEqual(third_copy_revision_content_dict.get(key, ""), self.first_test_content_dict.get(key, "")) self.assertEqual(getattr(third_copy.creator, User.USERNAME_FIELD), getattr(self.user, User.USERNAME_FIELD)) self.assertEqual(getattr(third_copy_revision.creator, User.USERNAME_FIELD), getattr(self.user, User.USERNAME_FIELD)) self.assertEqual(getattr(third_copy_revision.last_updated_by, User.USERNAME_FIELD), getattr(self.user, User.USERNAME_FIELD)) @@ -485,6 +544,7 @@ fourth_copy_url = post_url fourth_copy_response = self.client.post(fourth_copy_url, fourth_copy_data, format="json") fourth_copy_response_dict = json.loads(fourth_copy_response.content.decode()) + fourth_copy_response_content_dict = json.loads(fourth_copy_response_dict.get("content", "{}")) self.assertEqual(fourth_copy_response.status_code, status.HTTP_201_CREATED) self.assertEqual(Renkan.objects.count(), 5) # Checking data @@ -492,16 +552,20 @@ self.assertNotEqual(fourth_copy_response_dict.get("id"), source_renkan_guid) self.assertEqual(fourth_copy_response_dict.get("source_revision_id"), source_revision_guid) self.assertEqual(fourth_copy_response_dict.get("title", ""), self.first_test_title) - self.assertEqual(fourth_copy_response_dict.get("content", ""), self.first_test_content) + for key in list(self.first_test_content_dict.keys()) + list(set(fourth_copy_response_content_dict.keys()) - set(self.first_test_content_dict.keys())): + if key != "updated" and key != "created" and key!= "id" and key != "space_id": + self.assertEqual(fourth_copy_response_content_dict.get(key, ""), self.first_test_content_dict.get(key, "")) self.assertEqual(fourth_copy_response_dict.get("created_by", ""), getattr(self.user, User.USERNAME_FIELD)) self.assertEqual(fourth_copy_response_dict.get("last_updated_by", ""), getattr(self.user, User.USERNAME_FIELD)) # in the database fourth_copy = Renkan.objects.get(renkan_guid=fourth_copy_response_dict.get("id", "")) fourth_copy_revision = Revision.objects.get(revision_guid=fourth_copy_response_dict.get("current_revision_id", "")) self.assertEqual(fourth_copy.title, self.first_test_title) - self.assertEqual(fourth_copy.content, self.first_test_content) self.assertEqual(fourth_copy_revision.title, self.first_test_title) - self.assertEqual(fourth_copy_revision.content, self.first_test_content) + fourth_copy_revision_content_dict = json.loads(fourth_copy_revision.content) + for key in list(self.first_test_content_dict.keys()) + list(set(fourth_copy_revision_content_dict.keys()) - set(self.first_test_content_dict.keys())): + if key != "updated" and key != "created" and key!= "id" and key != "space_id": + self.assertEqual(fourth_copy_revision_content_dict.get(key, ""), self.first_test_content_dict.get(key, "")) self.assertEqual(getattr(fourth_copy.creator, User.USERNAME_FIELD), getattr(self.user, User.USERNAME_FIELD)) self.assertEqual(getattr(fourth_copy_revision.creator, User.USERNAME_FIELD), getattr(self.user, User.USERNAME_FIELD)) self.assertEqual(getattr(fourth_copy_revision.last_updated_by, User.USERNAME_FIELD), getattr(self.user, User.USERNAME_FIELD)) @@ -542,6 +606,7 @@ post_response_dict = json.loads(post_response.content.decode()) to_delete_renkan_guid = post_response_dict.get("id", "") to_delete_revision_guid = post_response_dict.get("current_revision_id", "") + to_delete_renkan_ts = json.loads(post_response_dict.get("content", "")).get("updated", "") ################################################### # POSTing copy @@ -552,7 +617,7 @@ copy_renkan_guid = post_response_dict.get("id", "") # Adding another revision - put_data = {"title": self.first_test_title+"_edited!", "create_new_revision": True} + put_data = {"title": self.first_test_title+"_edited!", "validation_timestamp": to_delete_renkan_ts, "create_new_revision": True} put_url = reverse("v1.0:renkan_detail", kwargs={"renkan_guid":to_delete_renkan_guid}) put_response = self.client.put(put_url, put_data, format="json") self.assertEqual(Renkan.objects.count(), 2) @@ -592,7 +657,7 @@ # Check that remaining renkan doesn"t have reference to deleted renkan ################################################### copy_renkan = Renkan.objects.get(renkan_guid=copy_renkan_guid) - self.assertEqual(copy_renkan.source_revision_guid, "") + self.assertIsNone(copy_renkan.source_revision) ################################################### # Try to DELETE renkan with wrong guid diff -r 854a027c80ff -r b9edc1c1538a server/python/django2/renkanmanager/tests/v1_0/tests_revision.py --- a/server/python/django2/renkanmanager/tests/v1_0/tests_revision.py Wed Apr 27 16:36:30 2016 +0200 +++ b/server/python/django2/renkanmanager/tests/v1_0/tests_revision.py Tue May 03 14:32:28 2016 +0200 @@ -24,21 +24,24 @@ post_url = reverse("v1.0:renkan_list") self.test_title = "test_title_1" - self.test_content = json.dumps({ + test_content_dict_init = { "id": "", "title": "test_title_1", - "description": "test_description_1", - "created": "", - "updated": "", + "description": "", + "created": "2016-03-11 15:10:10.645193+00:00", + "updated": "2016-03-11 15:10:10.645193+00:00", "edges": [], "nodes": [], "users": [], "space_id": "", "views": [] - }) + } + self.test_content = json.dumps(test_content_dict_init) self.test_data = {"title": self.test_title, "content": self.test_content} post_response = self.client.post(post_url, self.test_data, format="json") post_response_dict = json.loads(post_response.content.decode()) + self.test_content_dict = json.loads(post_response_dict.get("content", "{}")) + self.post_response_content_dict = json.loads(post_response_dict.get("content", "{}")) self.test_renkan_guid = post_response_dict.get("id", "") self.test_initial_revision_guid = post_response_dict.get("current_revision_id", "") @@ -59,7 +62,6 @@ self.assertEqual(get_response_dict.get("revision_created_by", ""), getattr(self.user, User.USERNAME_FIELD)) self.assertEqual(get_response_dict.get("revision_last_updated_by", ""), getattr(self.user, User.USERNAME_FIELD)) self.assertEqual(get_response_dict.get("title", ""), self.test_data.get("title", "")) - self.assertEqual(get_response_dict.get("content", ""), self.test_data.get("content", "")) ################################################### # GETting with wrong renkan guid @@ -96,6 +98,7 @@ put_title = "edited_title" put_data = { "title" : put_title, + "validation_timestamp": self.post_response_content_dict.get("updated", ""), "create_new_revision": True } put_response = self.client.put(put_url, put_data, format="json") @@ -139,6 +142,7 @@ put_title = "edited_title" put_data = { "title" : put_title, + "validation_timestamp": self.post_response_content_dict.get("updated", ""), "create_new_revision": True } put_response = self.client.put(put_url, put_data, format="json")