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
79 changes: 79 additions & 0 deletions .github/workflows/trigger-helm-chart-upload.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: Publish Helm Chart

on:
push:
tags:
- "*"

jobs:
publish-helm-chart:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Helm
uses: azure/setup-helm@v4

- name: Package Helm Chart
id: package
run: |
cd deploy/helm
rm -f baserow/Chart.lock

# Add Helm repositories
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add caddy https://caddyserver.github.io/ingress

# Build dependencies
helm dependency build baserow

# Lint the chart with strict mode
helm lint baserow --strict

# Package the chart
helm package baserow

# Get chart details
CHART_FILE=$(ls baserow-*.tgz)
echo "chart-file=$CHART_FILE" >> $GITHUB_OUTPUT

HELM_CHART_VERSION=$(helm show chart baserow | grep '^version:' | awk '{print $2}')
echo "chart-version=$HELM_CHART_VERSION" >> $GITHUB_OUTPUT

echo "Packaged chart: $CHART_FILE (version: $HELM_CHART_VERSION)"

- name: Upload Helm Chart Artifact
uses: actions/upload-artifact@v4
with:
name: helm-chart
path: deploy/helm/baserow-*.tgz
retention-days: 30

- name: Generate GitHub App Token
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
id: generate-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.CHART_REPO_APP_ID }}
private-key: ${{ secrets.CHART_REPO_APP_PRIVATE_KEY }}
owner: ${{ vars.CHART_REPO_OWNER }}
repositories: ${{ vars.CHART_REPO_NAME }}

- name: Trigger Chart Repository Update
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
run: |
curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: Bearer ${{ steps.generate-token.outputs.token }}" \
https://api.github.com/repos/${{ vars.CHART_REPO_OWNER }}/${{ vars.CHART_REPO_NAME }}/dispatches \
-d '{
"event_type": "helm-chart-published",
"client_payload": {
"chart_file": "${{ steps.package.outputs.chart-file }}",
"chart_version": "${{ steps.package.outputs.chart-version }}",
"app_version": "${{ github.ref_name }}",
"source_repo": "${{ github.event.repository.name }}",
"source_run_id": "${{ github.run_id }}"
}
}'
3 changes: 2 additions & 1 deletion backend/src/baserow/api/two_factor_auth/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@

class TwoFactorAuthSerializer(serializers.ModelSerializer):
type = serializers.SerializerMethodField(read_only=True)
is_enabled = serializers.BooleanField(read_only=True)

def get_type(self, instance):
return instance.get_type().type

class Meta:
model = TwoFactorAuthProviderModel
fields = ["type"]
fields = ["type", "is_enabled"]


class CreateTwoFactorAuthSerializer(serializers.ModelSerializer):
Expand Down
11 changes: 11 additions & 0 deletions backend/src/baserow/api/workspaces/users/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from rest_framework import serializers

from baserow.api.mixins import UnknownFieldRaisesExceptionSerializerMixin
from baserow.api.two_factor_auth.serializers import TwoFactorAuthSerializer
from baserow.api.user.registries import member_data_registry
from baserow.core.generative_ai.registries import generative_ai_model_type_registry
from baserow.core.models import WorkspaceUser
Expand All @@ -19,6 +20,7 @@ class WorkspaceUserSerializer(serializers.ModelSerializer):
source="user.profile.to_be_deleted",
help_text="True if user account is pending deletion.",
)
two_factor_auth = serializers.SerializerMethodField()

class Meta:
model = WorkspaceUser
Expand All @@ -31,6 +33,7 @@ class Meta:
"created_on",
"user_id",
"to_be_deleted",
"two_factor_auth",
)

@extend_schema_field(OpenApiTypes.STR)
Expand All @@ -41,6 +44,14 @@ def get_name(self, object):
def get_email(self, object):
return object.user.email

def get_two_factor_auth(self, object):
try:
provider = object.user.two_factor_auth_providers.all()[0]
except IndexError:
provider = None

return TwoFactorAuthSerializer(provider).data


