Skip to content

Commit dbb1bcd

Browse files
authored
Slack send message service (baserow#4187)
* Initial working commit * Post rebase tweaks. * Backend tests; more moving files around; post-rebase tweaks. * Improving the experience setting up the slack bot integration. There are now instructions inside the modal to guide you. * We don't always start with an integrationType. * Small bug fixes and improvements. * Lint fix * Post-rebase lint * Forgot a comma * Address spirit-check feedback * Address feedback
1 parent 782a130 commit dbb1bcd

File tree

33 files changed

+1200
-28
lines changed

33 files changed

+1200
-28
lines changed

backend/src/baserow/contrib/automation/apps.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def ready(self):
3636
LocalBaserowRowsDeletedNodeTriggerType,
3737
LocalBaserowRowsUpdatedNodeTriggerType,
3838
LocalBaserowUpdateRowNodeType,
39+
SlackWriteMessageActionNodeType,
3940
)
4041
from baserow.contrib.automation.nodes.object_scopes import (
4142
AutomationNodeObjectScopeType,
@@ -167,6 +168,7 @@ def ready(self):
167168
LocalBaserowRowsDeletedNodeTriggerType()
168169
)
169170
automation_node_type_registry.register(CorePeriodicTriggerNodeType())
171+
automation_node_type_registry.register(SlackWriteMessageActionNodeType())
170172
automation_node_type_registry.register(CoreHTTPTriggerNodeType())
171173
automation_node_type_registry.register(AIAgentActionNodeType())
172174

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Generated by Django 5.0.14 on 2025-11-04 11:15
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
dependencies = [
9+
(
10+
"automation",
11+
"0022_aiagentactionnode",
12+
),
13+
]
14+
15+
operations = [
16+
migrations.CreateModel(
17+
name="SlackWriteMessageActionNode",
18+
fields=[
19+
(
20+
"automationnode_ptr",
21+
models.OneToOneField(
22+
auto_created=True,
23+
on_delete=django.db.models.deletion.CASCADE,
24+
parent_link=True,
25+
primary_key=True,
26+
serialize=False,
27+
to="automation.automationnode",
28+
),
29+
),
30+
],
31+
options={
32+
"abstract": False,
33+
},
34+
bases=("automation.automationnode",),
35+
),
36+
]

backend/src/baserow/contrib/automation/nodes/handler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,5 +410,5 @@ def dispatch_node(
410410
)
411411
except ServiceImproperlyConfiguredDispatchException as e:
412412
raise AutomationNodeMisconfiguredService(
413-
f"The node {node.id} has a misconfigured service."
413+
f"The node {node.id} is misconfigured and cannot be dispatched. {str(e)}"
414414
) from e

backend/src/baserow/contrib/automation/nodes/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,7 @@ class CoreIteratorActionNode(AutomationActionNode):
228228

229229
class AIAgentActionNode(AutomationActionNode):
230230
...
231+
232+
233+
class SlackWriteMessageActionNode(AutomationActionNode):
234+
...

backend/src/baserow/contrib/automation/nodes/node_types.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
LocalBaserowRowsDeletedTriggerNode,
3535
LocalBaserowRowsUpdatedTriggerNode,
3636
LocalBaserowUpdateRowActionNode,
37+
SlackWriteMessageActionNode,
3738
)
3839
from baserow.contrib.automation.nodes.registries import AutomationNodeType
3940
from baserow.contrib.automation.nodes.types import NodePositionType
@@ -58,6 +59,9 @@
5859
LocalBaserowRowsUpdatedServiceType,
5960
LocalBaserowUpsertRowServiceType,
6061
)
62+
from baserow.contrib.integrations.slack.service_types import (
63+
SlackWriteMessageServiceType,
64+
)
6165
from baserow.core.registry import Instance
6266
from baserow.core.services.models import Service
6367
from baserow.core.services.registries import service_type_registry
@@ -404,3 +408,9 @@ class CoreHTTPTriggerNodeType(AutomationNodeTriggerType):
404408
type = "http_trigger"
405409
model_class = CoreHTTPTriggerNode
406410
service_type = CoreHTTPTriggerServiceType.type
411+
412+
413+
class SlackWriteMessageActionNodeType(AutomationNodeActionNodeType):
414+
type = "slack_write_message"
415+
model_class = SlackWriteMessageActionNode
416+
service_type = SlackWriteMessageServiceType.type

backend/src/baserow/contrib/integrations/apps.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,16 @@ def ready(self):
1212
from baserow.contrib.integrations.local_baserow.integration_types import (
1313
LocalBaserowIntegrationType,
1414
)
15+
from baserow.contrib.integrations.slack.integration_types import (
16+
SlackBotIntegrationType,
17+
)
1518
from baserow.core.integrations.registries import integration_type_registry
1619
from baserow.core.services.registries import service_type_registry
1720

