From 3a60888f79ac05ab556012a8eda4518ffc04d7dc Mon Sep 17 00:00:00 2001
From: Allen Gilliland <agilliland@gmail.com>
Date: Mon, 21 Mar 2016 14:54:59 -0700
Subject: [PATCH] update slack api settings endpoint to handle
 `metabot-enabled` setting and add a new metabot-lifecycle event handler which
 can listen for the appropriate events (:settings-update) and trigger
 startup/shutdown of metabot as needed.

---
 src/metabase/api/slack.clj                | 14 ++++---
 src/metabase/events/metabot_lifecycle.clj | 47 +++++++++++++++++++++++
 src/metabase/integrations/slack.clj       |  8 +---
 src/metabase/models/setting.clj           |  3 +-
 4 files changed, 58 insertions(+), 14 deletions(-)
 create mode 100644 src/metabase/events/metabot_lifecycle.clj

diff --git a/src/metabase/api/slack.clj b/src/metabase/api/slack.clj
index 006e971b294..810b635a94f 100644
--- a/src/metabase/api/slack.clj
+++ b/src/metabase/api/slack.clj
@@ -3,18 +3,20 @@
   (:require [compojure.core :refer [PUT]]
             [metabase.api.common :refer :all]
             [metabase.config :as config]
-            [metabase.integrations.slack :as slack]))
+            [metabase.integrations.slack :as slack]
+            [metabase.models.setting :as setting]))
 
 (defendpoint PUT "/settings"
-  "Update the `slack-token`. You must be a superuser to do this."
-  [:as {{slack-token :slack-token} :body}]
-  {slack-token [Required NonEmptyString]}
+  "Update Slack related settings. You must be a superuser to do this."
+  [:as {{slack-token :slack-token, metabot-enabled :metabot-enabled, :as slack-settings} :body}]
+  {slack-token     [Required NonEmptyString]
+   metabot-enabled [Required]}
   (check-superuser)
   (try
-    ;; just check that channels.list doesn't throw an exception (that the connection works)
+    ;; just check that channels.list doesn't throw an exception (a.k.a. that the token works)
     (when-not config/is-test?
       (slack/GET :channels.list, :exclude_archived 1, :token slack-token))
-    (slack/slack-token slack-token)
+    (setting/set-all slack-settings)
     {:ok true}
     (catch clojure.lang.ExceptionInfo info
       {:status 400, :body (ex-data info)})))
diff --git a/src/metabase/events/metabot_lifecycle.clj b/src/metabase/events/metabot_lifecycle.clj
new file mode 100644
index 00000000000..bc6406c0149
--- /dev/null
+++ b/src/metabase/events/metabot_lifecycle.clj
@@ -0,0 +1,47 @@
+(ns metabase.events.metabot-lifecycle
+  (:require [clojure.core.async :as async]
+            [clojure.tools.logging :as log]
+            [metabase.db :as db]
+            [metabase.driver :as driver]
+            [metabase.events :as events]
+            [metabase.metabot :as metabot]
+            [metabase.models.database :refer [Database]]))
+
+
+(def ^:const ^:private metabot-lifecycle-topics
+  "The `Set` of event topics which are subscribed to for use in metabot lifecycle."
+  #{:settings-update})
+
+(def ^:private metabot-lifecycle-channel
+  "Channel for receiving event notifications we want to subscribe to for metabot lifecycle events."
+  (async/chan))
+
+
+;;; ## ---------------------------------------- EVENT PROCESSING ----------------------------------------
+
+
+(defn process-metabot-lifecycle-event
+  "Handle processing for a single event notification received on the metabot-lifecycle-channel"
+  [metabot-lifecycle-event]
+  ;; try/catch here to prevent individual topic processing exceptions from bubbling up.  better to handle them here.
+  (when-let [{topic :topic object :item} metabot-lifecycle-event]
+    (try
+      ;; if someone updated our slack-token, or metabot was enabled/disabled then react accordingly
+      (let [{:keys [slack-token metabot-enabled]} object]
+        (cond
+          (and (contains? object :metabot-enabled)
+               (not (= "true" metabot-enabled)))   (metabot/stop-metabot!)
+          (and (contains? object :slack-token)
+               (seq slack-token))                  (metabot/start-metabot!)))
+      (catch Throwable e
+        (log/warn (format "Failed to process driver notifications event. %s" topic) e)))))
+
+
+
+;;; ## ---------------------------------------- LIFECYLE ----------------------------------------
+
+
+(defn events-init
+  "Automatically called during startup; start event listener for metabot lifecycle events."
+  []
+  (events/start-event-listener metabot-lifecycle-topics metabot-lifecycle-channel process-metabot-lifecycle-event))
diff --git a/src/metabase/integrations/slack.clj b/src/metabase/integrations/slack.clj
index b72d3218185..a27bb56a57a 100644
--- a/src/metabase/integrations/slack.clj
+++ b/src/metabase/integrations/slack.clj
@@ -7,13 +7,7 @@
 
 
 ;; Define a setting which captures our Slack api token
-(defsetting slack-token "Slack API bearer token obtained from https://api.slack.com/web#authentication" nil
-  :setter (fn [new-value]
-            (setting/set* :slack-token new-value)
-            (require 'metabase.metabot)
-            ((ns-resolve 'metabase.metabot (if (seq new-value)
-                                              'start-metabot!
-                                              'stop-metabot!)))))
+(defsetting slack-token "Slack API bearer token obtained from https://api.slack.com/web#authentication" nil)
 
 (def ^:private ^:const ^String slack-api-base-url "https://slack.com/api")
 (def ^:private ^:const ^String files-channel-name "metabase_files")
diff --git a/src/metabase/models/setting.clj b/src/metabase/models/setting.clj
index 689c2b8b9bb..aa696d5ea94 100644
--- a/src/metabase/models/setting.clj
+++ b/src/metabase/models/setting.clj
@@ -5,6 +5,7 @@
             [korma.core :as k]
             [metabase.config :as config]
             [metabase.db :refer [exists? sel del]]
+            [metabase.events :as events]
             [metabase.models [common :as common]
                              [interface :as i]]
             [metabase.setup :as setup]
@@ -128,7 +129,7 @@
     (if-let [v (clojure.core/get settings k)]
       (set k v)
       (delete k)))
-  settings)
+  (events/publish-event :settings-update settings))
 
 (defn set*
   "Set the value of a `Setting`, deleting it if VALUE is `nil` or an empty string."
-- 
GitLab