diff --git a/NOTICE b/NOTICE new file mode 100644 index 00000000..3c471e11 --- /dev/null +++ b/NOTICE @@ -0,0 +1 @@ +Some parts of Nautobot-App-Chatops are based upon `channels`, licensed under BSD-3-Clause by Django Software Foundation. \ No newline at end of file diff --git a/docs/admin/release_notes/version_3.1.md b/docs/admin/release_notes/version_3.1.md index 95995c15..13227ec5 100644 --- a/docs/admin/release_notes/version_3.1.md +++ b/docs/admin/release_notes/version_3.1.md @@ -2,6 +2,12 @@ # v3.1 Release Notes +## [v3.1.3 (2024-12-20)](https://github.com/nautobot/nautobot-app-chatops/releases/tag/v3.1.3) + +### Fixed + +- [#355](https://github.com/nautobot/nautobot-app-chatops/issues/355) - Fixed "Server has gone away" error in Slack Socket Mode. + ## [v3.1.2 (2024-12-19)](https://github.com/nautobot/nautobot-app-chatops/releases/tag/v3.1.2) ### Fixed diff --git a/nautobot_chatops/sockets/slack.py b/nautobot_chatops/sockets/slack.py index 5f95975a..bb71193b 100644 --- a/nautobot_chatops/sockets/slack.py +++ b/nautobot_chatops/sockets/slack.py @@ -4,7 +4,6 @@ import json import shlex -from asgiref.sync import sync_to_async from django.conf import settings from slack_sdk.socket_mode.aiohttp import SocketModeClient from slack_sdk.socket_mode.request import SocketModeRequest @@ -12,7 +11,7 @@ from slack_sdk.web.async_client import AsyncWebClient from nautobot_chatops.dispatchers.slack import SlackDispatcher -from nautobot_chatops.utils import socket_check_and_enqueue_command +from nautobot_chatops.utils import database_sync_to_async, socket_check_and_enqueue_command from nautobot_chatops.workers import commands_help, get_commands_registry, parse_command_string @@ -72,7 +71,7 @@ async def process_slash_command(client, req): client.logger.error("%s", err) return - registry = await sync_to_async(get_commands_registry)() + registry = await database_sync_to_async(get_commands_registry)() if command not in registry: SlackDispatcher(context).send_markdown(commands_help(prefix=SLASH_PREFIX)) @@ -211,7 +210,7 @@ async def process_interactive(client, req): client.logger.info(f"command: {command}, subcommand: {subcommand}, params: {params}") - registry = await sync_to_async(get_commands_registry)() + registry = await database_sync_to_async(get_commands_registry)() if command not in registry: SlackDispatcher(context).send_markdown(commands_help(prefix=SLASH_PREFIX)) @@ -242,7 +241,7 @@ async def process_mention(client, req): client.logger.error("%s", err) return - registry = await sync_to_async(get_commands_registry)() + registry = await database_sync_to_async(get_commands_registry)() if command not in registry: SlackDispatcher(context).send_markdown(commands_help(prefix=SLASH_PREFIX)) diff --git a/nautobot_chatops/utils.py b/nautobot_chatops/utils.py index 59be36d3..f85b33f9 100644 --- a/nautobot_chatops/utils.py +++ b/nautobot_chatops/utils.py @@ -4,8 +4,9 @@ import sys from datetime import datetime, timezone -from asgiref.sync import sync_to_async +from asgiref.sync import SyncToAsync from django.conf import settings +from django.db import close_old_connections from django.db.models import Q from django.http import HttpResponse, JsonResponse from nautobot.core.celery import nautobot_task @@ -18,6 +19,26 @@ logger = logging.getLogger(__name__) +class DatabaseSyncToAsync(SyncToAsync): + """ + SyncToAsync version that cleans up old database connections when it exits. + + Sourced from Channels, see NOTICE file for license information. + """ + + def thread_handler(self, loop, *args, **kwargs): + """Run the handler in a thread, closing old database connections before and after.""" + close_old_connections() + try: + return super().thread_handler(loop, *args, **kwargs) + finally: + close_old_connections() + + +# The class is TitleCased, but we want to encourage use as a callable/decorator +database_sync_to_async = DatabaseSyncToAsync # pylint: disable=invalid-name + + def get_app_config_part(prefix: str) -> dict: """Get part of the app config. @@ -113,7 +134,7 @@ def create_command_log(dispatcher, registry, command, subcommand, params=()): ) -@sync_to_async +@database_sync_to_async def socket_check_and_enqueue_command(*args, **kwargs): """Calls check_and_enqueue_command() when in Socket mode.""" return check_and_enqueue_command(*args, **kwargs) diff --git a/poetry.lock b/poetry.lock index 02282a63..f859ee15 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "aiodns" diff --git a/pyproject.toml b/pyproject.toml index 73d8ce72..dcd5e56a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nautobot-chatops" -version = "3.1.2" +version = "3.1.3" description = "A app providing chatbot capabilities for Nautobot" authors = ["Network to Code, LLC "] license = "Apache-2.0"