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/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def ready(self):
LocalBaserowRowsDeletedNodeTriggerType,
LocalBaserowRowsUpdatedNodeTriggerType,
LocalBaserowUpdateRowNodeType,
SlackWriteMessageActionNodeType,
)
from baserow.contrib.automation.nodes.object_scopes import (
AutomationNodeObjectScopeType,
Expand Down Expand Up @@ -167,6 +168,7 @@ def ready(self):
LocalBaserowRowsDeletedNodeTriggerType()
)
automation_node_type_registry.register(CorePeriodicTriggerNodeType())
automation_node_type_registry.register(SlackWriteMessageActionNodeType())
automation_node_type_registry.register(CoreHTTPTriggerNodeType())
automation_node_type_registry.register(AIAgentActionNodeType())

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Generated by Django 5.0.14 on 2025-11-04 11:15

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


class Migration(migrations.Migration):
dependencies = [
(
"automation",
"0022_aiagentactionnode",
),
]

operations = [
migrations.CreateModel(
name="SlackWriteMessageActionNode",
fields=[
(
"automationnode_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="automation.automationnode",
),
),
],
options={
"abstract": False,
},
bases=("automation.automationnode",),
),
]
2 changes: 1 addition & 1 deletion backend/src/baserow/contrib/automation/nodes/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,5 +410,5 @@ def dispatch_node(
)
except ServiceImproperlyConfiguredDispatchException as e:
raise AutomationNodeMisconfiguredService(
f"The node {node.id} has a misconfigured service."
f"The node {node.id} is misconfigured and cannot be dispatched. {str(e)}"
) from e
4 changes: 4 additions & 0 deletions backend/src/baserow/contrib/automation/nodes/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,7 @@ class CoreIteratorActionNode(AutomationActionNode):

class AIAgentActionNode(AutomationActionNode):
...


class SlackWriteMessageActionNode(AutomationActionNode):
...
10 changes: 10 additions & 0 deletions backend/src/baserow/contrib/automation/nodes/node_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
LocalBaserowRowsDeletedTriggerNode,
LocalBaserowRowsUpdatedTriggerNode,
LocalBaserowUpdateRowActionNode,
SlackWriteMessageActionNode,
)
from baserow.contrib.automation.nodes.registries import AutomationNodeType
from baserow.contrib.automation.nodes.types import NodePositionType
Expand All @@ -58,6 +59,9 @@
LocalBaserowRowsUpdatedServiceType,
LocalBaserowUpsertRowServiceType,
)
from baserow.contrib.integrations.slack.service_types import (
SlackWriteMessageServiceType,
)
from baserow.core.registry import Instance
from baserow.core.services.models import Service
from baserow.core.services.registries import service_type_registry
Expand Down Expand Up @@ -404,3 +408,9 @@ class CoreHTTPTriggerNodeType(AutomationNodeTriggerType):
type = "http_trigger"
model_class = CoreHTTPTriggerNode
service_type = CoreHTTPTriggerServiceType.type


class SlackWriteMessageActionNodeType(AutomationNodeActionNodeType):
type = "slack_write_message"
model_class = SlackWriteMessageActionNode
service_type = SlackWriteMessageServiceType.type
10 changes: 10 additions & 0 deletions backend/src/baserow/contrib/integrations/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ def ready(self):
from baserow.contrib.integrations.local_baserow.integration_types import (
LocalBaserowIntegrationType,
)
from baserow.contrib.integrations.slack.integration_types import (
SlackBotIntegrationType,
)
from baserow.core.integrations.registries import integration_type_registry
from baserow.core.services.registries import service_type_registry

integration_type_registry.register(LocalBaserowIntegrationType())
integration_type_registry.register(SMTPIntegrationType())
integration_type_registry.register(AIIntegrationType())
integration_type_registry.register(SlackBotIntegrationType())

