Skip to content

Commit ea73760

Browse files
committed
Fix #280 Django thread-local connection cleanup in multi threads
1 parent 12ce26a commit ea73760

File tree

17 files changed

+620
-154
lines changed

17 files changed

+620
-154
lines changed

examples/django/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
db.sqlite3
1+
db.sqlite3
2+
db/
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
version: '3.9'
2+
services:
3+
db:
4+
image: mysql:8
5+
environment:
6+
MYSQL_DATABASE: slackapp
7+
MYSQL_USER: app
8+
MYSQL_PASSWORD: password
9+
MYSQL_ROOT_PASSWORD: password
10+
#command:
11+
# - '--wait_timeout=3'
12+
volumes:
13+
- './db:/var/lib/mysql'
14+
ports:
15+
- 33306:3306
16+

examples/django/slackapp/migrations/0001_initial.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Generated by Django 3.1.4 on 2020-12-04 13:07
1+
# Generated by Django 3.1.7 on 2021-04-02 05:53
22

33
from django.db import migrations, models
44

@@ -22,15 +22,15 @@ class Migration(migrations.Migration):
2222
verbose_name="ID",
2323
),
2424
),
25-
("client_id", models.TextField()),
26-
("app_id", models.TextField()),
27-
("enterprise_id", models.TextField(null=True)),
25+
("client_id", models.CharField(max_length=32)),
26+
("app_id", models.CharField(max_length=32)),
27+
("enterprise_id", models.CharField(max_length=32, null=True)),
2828
("enterprise_name", models.TextField(null=True)),
29-
("team_id", models.TextField(null=True)),
29+
("team_id", models.CharField(max_length=32, null=True)),
3030
("team_name", models.TextField(null=True)),
3131
("bot_token", models.TextField(null=True)),
32-
("bot_id", models.TextField(null=True)),
33-
("bot_user_id", models.TextField(null=True)),
32+
("bot_id", models.CharField(max_length=32, null=True)),
33+
("bot_user_id", models.CharField(max_length=32, null=True)),
3434
("bot_scopes", models.TextField(null=True)),
3535
("is_enterprise_install", models.BooleanField(null=True)),
3636
("installed_at", models.DateTimeField()),
@@ -48,26 +48,26 @@ class Migration(migrations.Migration):
4848
verbose_name="ID",
4949
),
5050
),
51-
("client_id", models.TextField()),
52-
("app_id", models.TextField()),
53-
("enterprise_id", models.TextField(null=True)),
51+
("client_id", models.CharField(max_length=32)),
52+
("app_id", models.CharField(max_length=32)),
53+
("enterprise_id", models.CharField(max_length=32, null=True)),
5454
("enterprise_name", models.TextField(null=True)),
5555
("enterprise_url", models.TextField(null=True)),
56-
("team_id", models.TextField(null=True)),
56+
("team_id", models.CharField(max_length=32, null=True)),
5757
("team_name", models.TextField(null=True)),
5858
("bot_token", models.TextField(null=True)),
59-
("bot_id", models.TextField(null=True)),
59+
("bot_id", models.CharField(max_length=32, null=True)),
6060
("bot_user_id", models.TextField(null=True)),
6161
("bot_scopes", models.TextField(null=True)),
62-
("user_id", models.TextField()),
62+
("user_id", models.CharField(max_length=32)),
6363
("user_token", models.TextField(null=True)),
6464
("user_scopes", models.TextField(null=True)),
6565
("incoming_webhook_url", models.TextField(null=True)),
6666
("incoming_webhook_channel", models.TextField(null=True)),
6767
("incoming_webhook_channel_id", models.TextField(null=True)),
6868
("incoming_webhook_configuration_url", models.TextField(null=True)),
6969
("is_enterprise_install", models.BooleanField(null=True)),
70-
("token_type", models.TextField(null=True)),
70+
("token_type", models.CharField(max_length=32, null=True)),
7171
("installed_at", models.DateTimeField()),
7272
],
7373
),
@@ -83,7 +83,7 @@ class Migration(migrations.Migration):
8383
verbose_name="ID",
8484
),
8585
),
86-
("state", models.TextField()),
86+
("state", models.CharField(max_length=64)),
8787
("expire_at", models.DateTimeField()),
8888
],
8989
),
@@ -97,14 +97,14 @@ class Migration(migrations.Migration):
9797
"user_id",
9898
"installed_at",
9999
],
100-
name="bolt_slacki_client__62c411_idx",
100+
name="slackapp_sl_client__9b0d3f_idx",
101101
),
102102
),
103103
migrations.AddIndex(
104104
model_name="slackbot",
105105
index=models.Index(
106106
fields=["client_id", "enterprise_id", "team_id", "installed_at"],
107-
name="bolt_slackb_client__be066b_idx",
107+
name="slackapp_sl_client__d220d6_idx",
108108
),
109109
),
110110
]

