Detecting concurrent edition and failing properly if needed

remotes/origin/enhancement/email-actions
Alejandro Alonso 2014-11-28 14:53:33 +01:00 committed by Jesús Espino
parent 29df487bca
commit 53871e5a8a
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 Rest Framework resource mixin for resources that need to have concurrent
accesses and editions controlled. accesses and editions controlled.
""" """
def pre_save(self, obj): def _validate_and_update_version(self, obj):
current_version = obj.version current_version = None
param_version = self.request.DATA.get('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"}) raise exc.WrongArguments({"version": "The version doesn't match with the current one"})
if obj.id: if obj.id:
obj.version = models.F('version') + 1 obj.version = models.F('version') + 1
def pre_save(self, obj):
self._validate_and_update_version(obj)
super().pre_save(obj) super().pre_save(obj)
def post_save(self, obj, created=False): 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 import factories as f
from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals
from taiga.projects.votes.services import add_vote from taiga.projects.votes.services import add_vote
from taiga.projects.occ import OCCResourceMixin
from unittest import mock
import pytest import pytest
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
@ -132,6 +135,7 @@ def test_issue_update(client, data):
data.project_owner 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 = IssueSerializer(data.public_issue).data
issue_data["subject"] = "test" issue_data["subject"] = "test"
issue_data = json.dumps(issue_data) issue_data = json.dumps(issue_data)
@ -162,6 +166,7 @@ def test_issue_delete(client, data):
data.project_member_without_perms, data.project_member_without_perms,
data.project_member_with_perms, data.project_member_with_perms,
] ]
results = helper_test_http_method(client, 'delete', public_url, None, users) results = helper_test_http_method(client, 'delete', public_url, None, users)
assert results == [401, 403, 403, 204] assert results == [401, 403, 403, 204]
results = helper_test_http_method(client, 'delete', private_url1, None, users) results = helper_test_http_method(client, 'delete', private_url1, None, users)
@ -261,6 +266,7 @@ def test_issue_patch(client, data):
data.project_owner 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}) patch_data = json.dumps({"subject": "test", "version": data.public_issue.version})
results = helper_test_http_method(client, 'patch', public_url, patch_data, users) results = helper_test_http_method(client, 'patch', public_url, patch_data, users)
assert results == [401, 403, 403, 200, 200] assert results == [401, 403, 403, 200, 200]
@ -303,7 +309,6 @@ def test_issue_bulk_create(client, data):
data.project_owner data.project_owner
] ]
bulk_data = json.dumps({"bulk_issues": "test1\ntest2", bulk_data = json.dumps({"bulk_issues": "test1\ntest2",
"project_id": data.public_issue.project.pk}) "project_id": data.public_issue.project.pk})
results = helper_test_http_method(client, 'post', url, bulk_data, users) 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.base.utils import json
from taiga.projects.tasks.serializers import TaskSerializer from taiga.projects.tasks.serializers import TaskSerializer
from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS 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 import factories as f
from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals
from unittest import mock
import pytest import pytest
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
@ -132,6 +135,7 @@ def test_task_update(client, data):
data.project_owner 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 = TaskSerializer(data.public_task).data
task_data["subject"] = "test" task_data["subject"] = "test"
task_data = json.dumps(task_data) task_data = json.dumps(task_data)
@ -252,6 +256,7 @@ def test_task_patch(client, data):
data.project_owner 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}) patch_data = json.dumps({"subject": "test", "version": data.public_task.version})
results = helper_test_http_method(client, 'patch', public_url, patch_data, users) results = helper_test_http_method(client, 'patch', public_url, patch_data, users)
assert results == [401, 403, 403, 200, 200] 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.base.utils import json
from taiga.projects.userstories.serializers import UserStorySerializer from taiga.projects.userstories.serializers import UserStorySerializer
from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS 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 import factories as f
from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals
from unittest import mock
import pytest import pytest
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
@ -130,6 +133,7 @@ def test_user_story_update(client, data):
data.project_owner 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 = UserStorySerializer(data.public_user_story).data
user_story_data["subject"] = "test" user_story_data["subject"] = "test"
user_story_data = json.dumps(user_story_data) user_story_data = json.dumps(user_story_data)
@ -235,6 +239,7 @@ def test_user_story_patch(client, data):
data.project_owner 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}) patch_data = json.dumps({"subject": "test", "version": data.public_user_story.version})
results = helper_test_http_method(client, 'patch', public_url, patch_data, users) results = helper_test_http_method(client, 'patch', public_url, patch_data, users)
assert results == [401, 403, 403, 200, 200] 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.serializers import WikiPageSerializer, WikiLinkSerializer
from taiga.projects.wiki.models import WikiPage, WikiLink from taiga.projects.wiki.models import WikiPage, WikiLink
from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS 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 import factories as f
from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals
from unittest import mock
import pytest import pytest
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
@ -121,6 +124,7 @@ def test_wiki_page_update(client, data):
data.project_owner 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 = WikiPageSerializer(data.public_wiki_page).data
wiki_page_data["content"] = "test" wiki_page_data["content"] = "test"
wiki_page_data = json.dumps(wiki_page_data) wiki_page_data = json.dumps(wiki_page_data)
@ -238,6 +242,7 @@ def test_wiki_page_patch(client, data):
data.project_owner 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}) patch_data = json.dumps({"content": "test", "version": data.public_wiki_page.version})
results = helper_test_http_method(client, 'patch', public_url, patch_data, users) results = helper_test_http_method(client, 'patch', public_url, patch_data, users)
assert results == [401, 200, 200, 200, 200] assert results == [401, 200, 200, 200, 200]
@ -300,6 +305,7 @@ def test_wiki_link_update(client, data):
data.project_owner 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 = WikiLinkSerializer(data.public_wiki_link).data
wiki_link_data["title"] = "test" wiki_link_data["title"] = "test"
wiki_link_data = json.dumps(wiki_link_data) wiki_link_data = json.dumps(wiki_link_data)
@ -417,6 +423,7 @@ def test_wiki_link_patch(client, data):
data.project_owner data.project_owner
] ]
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version") as _validate_and_update_version_mock:
patch_data = json.dumps({"title": "test"}) patch_data = json.dumps({"title": "test"})
results = helper_test_http_method(client, 'patch', public_url, patch_data, users) results = helper_test_http_method(client, 'patch', public_url, patch_data, users)
assert results == [401, 200, 200, 200, 200] 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" mock_path = "taiga.projects.issues.api.IssueViewSet.pre_conditions_on_save"
with patch(mock_path) as m: with patch(mock_path) as m:
url = reverse("issues-detail", args=(issue.id,)) url = reverse("issues-detail", args=(issue.id,))
data = {} data = {"version":9}
response = client.patch(url, json.dumps(data), content_type="application/json") response = client.patch(url, json.dumps(data), content_type="application/json")
assert response.status_code == 400 assert response.status_code == 400
@ -71,7 +71,7 @@ def test_invalid_concurrent_save_for_wiki_page(client):
client.login(user) client.login(user)
url = reverse("wiki-detail", args=(wiki_page.id,)) url = reverse("wiki-detail", args=(wiki_page.id,))
data = {} data = {"version":9}
response = client.patch(url, json.dumps(data), content_type="application/json") response = client.patch(url, json.dumps(data), content_type="application/json")
assert response.status_code == 400 assert response.status_code == 400
@ -98,7 +98,7 @@ def test_invalid_concurrent_save_for_us(client):
client.login(user) client.login(user)
url = reverse("userstories-detail", args=(userstory.id,)) url = reverse("userstories-detail", args=(userstory.id,))
data = {} data = {"version":9}
response = client.patch(url, json.dumps(data), content_type="application/json") response = client.patch(url, json.dumps(data), content_type="application/json")
assert response.status_code == 400 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" mock_path = "taiga.projects.tasks.api.TaskViewSet.pre_conditions_on_save"
with patch(mock_path) as m: with patch(mock_path) as m:
url = reverse("tasks-detail", args=(task.id,)) url = reverse("tasks-detail", args=(task.id,))
data = {} data = {"version":9}
response = client.patch(url, json.dumps(data), content_type="application/json") response = client.patch(url, json.dumps(data), content_type="application/json")
assert response.status_code == 400 assert response.status_code == 400

View File

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