1821
integration_type_registry.register(LocalBaserowIntegrationType())
1922
integration_type_registry.register(SMTPIntegrationType())
2023
integration_type_registry.register(AIIntegrationType())
24+
integration_type_registry.register(SlackBotIntegrationType())
2125

2226
from baserow.contrib.integrations.local_baserow.service_types import (
2327
LocalBaserowAggregateRowsUserServiceType,
@@ -39,6 +43,12 @@ def ready(self):
3943
service_type_registry.register(LocalBaserowRowsUpdatedServiceType())
4044
service_type_registry.register(LocalBaserowRowsDeletedServiceType())
4145

46+
from baserow.contrib.integrations.slack.service_types import (
47+
SlackWriteMessageServiceType,
48+
)
49+
50+
service_type_registry.register(SlackWriteMessageServiceType())
51+
4252
from baserow.contrib.integrations.core.service_types import (
4353
CoreHTTPRequestServiceType,
4454
CoreHTTPTriggerServiceType,

backend/src/baserow/contrib/integrations/core/integration_types.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1+
from baserow.contrib.integrations.core.models import SMTPIntegration
12
from baserow.core.integrations.registries import IntegrationType
23
from baserow.core.integrations.types import IntegrationDict
34

4-
from .models import SMTPIntegration
5-
65

76
class SMTPIntegrationType(IntegrationType):
87
type = "smtp"

backend/src/baserow/contrib/integrations/core/service_types.py

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
HTTPHeader,
5555
HTTPQueryParam,
5656
)
57+
from baserow.contrib.integrations.utils import get_http_request_function
5758
from baserow.core.formula.types import BaserowFormulaObject
5859
from baserow.core.formula.validator import (
5960
ensure_array,
@@ -535,24 +536,6 @@ def formulas_to_resolve(
535536

536537
return formulas
537538

538-
def _get_request_function(self) -> callable:
539-
"""
540-
Return the appropriate request function based on production environment
541-
or settings.
542-
In production mode, the advocate library is used so that the internal
543-
network can't be reached. This can be disabled by changing the Django
544-
setting INTEGRATIONS_ALLOW_PRIVATE_ADDRESS.
545-
"""
546-
547-
if settings.INTEGRATIONS_ALLOW_PRIVATE_ADDRESS is True:
548-
from requests import request
549-
550-
return request
551-
else:
552-
from advocate import request
553-
554-
return request
555-
556539
def dispatch_data(
557540
self,
558541
service: CoreHTTPRequestService,
@@ -589,7 +572,7 @@ def dispatch_data(
589572
}
590573

591574
try:
592-
response = self._get_request_function()(
575+
response = get_http_request_function()(
593576
method=service.http_method,
594577
url=resolved_values["url"],
595578
headers=headers,
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Generated by Django 5.0.14 on 2025-11-04 11:20
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
import baserow.core.formula.field
7+
8+
9+
class Migration(migrations.Migration):
10+
dependencies = [
11+
("core", "0106_schemaoperation"),
12+
("integrations", "0023_aiagentservice_aiintegration"),
13+
]
14+
15+
operations = [
16+
migrations.CreateModel(
17+
name="SlackBotIntegration",
18+
fields=[
19+
(
20+
"integration_ptr",
21+
models.OneToOneField(
22+
auto_created=True,
23+
on_delete=django.db.models.deletion.CASCADE,
24+
parent_link=True,
25+
primary_key=True,
26+
serialize=False,
27+
to="core.integration",
28+
),
29+
),
30+
(
31+
"token",
32+
models.CharField(
33+
help_text="The Bot User OAuth Token listed in your Slack bot's OAuth & Permissions page.",
34+
max_length=255,
35+
),
36+
),
37+
],
38+
options={
39+
"abstract": False,
40+
},
41+
bases=("core.integration",),
42+
),
43+
migrations.CreateModel(
44+
name="SlackWriteMessageService",
45+
fields=[
46+
(
47+
"service_ptr",
48+
models.OneToOneField(
49+
auto_created=True,
50+
on_delete=django.db.models.deletion.CASCADE,
51+
parent_link=True,
52+
primary_key=True,
53+
serialize=False,
54+
to="core.service",
55+
),
56+
),
57+
(
58+
"channel",
59+
models.CharField(
60+
help_text="The Slack channel ID where the message will be sent.",
61+
max_length=80,
62+
),
63+
),
64+
(
65+
"text",
66+
baserow.core.formula.field.FormulaField(
67+
blank=True,
68+
default="",
69+
help_text="The text content of the Slack message.",
70+
null=True,
71+
),
72+
),
73+
],
74+
options={
75+
"abstract": False,
76+
},
77+
bases=("core.service",),
78+
),
79+
]

backend/src/baserow/contrib/integrations/slack/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)