Merge pull request #179 from taigaio/bug/1709/error-on-concurrent-edition

Detecting concurrent edition and failing properly if needed
remotes/origin/enhancement/email-actions
Jesús Espino 2014-12-03 10:21:50 +01:00
commit 911eacd664
7 changed files with 156 additions and 130 deletions

View File

@ -26,16 +26,20 @@ class OCCResourceMixin(object):
Rest Framework resource mixin for resources that need to have concurrent
accesses and editions controlled.
"""
def pre_save(self, obj):
current_version = obj.version
param_version = self.request.DATA.get('version', None)
def _validate_and_update_version(self, obj):
current_version = None
if obj.id:
current_version = type(obj).objects.model.objects.get(id=obj.id).version
if obj.id is not None and current_version != param_version:
param_version = self.request.DATA.get('version', None)
if current_version != param_version:
raise exc.WrongArguments({"version": "The version doesn't match with the current one"})
if obj.id:
obj.version = models.F('version') + 1
def pre_save(self, obj):
self._validate_and_update_version(obj)
super().pre_save(obj)
def post_save(self, obj, created=False):

View File

@ -7,6 +7,9 @@ from taiga.base.utils import json
from tests import factories as f
from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals
from taiga.projects.votes.services import add_vote
from taiga.projects.occ import OCCResourceMixin
from unittest import mock
import pytest
pytestmark = pytest.mark.django_db
@ -132,6 +135,7 @@ def test_issue_update(client, data):
data.project_owner
]
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version") as _validate_and_update_version_mock:
issue_data = IssueSerializer(data.public_issue).data
issue_data["subject"] = "test"
issue_data = json.dumps(issue_data)
@ -162,6 +166,7 @@ def test_issue_delete(client, data):
data.project_member_without_perms,
data.project_member_with_perms,
]
results = helper_test_http_method(client, 'delete', public_url, None, users)
assert results == [401, 403, 403, 204]
results = helper_test_http_method(client, 'delete', private_url1, None, users)
@ -261,6 +266,7 @@ def test_issue_patch(client, data):
data.project_owner
]
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version") as _validate_and_update_version_mock:
patch_data = json.dumps({"subject": "test", "version": data.public_issue.version})
results = helper_test_http_method(client, 'patch', public_url, patch_data, users)
assert results == [401, 403, 403, 200, 200]
@ -303,7 +309,6 @@ def test_issue_bulk_create(client, data):
data.project_owner
]
bulk_data = json.dumps({"bulk_issues": "test1\ntest2",
"project_id": data.public_issue.project.pk})
results = helper_test_http_method(client, 'post', url, bulk_data, users)

View File

@ -3,10 +3,13 @@ from django.core.urlresolvers import reverse
from taiga.base.utils import json
from taiga.projects.tasks.serializers import TaskSerializer
from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS
from taiga.projects.occ import OCCResourceMixin
from tests import factories as f
from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals
from unittest import mock
import pytest
pytestmark = pytest.mark.django_db
@ -132,6 +135,7 @@ def test_task_update(client, data):
data.project_owner
]
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version") as _validate_and_update_version_mock:
task_data = TaskSerializer(data.public_task).data
task_data["subject"] = "test"
task_data = json.dumps(task_data)
@ -252,6 +256,7 @@ def test_task_patch(client, data):
data.project_owner
]
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version") as _validate_and_update_version_mock:
patch_data = json.dumps({"subject": "test", "version": data.public_task.version})
results = helper_test_http_method(client, 'patch', public_url, patch_data, users)
assert results == [401, 403, 403, 200, 200]

View File

@ -3,10 +3,13 @@ from django.core.urlresolvers import reverse
from taiga.base.utils import json
from taiga.projects.userstories.serializers import UserStorySerializer
from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS
from taiga.projects.occ import OCCResourceMixin
from tests import factories as f
from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals
from unittest import mock
import pytest
pytestmark = pytest.mark.django_db
@ -130,6 +133,7 @@ def test_user_story_update(client, data):
data.project_owner
]
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version") as _validate_and_update_version_mock:
user_story_data = UserStorySerializer(data.public_user_story).data
user_story_data["subject"] = "test"
user_story_data = json.dumps(user_story_data)
@ -235,6 +239,7 @@ def test_user_story_patch(client, data):
data.project_owner
]
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version") as _validate_and_update_version_mock:
patch_data = json.dumps({"subject": "test", "version": data.public_user_story.version})
results = helper_test_http_method(client, 'patch', public_url, patch_data, users)
assert results == [401, 403, 403, 200, 200]

View File

@ -4,10 +4,13 @@ from taiga.base.utils import json
from taiga.projects.wiki.serializers import WikiPageSerializer, WikiLinkSerializer
from taiga.projects.wiki.models import WikiPage, WikiLink
from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS
from taiga.projects.occ import OCCResourceMixin
from tests import factories as f
from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals
from unittest import mock
import pytest
pytestmark = pytest.mark.django_db
@ -121,6 +124,7 @@ def test_wiki_page_update(client, data):
data.project_owner
]
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version") as _validate_and_update_version_mock:
wiki_page_data = WikiPageSerializer(data.public_wiki_page).data
wiki_page_data["content"] = "test"
wiki_page_data = json.dumps(wiki_page_data)
@ -238,6 +242,7 @@ def test_wiki_page_patch(client, data):
data.project_owner
]
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version") as _validate_and_update_version_mock:
patch_data = json.dumps({"content": "test", "version": data.public_wiki_page.version})
results = helper_test_http_method(client, 'patch', public_url, patch_data, users)
assert results == [401, 200, 200, 200, 200]
@ -300,6 +305,7 @@ def test_wiki_link_update(client, data):
data.project_owner
]
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version") as _validate_and_update_version_mock:
wiki_link_data = WikiLinkSerializer(data.public_wiki_link).data
wiki_link_data["title"] = "test"
wiki_link_data = json.dumps(wiki_link_data)
@ -417,6 +423,7 @@ def test_wiki_link_patch(client, data):
data.project_owner
]
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version") as _validate_and_update_version_mock:
patch_data = json.dumps({"title": "test"})
results = helper_test_http_method(client, 'patch', public_url, patch_data, users)
assert results == [401, 200, 200, 200, 200]

View File

@ -41,7 +41,7 @@ def test_invalid_concurrent_save_for_issue(client):
mock_path = "taiga.projects.issues.api.IssueViewSet.pre_conditions_on_save"
with patch(mock_path) as m:
url = reverse("issues-detail", args=(issue.id,))
data = {}
data = {"version":9}
response = client.patch(url, json.dumps(data), content_type="application/json")
assert response.status_code == 400
@ -71,7 +71,7 @@ def test_invalid_concurrent_save_for_wiki_page(client):
client.login(user)
url = reverse("wiki-detail", args=(wiki_page.id,))
data = {}
data = {"version":9}
response = client.patch(url, json.dumps(data), content_type="application/json")
assert response.status_code == 400
@ -98,7 +98,7 @@ def test_invalid_concurrent_save_for_us(client):
client.login(user)
url = reverse("userstories-detail", args=(userstory.id,))
data = {}
data = {"version":9}
response = client.patch(url, json.dumps(data), content_type="application/json")
assert response.status_code == 400
@ -143,7 +143,7 @@ def test_invalid_concurrent_save_for_task(client):
mock_path = "taiga.projects.tasks.api.TaskViewSet.pre_conditions_on_save"
with patch(mock_path) as m:
url = reverse("tasks-detail", args=(task.id,))
data = {}
data = {"version":9}
response = client.patch(url, json.dumps(data), content_type="application/json")
assert response.status_code == 400

View File

@ -149,7 +149,7 @@ def test_update_userstory_points(client):
# Api should save successful
data = {}
data["version"] = usdata["version"]
data["version"] = usdata["version"] + 1
data["points"] = copy.copy(usdata["points"])
data["points"].update({str(role1.pk):points3.pk})