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
2 changes: 2 additions & 0 deletions backend/src/baserow/contrib/automation/api/workflows/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ class AsyncPublishAutomationWorkflowView(APIView):
400: get_error_schema(
[
"ERROR_REQUEST_BODY_VALIDATION",
"ERROR_MAX_JOB_COUNT_EXCEEDED",
]
),
404: get_error_schema(["ERROR_AUTOMATION_WORKFLOW_DOES_NOT_EXIST"]),
Expand All @@ -388,6 +389,7 @@ class AsyncPublishAutomationWorkflowView(APIView):
@map_exceptions(
{
AutomationWorkflowDoesNotExist: ERROR_AUTOMATION_WORKFLOW_DOES_NOT_EXIST,
MaxJobCountExceeded: ERROR_MAX_JOB_COUNT_EXCEEDED,
}
)
def post(self, request, workflow_id: int):
Expand Down
4 changes: 4 additions & 0 deletions backend/src/baserow/contrib/builder/api/domains/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
validate_body,
validate_body_custom_fields,
)
from baserow.api.jobs.errors import ERROR_MAX_JOB_COUNT_EXCEEDED
from baserow.api.jobs.serializers import JobSerializer
from baserow.api.schemas import CLIENT_SESSION_ID_SCHEMA_PARAMETER, get_error_schema
from baserow.api.utils import (
Expand Down Expand Up @@ -49,6 +50,7 @@
from baserow.contrib.builder.exceptions import BuilderDoesNotExist
from baserow.contrib.builder.handler import BuilderHandler
from baserow.core.exceptions import ApplicationDoesNotExist
from baserow.core.jobs.exceptions import MaxJobCountExceeded
from baserow.core.jobs.registries import job_type_registry


Expand Down Expand Up @@ -331,6 +333,7 @@ class AsyncPublishDomainView(APIView):
400: get_error_schema(
[
"ERROR_REQUEST_BODY_VALIDATION",
"ERROR_MAX_JOB_COUNT_EXCEEDED",
]
),
404: get_error_schema(["ERROR_APPLICATION_DOES_NOT_EXIST"]),
Expand All @@ -341,6 +344,7 @@ class AsyncPublishDomainView(APIView):
{
ApplicationDoesNotExist: ERROR_APPLICATION_DOES_NOT_EXIST,
DomainDoesNotExist: ERROR_DOMAIN_DOES_NOT_EXIST,
MaxJobCountExceeded: ERROR_MAX_JOB_COUNT_EXCEEDED,
}
)
def post(self, request, domain_id: int):
Expand Down
6 changes: 3 additions & 3 deletions backend/src/baserow/contrib/database/api/tokens/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ class TokensView(APIView):
def get(self, request):
"""Lists all the tokens of a user."""

tokens = Token.objects.filter(user=request.user).prefetch_related(
"tokenpermission_set"
)
tokens = Token.objects.filter(
user=request.user, workspace__workspaceuser__user=request.user
).prefetch_related("tokenpermission_set")
serializer = TokenSerializer(tokens, many=True)
return Response(serializer.data)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("database", "0207_fix_data_sync_missing_primary_field"),
]

operations = [
migrations.AddField(
model_name="gridview",
name="frozen_column_count",
field=models.PositiveSmallIntegerField(
default=1,
db_default=1,
),
),
]
3 changes: 3 additions & 0 deletions backend/src/baserow/contrib/database/views/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,9 @@ class RowHeightSizes(models.TextChoices):
max_length=10,
db_default="small",
)
# Number of frozen (pinned) columns including the primary field. Max defined in
# the serializer.
frozen_column_count = models.PositiveSmallIntegerField(default=1, db_default=1)


class GridViewFieldOptionsManager(models.Manager):
Expand Down
18 changes: 16 additions & 2 deletions backend/src/baserow/contrib/database/views/view_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,28 @@ class GridViewType(ViewType):
has_public_info = True
can_group_by = True
when_shared_publicly_requires_realtime_events = True
allowed_fields = ["row_identifier_type", "row_height_size"]
allowed_fields = ["row_identifier_type", "row_height_size", "frozen_column_count"]
field_options_allowed_fields = [
"width",
"hidden",
"order",
"aggregation_type",
"aggregation_raw_type",
]
serializer_field_names = ["row_identifier_type", "row_height_size"]
serializer_field_names = [
"row_identifier_type",
"row_height_size",
"frozen_column_count",
]
serializer_field_overrides = {
"frozen_column_count": serializers.IntegerField(
min_value=0,
max_value=4,
required=False,
default=1,
help_text="Number of frozen columns including the primary field.",
),
}

