Skip to content
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
3 changes: 0 additions & 3 deletions backend/src/baserow/contrib/automation/history/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ class AutomationWorkflowHistory(AutomationHistory):
"automation.AutomationWorkflow",
on_delete=models.CASCADE,
related_name="workflow_histories",
# TODO ZDM: Make non-nullable after next release and add backfill
# migration. See: https://github.com/baserow/baserow/issues/5236
null=True,
)
workflow = models.ForeignKey(
"automation.AutomationWorkflow",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Generated by Django 5.2.13 on 2026-05-07 07:08

import django.db.models.deletion
from django.db import migrations, models


def backfill_original_workflow(apps, schema_editor):
AutomationWorkflowHistory = apps.get_model(
"automation", "AutomationWorkflowHistory"
)
AutomationWorkflowHistory.objects.filter(original_workflow__isnull=True).update(
original_workflow_id=models.F("workflow_id")
)


class Migration(migrations.Migration):

dependencies = [
('automation', '0028_automationworkflowhistory_original_workflow_and_more'),
]

operations = [
migrations.RunPython(
backfill_original_workflow,
reverse_code=migrations.RunPython.noop,
),
migrations.AlterField(
model_name='automationworkflowhistory',
name='original_workflow',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workflow_histories', to='automation.automationworkflow'),
),
]
6 changes: 0 additions & 6 deletions backend/src/baserow/contrib/builder/api/pages/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@
"The page id {e.page_id} does not belong to the builder.",
)

ERROR_PAGE_NAME_NOT_UNIQUE = (
"ERROR_PAGE_NAME_NOT_UNIQUE",
HTTP_400_BAD_REQUEST,
"The page name {e.name} already exists for your builder instance.",
)

