Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add database constraints to enforce unique version numbers #1538

Merged
merged 1 commit into from
Jun 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions integreat_cms/cms/migrations/0031_unique_version_constraint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Generated by Django 3.2.13 on 2022-06-09 20:00

from django.db import migrations, models


class Migration(migrations.Migration):
"""
Ensure version numbers are unique for all content objects in each language.
"""

dependencies = [
("cms", "0030_alter_region_aliases"),
]

operations = [
migrations.AddConstraint(
model_name="eventtranslation",
constraint=models.UniqueConstraint(
fields=("event", "language", "version"),
name="eventtranslation_unique_version",
),
),
migrations.AddConstraint(
model_name="imprintpagetranslation",
constraint=models.UniqueConstraint(
fields=("page", "language", "version"),
name="imprintpagetranslation_unique_version",
),
),
migrations.AddConstraint(
model_name="pagetranslation",
constraint=models.UniqueConstraint(
fields=("page", "language", "version"),
name="pagetranslation_unique_version",
),
),
migrations.AddConstraint(
model_name="poitranslation",
constraint=models.UniqueConstraint(
fields=("poi", "language", "version"),
name="poitranslation_unique_version",
),
),
]
7 changes: 7 additions & 0 deletions integreat_cms/cms/models/events/event_translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,10 @@ class Meta:
default_permissions = ()
#: The fields which are used to sort the returned objects of a QuerySet
ordering = ["event__pk", "language__pk", "-version"]
#: A list of database constraints for this model
constraints = [
models.UniqueConstraint(
fields=["event", "language", "version"],
name="%(class)s_unique_version",
),
]
7 changes: 7 additions & 0 deletions integreat_cms/cms/models/pages/imprint_page_translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,10 @@ class Meta:
default_permissions = ()
#: The fields which are used to sort the returned objects of a QuerySet
ordering = ["page__pk", "language__pk", "-version"]
#: A list of database constraints for this model
constraints = [
models.UniqueConstraint(
fields=["page", "language", "version"],
name="%(class)s_unique_version",
),
]
7 changes: 7 additions & 0 deletions integreat_cms/cms/models/pages/page_translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,3 +338,10 @@ class Meta:
default_permissions = ()
#: The fields which are used to sort the returned objects of a QuerySet
ordering = ["page__pk", "language__pk", "-version"]
#: A list of database constraints for this model
constraints = [
models.UniqueConstraint(
fields=["page", "language", "version"],
name="%(class)s_unique_version",
),
]
7 changes: 7 additions & 0 deletions integreat_cms/cms/models/pois/poi_translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,10 @@ class Meta:
default_permissions = ()
#: The fields which are used to sort the returned objects of a QuerySet
ordering = ["poi__pk", "language__pk", "-version"]
#: A list of database constraints for this model
constraints = [
models.UniqueConstraint(
fields=["poi", "language", "version"],
name="%(class)s_unique_version",
),
]
2 changes: 1 addition & 1 deletion integreat_cms/locale/de/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 1.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-06-08 20:12+0000\n"
"POT-Creation-Date: 2022-06-09 22:40+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Integreat <[email protected]>\n"
"Language-Team: Integreat <[email protected]>\n"
Expand Down
69 changes: 41 additions & 28 deletions tests/xliff/xliff_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,31 +138,42 @@ def test_xliff_import(login_role_user, settings):
import_path = "tests/xliff/files/import"
file_1 = "augsburg_de_en_1_2_willkommen.xliff"
file_2 = "augsburg_de_en_2_1_willkommen-in-augsburg.xliff"
# Upload once for successful import
with open(f"{import_path}/{file_1}", encoding="utf-8") as f1:
with open(f"{import_path}/{file_2}", encoding="utf-8") as f2:
response = client.post(
response1 = client.post(
upload_xliff, data={"xliff_file": [f1, f2]}, format="multipart"
)
print(response.headers)
print(response1.headers)
# Upload second time to test unchanged import
with open(f"{import_path}/{file_1}", encoding="utf-8") as f1:
with open(f"{import_path}/{file_2}", encoding="utf-8") as f2:
response2 = client.post(
upload_xliff, data={"xliff_file": [f1, f2]}, format="multipart"
)
print(response2.headers)

if role in PRIV_STAFF_ROLES + [MANAGEMENT, EDITOR, AUTHOR]:
# If the role should be allowed to access the view, we expect a successful result
assert response.status_code == 302
page_tree = reverse(
"pages", kwargs={"region_slug": "augsburg", "language_slug": "en"}
)
redirect_location = response.headers.get("Location")
# If errors occur, we get redirected to the page tree
assert redirect_location != page_tree
# Check if xliff import view is correctly rendered
response = client.get(redirect_location)
print(response.headers)
assert response.status_code == 200
assert file_1 in response.content.decode("utf-8")
assert file_2 in response.content.decode("utf-8")
# Check if XLIFF import is correctly confirmed
# (perform test twice to check whether unchanged diffs can be imported as well)
for msg in ["successfully", "without changes"]:
# Perform test twice to check whether unchanged diffs can be imported as well
for response, msg in [
(response1, "successfully"),
(response2, "without changes"),
]:
# If the role should be allowed to access the view, we expect a successful result
assert response.status_code == 302
page_tree = reverse(
"pages", kwargs={"region_slug": "augsburg", "language_slug": "en"}
)
redirect_location = response.headers.get("Location")
# If errors occur, we get redirected to the page tree
assert redirect_location != page_tree
# Check if xliff import view is correctly rendered
response = client.get(redirect_location)
print(response.headers)
assert response.status_code == 200
assert file_1 in response.content.decode("utf-8")
assert file_2 in response.content.decode("utf-8")
# Check if XLIFF import is correctly confirmed
response = client.post(redirect_location)
print(response.headers)
assert response.status_code == 302
Expand All @@ -183,7 +194,7 @@ def test_xliff_import(login_role_user, settings):
in response.content.decode("utf-8")
)
if translation.version > 1:
# If a translation already exists for this version, asser that the status is inherited
# If a translation already exists for this version, assert that the status is inherited
previous_translation = page.translations.get(
language__slug="en", version=translation.version - 1
)
Expand All @@ -192,12 +203,14 @@ def test_xliff_import(login_role_user, settings):
# Else, the status should be inherited from the source translation
assert translation.source_translation.status == translation.status
elif role == ANONYMOUS:
# For anonymous users, we want to redirect to the login form instead of showing an error
assert response.status_code == 302
assert (
response.headers.get("location")
== f"{settings.LOGIN_URL}?next={upload_xliff}"
)
for response in [response1, response2]:
# For anonymous users, we want to redirect to the login form instead of showing an error
assert response.status_code == 302
assert (
response.headers.get("location")
== f"{settings.LOGIN_URL}?next={upload_xliff}"
)
else:
# For logged in users, we want to show an error if they get a permission denied
assert response.status_code == 403
for response in [response1, response2]:
# For logged in users, we want to show an error if they get a permission denied
assert response.status_code == 403