api_exceptions_map = {
GridViewAggregationDoesNotSupportField: ERROR_AGGREGATION_DOES_NOT_SUPPORTED_FIELD,
Expand Down Expand Up @@ -123,6 +136,7 @@ def export_serialized(
)
serialized["row_identifier_type"] = grid.row_identifier_type
serialized["row_height_size"] = grid.row_height_size
serialized["frozen_column_count"] = grid.frozen_column_count

serialized_field_options = []
for field_option in grid.get_field_options():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,43 @@ def test_publish_workflow(api_client, data_fixture):
}


@pytest.mark.django_db
def test_publish_workflow_error_max_job_count_exceeded(api_client, data_fixture):
user = data_fixture.create_user()
automation = data_fixture.create_automation_application(user)
workflow_1 = data_fixture.create_automation_workflow(
user, automation=automation, name="test1"
)
workflow_2 = data_fixture.create_automation_workflow(
user, automation=automation, name="test2"
)
workflow_3 = data_fixture.create_automation_workflow(
user, automation=automation, name="test3"
)

token = data_fixture.generate_token(user)
for workflow_id in [workflow_1.id, workflow_2.id]:
url = reverse(API_URL_WORKFLOW_PUBLISH, kwargs={"workflow_id": workflow_id})
response = api_client.post(
url,
format="json",
HTTP_AUTHORIZATION=f"JWT {token}",
)
assert response.status_code == HTTP_202_ACCEPTED

url = reverse(API_URL_WORKFLOW_PUBLISH, kwargs={"workflow_id": workflow_3.id})
response = api_client.post(
url,
format="json",
HTTP_AUTHORIZATION=f"JWT {token}",
)
assert response.status_code == HTTP_400_BAD_REQUEST
assert response.json() == {
"detail": "Max running job count for this type is exceeded.",
"error": "ERROR_MAX_JOB_COUNT_EXCEEDED",
}


@pytest.mark.django_db
def test_publish_workflow_error_invalid_workflow(api_client, data_fixture):
_, token = data_fixture.create_user_and_token()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,41 @@ def test_publish_builder(mock_run_async_job, api_client, data_fixture):
assert args[0][0] == response_json["id"]


@pytest.mark.django_db(transaction=True)
@patch("baserow.core.jobs.handler.run_async_job")
def test_publish_builder_max_job_count_exceeded(
mock_run_async_job, api_client, data_fixture
):
user, token = data_fixture.create_user_and_token()
builder = data_fixture.create_builder_application(user=user)
data_fixture.create_builder_page(builder=builder, user=user)
domain = data_fixture.create_builder_custom_domain(
domain_name="test.getbaserow.io", builder=builder
)
url = reverse(
"api:builder:domains:publish",
kwargs={"domain_id": domain.id},
)

# Simulate publishing 2 builder apps. Since we're patching run_async_job,
# the jobs will never complete.
api_client.post(
url, {"domain_id": domain.id}, format="json", HTTP_AUTHORIZATION=f"JWT {token}"
)
api_client.post(
url, {"domain_id": domain.id}, format="json", HTTP_AUTHORIZATION=f"JWT {token}"
)
response = api_client.post(
url,
{"domain_id": domain.id},
format="json",
HTTP_AUTHORIZATION=f"JWT {token}",
)

assert response.status_code == HTTP_400_BAD_REQUEST
assert response.json()["error"] == "ERROR_MAX_JOB_COUNT_EXCEEDED"


@pytest.mark.django_db
def test_get_elements_of_public_builder(api_client, data_fixture):
user = data_fixture.create_user()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ def test_to_baserow_database_export():
"order": 1,
"row_identifier_type": "count",
"row_height_size": "small",
"frozen_column_count": 1,
"filter_type": "AND",
"filters_disabled": False,
"filters": [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ def test_import_grid_view():
"ownership_type": "collaborative",
"public": False,
"row_height_size": "medium",
"frozen_column_count": 1,
"row_identifier_type": "count",
"sortings": [],
"type": "grid",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,35 @@ def test_list_tokens(api_client, data_fixture):
}


@pytest.mark.django_db
def test_list_tokens_excludes_tokens_for_workspaces_user_left(api_client, data_fixture):
user, jwt_token = data_fixture.create_user_and_token()
workspace_1 = data_fixture.create_workspace(user=user)
workspace_2 = data_fixture.create_workspace(user=user)
token_1 = data_fixture.create_token(user=user, workspace=workspace_1)
data_fixture.create_token(user=user, workspace=workspace_2)