ERROR_PAGE_PATH_NOT_UNIQUE = (
"ERROR_PAGE_PATH_NOT_UNIQUE",
HTTP_400_BAD_REQUEST,
Expand Down
6 changes: 0 additions & 6 deletions backend/src/baserow/contrib/builder/api/pages/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
ERROR_DUPLICATE_QUERY_PARAMS,
ERROR_INVALID_QUERY_PARAM_NAME,
ERROR_PAGE_DOES_NOT_EXIST,
ERROR_PAGE_NAME_NOT_UNIQUE,
ERROR_PAGE_NOT_IN_BUILDER,
ERROR_PAGE_PATH_NOT_UNIQUE,
ERROR_PATH_PARAM_NOT_DEFINED,
Expand All @@ -38,7 +37,6 @@
DuplicatePathParamsInPath,
InvalidQueryParamName,
PageDoesNotExist,
PageNameNotUnique,
PageNotInBuilder,
PagePathNotUnique,
PathParamNotDefined,
Expand Down Expand Up @@ -76,7 +74,6 @@ class PagesView(APIView):
400: get_error_schema(
[
"ERROR_REQUEST_BODY_VALIDATION",
"ERROR_PAGE_NAME_NOT_UNIQUE",
"ERROR_PAGE_PATH_NOT_UNIQUE",
"ERROR_PATH_PARAM_NOT_IN_PATH",
"ERROR_PATH_PARAM_NOT_DEFINED",
Expand All @@ -91,7 +88,6 @@ class PagesView(APIView):
@map_exceptions(
{
ApplicationDoesNotExist: ERROR_APPLICATION_DOES_NOT_EXIST,
PageNameNotUnique: ERROR_PAGE_NAME_NOT_UNIQUE,
PagePathNotUnique: ERROR_PAGE_PATH_NOT_UNIQUE,
PathParamNotInPath: ERROR_PATH_PARAM_NOT_IN_PATH,
PathParamNotDefined: ERROR_PATH_PARAM_NOT_DEFINED,
Expand Down Expand Up @@ -137,7 +133,6 @@ class PageView(APIView):
400: get_error_schema(
[
"ERROR_REQUEST_BODY_VALIDATION",
"ERROR_PAGE_NAME_NOT_UNIQUE",
"ERROR_PAGE_PATH_NOT_UNIQUE",
"ERROR_PATH_PARAM_NOT_IN_PATH",
"ERROR_PATH_PARAM_NOT_DEFINED",
Expand All @@ -156,7 +151,6 @@ class PageView(APIView):
{
ApplicationDoesNotExist: ERROR_APPLICATION_DOES_NOT_EXIST,
PageDoesNotExist: ERROR_PAGE_DOES_NOT_EXIST,
PageNameNotUnique: ERROR_PAGE_NAME_NOT_UNIQUE,
PagePathNotUnique: ERROR_PAGE_PATH_NOT_UNIQUE,
PathParamNotInPath: ERROR_PATH_PARAM_NOT_IN_PATH,
PathParamNotDefined: ERROR_PATH_PARAM_NOT_DEFINED,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 5.2.13 on 2026-05-07 08:31

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('builder', '0067_slackwritemessageworkflowaction'),
]

operations = [
migrations.AlterUniqueTogether(
name='page',
unique_together={('builder', 'path')},
),
]
14 changes: 0 additions & 14 deletions backend/src/baserow/contrib/builder/pages/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,6 @@ class SharedPageIsReadOnly(Exception):
"""Raised when trying to do something on shared page."""


class PageNameNotUnique(Exception):
"""Raised when a page is trying to be created with a name that already exists"""

def __init__(self, name=None, builder_id=None, *args, **kwargs):
self.name = name
self.builder_id = builder_id
super().__init__(
f"A page with the name {name} already exists in the builder with id "
f"{builder_id}",
*args,
**kwargs,
)


class PagePathNotUnique(Exception):
"""Raised when a page is trying to be created with a path that already exists"""

Expand Down
5 changes: 0 additions & 5 deletions backend/src/baserow/contrib/builder/pages/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
DuplicatePathParamsInPath,
InvalidQueryParamName,
PageDoesNotExist,
PageNameNotUnique,
PageNotInBuilder,
PagePathNotUnique,
PathParamNotDefined,
Expand Down Expand Up @@ -144,8 +143,6 @@ def create_page(
shared=shared,
)
except IntegrityError as e:
if "unique constraint" in e.args[0] and "name" in e.args[0]:
raise PageNameNotUnique(name=name, builder_id=builder.id)
if "unique constraint" in e.args[0] and "path" in e.args[0]:
raise PagePathNotUnique(path=path, builder_id=builder.id)
raise e
Expand Down Expand Up @@ -203,8 +200,6 @@ def update_page(self, page: Page, **kwargs) -> Page:
try:
page.save()
except IntegrityError as e:
if is_unique_violation_error(e) and "name" in e.args[0]:
raise PageNameNotUnique(name=page.name, builder_id=page.builder_id)
if is_unique_violation_error(e) and "path" in e.args[0]:
raise PagePathNotUnique(path=page.path, builder_id=page.builder_id)
raise e
Expand Down
2 changes: 1 addition & 1 deletion backend/src/baserow/contrib/builder/pages/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class Meta:
"-shared", # First page is the shared one if any.
"order",
)
unique_together = [["builder", "name"], ["builder", "path"]]
unique_together = [["builder", "path"]]
indexes = [
models.Index(fields=["-shared", "order"]),
models.Index(fields=["builder", "-shared", "order"]),
Expand Down
17 changes: 11 additions & 6 deletions backend/tests/baserow/contrib/builder/api/pages/test_page_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
HTTP_404_NOT_FOUND,
)

from baserow.contrib.builder.pages.models import Page


@pytest.mark.django_db
def test_create_page(api_client, data_fixture):
Expand Down Expand Up @@ -182,8 +184,9 @@ def test_create_page_duplicate_page_name(api_client, data_fixture):
HTTP_AUTHORIZATION=f"JWT {token}",
)

assert response.status_code == HTTP_400_BAD_REQUEST
assert response.json()["error"] == "ERROR_PAGE_NAME_NOT_UNIQUE"
assert response.status_code == HTTP_200_OK
duplicated_page = Page.objects.get(id=response.json()["id"])
assert duplicated_page.name == page.name


@pytest.mark.django_db
Expand Down Expand Up @@ -387,8 +390,9 @@ def test_update_page_duplicate_page_name(api_client, data_fixture):
HTTP_AUTHORIZATION=f"JWT {token}",
)

assert response.status_code == HTTP_400_BAD_REQUEST
assert response.json()["error"] == "ERROR_PAGE_NAME_NOT_UNIQUE"
assert response.status_code == HTTP_200_OK
page_two.refresh_from_db()
assert page_two.name == page.name


@pytest.mark.django_db
Expand Down Expand Up @@ -728,5 +732,6 @@ def test_rename_page_using_existing_page_name(api_client, data_fixture):
HTTP_AUTHORIZATION=f"JWT {token}",
)

assert response.status_code == HTTP_400_BAD_REQUEST
assert response.json()["error"] == "ERROR_PAGE_NAME_NOT_UNIQUE"
assert response.status_code == HTTP_200_OK
page_2.refresh_from_db()
assert page_2.name == "test1"
11 changes: 5 additions & 6 deletions backend/tests/baserow/contrib/builder/pages/test_page_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
DuplicatePathParamsInPath,
InvalidQueryParamName,
PageDoesNotExist,
PageNameNotUnique,
PageNotInBuilder,
PagePathNotUnique,
PathParamNotDefined,
Expand Down Expand Up @@ -59,8 +58,8 @@ def test_create_page(data_fixture):
def test_create_page_page_name_not_unique(data_fixture):
page = data_fixture.create_builder_page(name="test", path="/test")

with pytest.raises(PageNameNotUnique):
PageHandler().create_page(page.builder, name="test", path="/new")
new_page = PageHandler().create_page(page.builder, name="test", path="/new")
assert new_page.name == page.name


@pytest.mark.django_db
Expand Down Expand Up @@ -152,9 +151,9 @@ def test_update_shared_page(data_fixture):
def test_update_page_page_name_not_unique(data_fixture):
page = data_fixture.create_builder_page(name="test")
page_two = data_fixture.create_builder_page(builder=page.builder, name="test2")

with pytest.raises(PageNameNotUnique):
PageHandler().update_page(page_two, name=page.name)
PageHandler().update_page(page_two, name=page.name)
page_two.refresh_from_db()
assert page_two.name == page.name


@pytest.mark.django_db
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "refactor",
"message": "Removed the unique constraint on Builder page names.",
"issue_origin": "github",
"issue_number": 5042,
"domain": "builder",
"bullet_points": [],
"created_at": "2026-05-07"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "refactor",
"message": "Make AutomationWorkflowHistory.original_workflow non-nullable.",
"issue_origin": "github",
"issue_number": 5236,
"domain": "automation",
"bullet_points": [],
"created_at": "2026-05-07"
}
2 changes: 1 addition & 1 deletion e2e-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"@faker-js/faker": "7.6.0",
"@nuxt/test-utils": "^3.21.0",
"@playwright/test": "^1.48.0",
"axios": "1.15.0",
"axios": "1.15.2",
"dotenv": "16.0.3"
}
}
8 changes: 4 additions & 4 deletions e2e-tests/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -277,10 +277,10 @@ asynckit@^0.4.0:
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==