def get_member_data_types_request_serializer():
"""
Expand Down
16 changes: 14 additions & 2 deletions backend/src/baserow/api/workspaces/users/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.db import transaction
from django.db.models import Prefetch

from drf_spectacular.openapi import OpenApiParameter, OpenApiTypes
from drf_spectacular.utils import extend_schema
Expand Down Expand Up @@ -29,6 +30,7 @@
ERROR_CANNOT_DELETE_YOURSELF_FROM_GROUP,
ERROR_GROUP_USER_DOES_NOT_EXIST,
)
from baserow.core.db import specific_queryset
from baserow.core.exceptions import (
CannotDeleteYourselfFromWorkspace,
UserInvalidWorkspacePermissionsError,
Expand All @@ -39,6 +41,7 @@
from baserow.core.handler import CoreHandler
from baserow.core.models import WorkspaceUser
from baserow.core.operations import ListWorkspaceUsersWorkspaceOperationType
from baserow.core.two_factor_auth.models import TwoFactorAuthProviderModel

from .generated_serializers import ListWorkspaceUsersWithMemberDataSerializer
from .serializers import (
Expand Down Expand Up @@ -123,8 +126,17 @@ def get(self, request, workspace_id, query_params):
context=workspace,
)

qs = WorkspaceUser.objects.filter(workspace=workspace).select_related(
"workspace", "user", "user__profile"
qs = (
WorkspaceUser.objects.filter(workspace=workspace)
.select_related("workspace", "user", "user__profile")
.prefetch_related(
Prefetch(
"user__two_factor_auth_providers",
queryset=specific_queryset(
TwoFactorAuthProviderModel.objects.all()
),
)
)
)

qs = self.apply_search(search, qs)
Expand Down
4 changes: 4 additions & 0 deletions backend/src/baserow/contrib/builder/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ def ready(self):
NotificationWorkflowActionType,
OpenPageWorkflowActionType,
RefreshDataSourceWorkflowActionType,
SlackWriteMessageWorkflowActionType,
UpdateRowWorkflowActionType,
)

Expand All @@ -301,6 +302,9 @@ def ready(self):
builder_workflow_action_type_registry.register(CoreHttpRequestActionType())
builder_workflow_action_type_registry.register(CoreSMTPEmailActionType())
builder_workflow_action_type_registry.register(AIAgentWorkflowActionType())
builder_workflow_action_type_registry.register(
SlackWriteMessageWorkflowActionType()
)

from .elements.collection_field_types import (
BooleanCollectionFieldType,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Generated by Django 5.0.14 on 2025-11-13 11:57

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


class Migration(migrations.Migration):
dependencies = [
("builder", "0066_element_visibility_condition"),
("core", "0107_twofactorauthprovidermodel_totpauthprovidermodel_and_more"),
]

operations = [
migrations.CreateModel(
name="SlackWriteMessageWorkflowAction",
fields=[
(
"builderworkflowaction_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="builder.builderworkflowaction",
),
),
(
"service",
models.ForeignKey(
help_text="The service which this action is associated with.",
on_delete=django.db.models.deletion.CASCADE,
to="core.service",
),
),
],
options={
"abstract": False,
},
bases=("builder.builderworkflowaction",),
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,7 @@ class CoreSMTPEmailWorkflowAction(BuilderWorkflowServiceAction):

class AIAgentWorkflowAction(BuilderWorkflowServiceAction):
...


class SlackWriteMessageWorkflowAction(BuilderWorkflowServiceAction):
...
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
NotificationWorkflowAction,
OpenPageWorkflowAction,
RefreshDataSourceWorkflowAction,
SlackWriteMessageWorkflowAction,
)
from baserow.contrib.builder.workflow_actions.registries import (
BuilderWorkflowActionType,
Expand All @@ -40,6 +41,9 @@
LocalBaserowDeleteRowServiceType,
LocalBaserowUpsertRowServiceType,
)
from baserow.contrib.integrations.slack.service_types import (
SlackWriteMessageServiceType,
)
from baserow.core.db import specific_queryset
from baserow.core.formula.field import BASEROW_FORMULA_VERSION_INITIAL
from baserow.core.formula.serializers import FormulaSerializerField
Expand Down Expand Up @@ -493,3 +497,13 @@ class AIAgentWorkflowActionType(BuilderWorkflowServiceActionType):
def get_pytest_params(self, pytest_data_fixture) -> Dict[str, int]:
service = pytest_data_fixture.create_ai_agent_service()
return {"service": service}


class SlackWriteMessageWorkflowActionType(BuilderWorkflowServiceActionType):
type = "slack_write_message"
model_class = SlackWriteMessageWorkflowAction
service_type = SlackWriteMessageServiceType.type

def get_pytest_params(self, pytest_data_fixture) -> Dict[str, int]:
service = pytest_data_fixture.create_slack_write_message_service()
return {"service": service}
4 changes: 2 additions & 2 deletions backend/src/baserow/contrib/database/rows/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2503,13 +2503,13 @@ def _extract_field_ids_from_row_values(

def get_rows(
self, model: GeneratedTableModel, row_ids: List[int]
) -> List[GeneratedTableModel]:
) -> QuerySet[GeneratedTableModel]:
"""
Returns a list of rows based on the provided row ids.

:param model: The model that should be used to get the rows.
:param row_ids: The list of row ids that should be fetched.
:return: The list of rows.
:return: A queryset of the fetched rows.
"""

return model.objects.filter(id__in=row_ids).enhance_by_fields()
Expand Down
Loading
Loading