url = reverse("api:database:tokens:list")

response = api_client.get(url, HTTP_AUTHORIZATION=f"JWT {jwt_token}")
assert response.status_code == HTTP_200_OK
assert len(response.json()) == 2

workspace_2.workspaceuser_set.filter(user=user).delete()

response = api_client.get(url, HTTP_AUTHORIZATION=f"JWT {jwt_token}")
assert response.status_code == HTTP_200_OK
response_json = response.json()
assert len(response_json) == 1
assert response_json[0]["id"] == token_1.id

data_fixture.create_user_workspace(user=user, workspace=workspace_2)

response = api_client.get(url, HTTP_AUTHORIZATION=f"JWT {jwt_token}")
assert response.status_code == HTTP_200_OK
assert len(response.json()) == 2


@pytest.mark.django_db
def test_create_token(api_client, data_fixture):
user, token = data_fixture.create_user_and_token()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3349,6 +3349,7 @@ def test_get_public_grid_view(api_client, data_fixture):
"type": "grid",
"row_identifier_type": grid_view.row_identifier_type,
"row_height_size": grid_view.row_height_size,
"frozen_column_count": 1,
"show_logo": True,
"allow_public_export": False,
"ownership_type": "collaborative",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,7 @@ def test_user_with_password_can_get_info_about_a_public_password_protected_view(
"type": "grid",
"row_identifier_type": grid_view.row_identifier_type,
"row_height_size": grid_view.row_height_size,
"frozen_column_count": 1,
"show_logo": grid_view.show_logo,
"allow_public_export": grid_view.allow_public_export,
"ownership_type": "collaborative",
Expand Down Expand Up @@ -1048,6 +1049,7 @@ def test_user_with_password_can_get_info_about_a_public_password_protected_view(
"type": "grid",
"row_identifier_type": grid_view.row_identifier_type,
"row_height_size": grid_view.row_height_size,
"frozen_column_count": 1,
"show_logo": grid_view.show_logo,
"allow_public_export": grid_view.allow_public_export,
"ownership_type": "collaborative",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def test_view_created_event_type(data_fixture):
"public": False,
"slug": view.slug,
"row_height_size": "small",
"frozen_column_count": 1,
},
}

Expand Down Expand Up @@ -93,6 +94,7 @@ def test_view_created_event_type_test_payload(data_fixture):
"row_identifier_type": "id",
"public": False,
"row_height_size": "small",
"frozen_column_count": 1,
},
}

Expand Down Expand Up @@ -147,6 +149,7 @@ def test_view_updated_event_type(data_fixture):
"public": False,
"slug": view.slug,
"row_height_size": "small",
"frozen_column_count": 1,
},
}

Expand Down Expand Up @@ -190,6 +193,7 @@ def test_view_updated_event_type_test_payload(data_fixture):
"row_identifier_type": "id",
"public": False,
"row_height_size": "small",
"frozen_column_count": 1,
},
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "bug",
"message": "Allow advanced formula for JSON body content of HTTP request node",
"issue_origin": "github",
"issue_number": 5002,
"domain": "integration",
"bullet_points": [],
"created_at": "2026-04-08"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "bug",
"message": "Filter tokens by workspace membership",
"issue_origin": "github",
"issue_number": 5144,
"domain": "core",
"bullet_points": [],
"created_at": "2026-04-08"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "bug",
"message": "Ensure that the correct error is shown when exceeding the max publish job count.",
"issue_origin": "github",
"issue_number": null,
"domain": "builder",
"bullet_points": [],
"created_at": "2026-03-30"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "bug",
"message": "Ensure the correct error is shown when a page is not found.",
"issue_origin": "github",
"issue_number": null,
"domain": "builder",
"bullet_points": [],
"created_at": "2026-03-30"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "bug",
"message": "Ensure the max publish job count error is shown when exceeding the limit for publishing an Automation Workflow.",
"issue_origin": "github",
"issue_number": null,
"domain": "automation",
"bullet_points": [],
"created_at": "2026-04-01"
}
9 changes: 9 additions & 0 deletions changelog/entries/unreleased/bug/fix_link_row_checkbox.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "bug",
"message": "Fix linked row modal checkbox",
"issue_origin": null,
"issue_number": null,
"domain": "database",
"bullet_points": [],
"created_at": "2026-04-08"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "bug",
"message": "Fixed a bug where an unknown timezone could cause a data source dispatch to fail.",
"issue_origin": "github",
"issue_number": null,
"domain": "builder",
"bullet_points": [],
"created_at": "2026-03-31"
}
Loading
Loading