-
Notifications
You must be signed in to change notification settings - Fork 272
Fix #280 Django thread-local connection cleanup in multi threads #281
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,2 @@ | ||
| db.sqlite3 | ||
| db.sqlite3 | ||
| db/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| version: '3.9' | ||
| services: | ||
| db: | ||
| image: mysql:8 | ||
| environment: | ||
| MYSQL_DATABASE: slackapp | ||
| MYSQL_USER: app | ||
| MYSQL_PASSWORD: password | ||
| MYSQL_ROOT_PASSWORD: password | ||
| #command: | ||
| # - '--wait_timeout=3' | ||
| volumes: | ||
| - './db:/var/lib/mysql' | ||
| ports: | ||
| - 33306:3306 | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,15 +6,15 @@ | |
|
|
||
|
|
||
| class SlackBot(models.Model): | ||
| client_id = models.TextField(null=False) | ||
| app_id = models.TextField(null=False) | ||
| enterprise_id = models.TextField(null=True) | ||
| client_id = models.CharField(null=False, max_length=32) | ||
| app_id = models.CharField(null=False, max_length=32) | ||
| enterprise_id = models.CharField(null=True, max_length=32) | ||
| enterprise_name = models.TextField(null=True) | ||
| team_id = models.TextField(null=True) | ||
| team_id = models.CharField(null=True, max_length=32) | ||
| team_name = models.TextField(null=True) | ||
| bot_token = models.TextField(null=True) | ||
| bot_id = models.TextField(null=True) | ||
| bot_user_id = models.TextField(null=True) | ||
| bot_id = models.CharField(null=True, max_length=32) | ||
| bot_user_id = models.CharField(null=True, max_length=32) | ||
| bot_scopes = models.TextField(null=True) | ||
| is_enterprise_install = models.BooleanField(null=True) | ||
| installed_at = models.DateTimeField(null=False) | ||
|
|
@@ -28,26 +28,26 @@ class Meta: | |
|
|
||
|
|
||
| class SlackInstallation(models.Model): | ||
| client_id = models.TextField(null=False) | ||
| app_id = models.TextField(null=False) | ||
| enterprise_id = models.TextField(null=True) | ||
| client_id = models.CharField(null=False, max_length=32) | ||
| app_id = models.CharField(null=False, max_length=32) | ||
| enterprise_id = models.CharField(null=True, max_length=32) | ||
| enterprise_name = models.TextField(null=True) | ||
| enterprise_url = models.TextField(null=True) | ||
| team_id = models.TextField(null=True) | ||
| team_id = models.CharField(null=True, max_length=32) | ||
| team_name = models.TextField(null=True) | ||
| bot_token = models.TextField(null=True) | ||
| bot_id = models.TextField(null=True) | ||
| bot_id = models.CharField(null=True, max_length=32) | ||
| bot_user_id = models.TextField(null=True) | ||
| bot_scopes = models.TextField(null=True) | ||
| user_id = models.TextField(null=False) | ||
| user_id = models.CharField(null=False, max_length=32) | ||
| user_token = models.TextField(null=True) | ||
| user_scopes = models.TextField(null=True) | ||
| incoming_webhook_url = models.TextField(null=True) | ||
| incoming_webhook_channel = models.TextField(null=True) | ||
| incoming_webhook_channel_id = models.TextField(null=True) | ||
| incoming_webhook_configuration_url = models.TextField(null=True) | ||
| is_enterprise_install = models.BooleanField(null=True) | ||
| token_type = models.TextField(null=True) | ||
| token_type = models.CharField(null=True, max_length=32) | ||
| installed_at = models.DateTimeField(null=False) | ||
|
|
||
| class Meta: | ||
|
|
@@ -65,7 +65,7 @@ class Meta: | |
|
|
||
|
|
||
| class SlackOAuthState(models.Model): | ||
| state = models.TextField(null=False) | ||
| state = models.CharField(null=False, max_length=64) | ||
| expire_at = models.DateTimeField(null=False) | ||
|
|
||
|
|
||
|
|
@@ -81,6 +81,7 @@ class SlackOAuthState(models.Model): | |
| from django.utils import timezone | ||
| from slack_sdk.oauth import InstallationStore, OAuthStateStore | ||
| from slack_sdk.oauth.installation_store import Bot, Installation | ||
| from slack_sdk.webhook import WebhookClient | ||
|
|
||
|
|
||
| class DjangoInstallationStore(InstallationStore): | ||
|
|
@@ -100,9 +101,13 @@ def logger(self) -> Logger: | |
|
|
||
| def save(self, installation: Installation): | ||
| i = installation.to_dict() | ||
| if is_naive(i["installed_at"]): | ||
| i["installed_at"] = make_aware(i["installed_at"]) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an improvement for MySQL apps. |
||
| i["client_id"] = self.client_id | ||
| SlackInstallation(**i).save() | ||
| b = installation.to_bot().to_dict() | ||
| if is_naive(b["installed_at"]): | ||
| b["installed_at"] = make_aware(b["installed_at"]) | ||
| b["client_id"] = self.client_id | ||
| SlackBot(**b).save() | ||
|
|
||
|
|
@@ -222,7 +227,7 @@ def consume(self, state: str) -> bool: | |
|
|
||
| import logging | ||
| import os | ||
| from slack_bolt import App | ||
| from slack_bolt import App, BoltContext | ||
| from slack_bolt.oauth.oauth_settings import OAuthSettings | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
@@ -249,12 +254,34 @@ def consume(self, state: str) -> bool: | |
| ) | ||
|
|
||
|
|
||
| @app.event("app_mention") | ||
| def event_test(body, say, logger): | ||
| def event_test(body, say, context: BoltContext, logger): | ||
| logger.info(body) | ||
| say("What's up?") | ||
| say(":wave: What's up?") | ||
|
|
||
| found_rows = list( | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there is no thread-bound database connection when first access to any Django models, Django automatically establishes a new connection and associates it with the thread. This example demonstrates the situation where a listener performs a database query using Django ORM in a different thread (=thread managed by Bolt, not Django). |
||
| SlackInstallation.objects.filter(enterprise_id=context.enterprise_id) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there is no thread-bound database connection when first access to any Django models, Django automatically establishes a new connection and associates it with the thread. This example demonstrates the situation where a listener performs a database query using Django ORM in a different thread (=thread managed by Bolt, not Django). |
||
| .filter(team_id=context.team_id) | ||
| .filter(incoming_webhook_url__isnull=False) | ||
| .order_by(F("installed_at").desc())[:1] | ||
| ) | ||
| if len(found_rows) > 0: | ||
| webhook_url = found_rows[0].incoming_webhook_url | ||
| logger.info(f"webhook_url: {webhook_url}") | ||
| client = WebhookClient(webhook_url) | ||
| client.send(text=":wave: This is a message posted using Incoming Webhook!") | ||
|
|
||
|
|
||
| # lazy listener example | ||
| def noop(): | ||
| pass | ||
|
|
||
|
|
||
| app.event("app_mention")( | ||
| ack=event_test, | ||
| lazy=[noop], | ||
| ) | ||
|
|
||
|
|
||
| @app.command("/hello-bolt-python") | ||
| @app.command("/hello-django-app") | ||
| def command(ack): | ||
| ack("This is a Django app!") | ||
| ack(":wave: Hello from a Django app :smile:") | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For MySQL compatibility, I've changed the database schema for this example app