axios@1.15.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.15.0.tgz#0fcee91ef03d386514474904b27863b2c683bf4f"
integrity sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==
axios@1.15.2:
version "1.15.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.15.2.tgz#eb8fb6d30349abace6ade5b4cb4d9e8a0dc23e5b"
integrity sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==
dependencies:
follow-redirects "^1.15.11"
form-data "^4.0.5"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,9 +282,6 @@ export default {
addQueryParam(newParam) {
this.localQueryParams.push(newParam)
},
isNameUnique(name) {
return !this.pageNames.includes(name) || name === this.page?.name
},
isPathUnique(path) {
const pathGeneralised = this.generalisePath(path)
return (
Expand Down Expand Up @@ -340,10 +337,6 @@ export default {
this.$t('error.requiredField'),
required
),
isUnique: helpers.withMessage(
this.$t('pageErrors.errorNameNotUnique'),
this.isNameUnique
),
maxLength: helpers.withMessage(
this.$t('error.maxLength', { max: 255 }),
maxLength(225)
Expand Down
2 changes: 0 additions & 2 deletions web-frontend/modules/builder/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@
"submit": "Add page"
},
"pageErrors": {
"errorNameNotUnique": "A page with this name already exists",
"errorNameNotUniqueDescription": "Please enter a unique name for the page",
"errorPathNotUnique": "A path with this name already exists",
"errorStartingSlash": "A path needs to start with a '/'",
"errorValidPathCharacters": "The path contains invalid characters",
Expand Down
8 changes: 1 addition & 7 deletions web-frontend/modules/builder/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,15 +158,9 @@ export default defineNuxtPlugin({
name: 'builder',
dependsOn: ['core', 'store'],
async setup(nuxtApp) {
const { $store, $registry, $clientErrorMap, $i18n } = nuxtApp
const { $store, $registry } = nuxtApp
const context = { app: nuxtApp }

$clientErrorMap.setError(
'ERROR_PAGE_NAME_NOT_UNIQUE',
$i18n.t('pageErrors.errorNameNotUnique'),
$i18n.t('pageErrors.errorNameNotUniqueDescription')
)

$store.registerModuleNuxtSafe('page', pageStore)
$store.registerModuleNuxtSafe('element', elementStore)
$store.registerModuleNuxtSafe('domain', domainStore)
Expand Down
Loading