examples/django/slackapp/models.py

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66

77

88
class SlackBot(models.Model):
9-
client_id = models.TextField(null=False)
10-
app_id = models.TextField(null=False)
11-
enterprise_id = models.TextField(null=True)
9+
client_id = models.CharField(null=False, max_length=32)
10+
app_id = models.CharField(null=False, max_length=32)
11+
enterprise_id = models.CharField(null=True, max_length=32)
1212
enterprise_name = models.TextField(null=True)
13-
team_id = models.TextField(null=True)
13+
team_id = models.CharField(null=True, max_length=32)
1414
team_name = models.TextField(null=True)
1515
bot_token = models.TextField(null=True)
16-
bot_id = models.TextField(null=True)
17-
bot_user_id = models.TextField(null=True)
16+
bot_id = models.CharField(null=True, max_length=32)
17+
bot_user_id = models.CharField(null=True, max_length=32)
1818
bot_scopes = models.TextField(null=True)
1919
is_enterprise_install = models.BooleanField(null=True)
2020
installed_at = models.DateTimeField(null=False)
@@ -28,26 +28,26 @@ class Meta:
2828

2929

3030
class SlackInstallation(models.Model):
31-
client_id = models.TextField(null=False)
32-
app_id = models.TextField(null=False)
33-
enterprise_id = models.TextField(null=True)
31+
client_id = models.CharField(null=False, max_length=32)
32+
app_id = models.CharField(null=False, max_length=32)
33+
enterprise_id = models.CharField(null=True, max_length=32)
3434
enterprise_name = models.TextField(null=True)
3535
enterprise_url = models.TextField(null=True)
36-
team_id = models.TextField(null=True)
36+
team_id = models.CharField(null=True, max_length=32)
3737
team_name = models.TextField(null=True)
3838
bot_token = models.TextField(null=True)
39-
bot_id = models.TextField(null=True)
39+
bot_id = models.CharField(null=True, max_length=32)
4040
bot_user_id = models.TextField(null=True)
4141
bot_scopes = models.TextField(null=True)
42-
user_id = models.TextField(null=False)
42+
user_id = models.CharField(null=False, max_length=32)
4343
user_token = models.TextField(null=True)
4444
user_scopes = models.TextField(null=True)
4545
incoming_webhook_url = models.TextField(null=True)
4646
incoming_webhook_channel = models.TextField(null=True)
4747
incoming_webhook_channel_id = models.TextField(null=True)
4848
incoming_webhook_configuration_url = models.TextField(null=True)
4949
is_enterprise_install = models.BooleanField(null=True)
50-
token_type = models.TextField(null=True)
50+
token_type = models.CharField(null=True, max_length=32)
5151
installed_at = models.DateTimeField(null=False)
5252

5353
class Meta:
@@ -65,7 +65,7 @@ class Meta:
6565

6666

6767
class SlackOAuthState(models.Model):
68-
state = models.TextField(null=False)
68+
state = models.CharField(null=False, max_length=64)
6969
expire_at = models.DateTimeField(null=False)
7070

7171