from baserow.contrib.integrations.local_baserow.service_types import (
LocalBaserowAggregateRowsUserServiceType,
Expand All @@ -39,6 +43,12 @@ def ready(self):
service_type_registry.register(LocalBaserowRowsUpdatedServiceType())
service_type_registry.register(LocalBaserowRowsDeletedServiceType())

from baserow.contrib.integrations.slack.service_types import (
SlackWriteMessageServiceType,
)

service_type_registry.register(SlackWriteMessageServiceType())

from baserow.contrib.integrations.core.service_types import (
CoreHTTPRequestServiceType,
CoreHTTPTriggerServiceType,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from baserow.contrib.integrations.core.models import SMTPIntegration
from baserow.core.integrations.registries import IntegrationType
from baserow.core.integrations.types import IntegrationDict

from .models import SMTPIntegration


class SMTPIntegrationType(IntegrationType):
type = "smtp"
Expand Down
21 changes: 2 additions & 19 deletions backend/src/baserow/contrib/integrations/core/service_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
HTTPHeader,
HTTPQueryParam,
)
from baserow.contrib.integrations.utils import get_http_request_function
from baserow.core.formula.types import BaserowFormulaObject
from baserow.core.formula.validator import (
ensure_array,
Expand Down Expand Up @@ -535,24 +536,6 @@ def formulas_to_resolve(

return formulas

def _get_request_function(self) -> callable:
"""
Return the appropriate request function based on production environment
or settings.
In production mode, the advocate library is used so that the internal
network can't be reached. This can be disabled by changing the Django
setting INTEGRATIONS_ALLOW_PRIVATE_ADDRESS.
"""

if settings.INTEGRATIONS_ALLOW_PRIVATE_ADDRESS is True:
from requests import request

return request
else:
from advocate import request

return request

def dispatch_data(
self,
service: CoreHTTPRequestService,
Expand Down Expand Up @@ -589,7 +572,7 @@ def dispatch_data(
}

try:
response = self._get_request_function()(
response = get_http_request_function()(
method=service.http_method,
url=resolved_values["url"],
headers=headers,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Generated by Django 5.0.14 on 2025-11-04 11:20

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

import baserow.core.formula.field


class Migration(migrations.Migration):
dependencies = [
("core", "0106_schemaoperation"),
("integrations", "0023_aiagentservice_aiintegration"),
]

operations = [
migrations.CreateModel(
name="SlackBotIntegration",
fields=[
(
"integration_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="core.integration",
),
),
(
"token",
models.CharField(
help_text="The Bot User OAuth Token listed in your Slack bot's OAuth & Permissions page.",
max_length=255,
),
),
],
options={
"abstract": False,
},
bases=("core.integration",),
),
migrations.CreateModel(
name="SlackWriteMessageService",
fields=[
(
"service_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="core.service",
),
),
(
"channel",
models.CharField(
help_text="The Slack channel ID where the message will be sent.",
max_length=80,
),
),
(
"text",
baserow.core.formula.field.FormulaField(
blank=True,
default="",
help_text="The text content of the Slack message.",
null=True,
),
),
],
options={
"abstract": False,
},
bases=("core.service",),
),
]
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from typing import Any, Dict

from baserow.contrib.integrations.slack.models import SlackBotIntegration
from baserow.core.integrations.registries import IntegrationType
from baserow.core.integrations.types import IntegrationDict
from baserow.core.models import Application


class SlackBotIntegrationType(IntegrationType):
type = "slack_bot"
model_class = SlackBotIntegration

class SerializedDict(IntegrationDict):
token: str

serializer_field_names = ["token"]
allowed_fields = ["token"]
sensitive_fields = ["token"]

request_serializer_field_names = ["token"]
request_serializer_field_overrides = {}

def import_serialized(
self,
application: Application,
serialized_values: Dict[str, Any],
id_mapping: Dict,
files_zip=None,
storage=None,
cache=None,
) -> SlackBotIntegration:
"""
Imports a serialized integration. Ensures that if we're importing an exported
integration (where `token` will be `None`), we set it to an empty string.
"""

if serialized_values["token"] is None:
serialized_values["token"] = "" # nosec B105

return super().import_serialized(
application,
serialized_values,
id_mapping,
files_zip=files_zip,
storage=storage,
cache=cache,
)
23 changes: 23 additions & 0 deletions backend/src/baserow/contrib/integrations/slack/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from django.db import models

from baserow.core.formula.field import FormulaField
from baserow.core.integrations.models import Integration
from baserow.core.services.models import Service


class SlackBotIntegration(Integration):
token = models.CharField(
max_length=255,
help_text="The Bot User OAuth Token listed in "
"your Slack bot's OAuth & Permissions page.",
)


class SlackWriteMessageService(Service):
channel = models.CharField(
max_length=80,
help_text="The Slack channel ID where the message will be sent.",
)
text = FormulaField(
help_text="The text content of the Slack message.",
)
Loading
Loading