@@ -81,6 +81,7 @@ class SlackOAuthState(models.Model):
8181
from django.utils import timezone
8282
from slack_sdk.oauth import InstallationStore, OAuthStateStore
8383
from slack_sdk.oauth.installation_store import Bot, Installation
84+
from slack_sdk.webhook import WebhookClient
8485

8586

8687
class DjangoInstallationStore(InstallationStore):
@@ -100,9 +101,13 @@ def logger(self) -> Logger:
100101

101102
def save(self, installation: Installation):
102103
i = installation.to_dict()
104+
if is_naive(i["installed_at"]):
105+
i["installed_at"] = make_aware(i["installed_at"])
103106
i["client_id"] = self.client_id
104107
SlackInstallation(**i).save()
105108
b = installation.to_bot().to_dict()
109+
if is_naive(b["installed_at"]):
110+
b["installed_at"] = make_aware(b["installed_at"])
106111
b["client_id"] = self.client_id
107112
SlackBot(**b).save()
108113

@@ -222,7 +227,7 @@ def consume(self, state: str) -> bool:
222227

223228
import logging
224229
import os
225-
from slack_bolt import App
230+
from slack_bolt import App, BoltContext
226231
from slack_bolt.oauth.oauth_settings import OAuthSettings
227232

228233
logger = logging.getLogger(__name__)
@@ -249,12 +254,34 @@ def consume(self, state: str) -> bool:
249254
)
250255

251256

252-
@app.event("app_mention")
253-
def event_test(body, say, logger):
257+
def event_test(body, say, context: BoltContext, logger):
254258
logger.info(body)
255-
say("What's up?")
259+
say(":wave: What's up?")
260+
261+
found_rows = list(
262+
SlackInstallation.objects.filter(enterprise_id=context.enterprise_id)
263+
.filter(team_id=context.team_id)
264+
.filter(incoming_webhook_url__isnull=False)
265+
.order_by(F("installed_at").desc())[:1]
266+
)
267+
if len(found_rows) > 0:
268+
webhook_url = found_rows[0].incoming_webhook_url
269+
logger.info(f"webhook_url: {webhook_url}")
270+
client = WebhookClient(webhook_url)
271+
client.send(text=":wave: This is a message posted using Incoming Webhook!")
272+
273+
274+
# lazy listener example
275+
def noop():
276+
pass
277+
278+
279+
app.event("app_mention")(
280+
ack=event_test,
281+
lazy=[noop],
282+
)
256283

257284

258-
@app.command("/hello-bolt-python")
285+
@app.command("/hello-django-app")
259286
def command(ack):
260-
ack("This is a Django app!")
287+
ack(":wave: Hello from a Django app :smile:")

examples/django/slackapp/settings.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@
2222
},
2323
"root": {
2424
"handlers": ["console"],
25-
"level": "INFO",
25+
"level": "DEBUG",
2626
},
2727
"loggers": {
2828
"django": {
2929
"handlers": ["console"],
3030
"level": os.getenv("DJANGO_LOG_LEVEL", "INFO"),
3131
"propagate": False,
3232
},
33-
"django.db.backends": {
33+
"django.db": {
3434
"level": "DEBUG",
3535
},
3636
"slack_bolt": {
@@ -105,10 +105,25 @@
105105
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
106106

107107
DATABASES = {
108+
# python manage.py migrate
109+
# python manage.py runserver 0.0.0.0:3000
110+
# "default": {
111+
# "ENGINE": "django.db.backends.sqlite3",
112+
# "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
113+
# },
114+
115+
# docker-compose -f mysql-docker-compose.yml up --build
116+
# pip install mysqlclient
117+
# python manage.py migrate
118+
# python manage.py runserver 0.0.0.0:3000
108119
"default": {
109-
"ENGINE": "django.db.backends.sqlite3",
110-
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
111-
}
120+
"ENGINE": "django.db.backends.mysql",
121+
"NAME": "slackapp",
122+
"USER": "app",
123+
"PASSWORD": "password",
124+
"HOST": "127.0.0.1",
125+
"PORT": 33306,
126+
},
112127
}
113128

114129
# Password validation

0 commit comments

Comments
 (0)