/api/alert endpoints.

Deprecated: will soon be migrated to notification APIs.

(ns ^:deprecated metabase.pulse.api.alert
  #_{:clj-kondo/ignore [:metabase/modules]}
  (:require
   [clojure.set :as set]
   [metabase.api.common :as api]
   [metabase.api.macros :as api.macros]
   [metabase.api.notification :as api.notification]
   [metabase.config :as config]
   [metabase.plugins.classloader :as classloader]
   [metabase.util.cron :as u.cron]
   [metabase.util.malli.schema :as ms]
   [toucan2.core :as t2]))
(set! *warn-on-reflection* true)
(when config/ee-available?
  (classloader/require 'metabase-enterprise.advanced-permissions.common))

Convert a notification to the legacy pulse structure for backward compatibility.

(defn- notification->pulse
  [notification]
  (let [subscription      (-> notification :subscriptions first)
        notification-card (-> notification :payload)
        card              (->> notification :payload :card_id (t2/select-one :model/Card))]
    (merge
     (select-keys notification [:id :creator_id :creator :created_at :updated_at])
     {:name                nil
      :alert_condition     (if (-> notification-card :send_condition (= :has_result)) "rows" "goal")
      :alert_above_goal    (if (-> notification-card :send_condition (= :goal_above)) true nil)
      :alert_first_only    (-> notification :payload :send_once)
      :archived            (not (:active notification))
      :collection_position nil
      :collection_id       nil
      :skip_if_empty       true
      :parameters          []
      :dashboard_id        nil
      :card                (merge
                            (select-keys card [:name :description :collection_id :display])
                            {:format_rows       true
                             :include_xls       false
                             :include_csv       true
                             :pivot_results     false
                             :dashboard_id      nil
                             :dashboard_card_id nil
                             :parameter_mappings nil})
      :channels            (map (fn [handler]
                                  (let [user-recipients  (->> handler
                                                              :recipients
                                                              (filter #(= :notification-recipient/user (:type %)))
                                                              (map :user)
                                                              (map #(select-keys % [:email :last_name :first_name :id :common_name])))
                                        ;; for external emails and slack channel
                                        value-recipients (->> handler
                                                              :recipients
                                                              (filter #(= :notification-recipient/raw-value (:type %)))
                                                              (map :details))]
                                    (merge
                                     (when subscription
                                       (select-keys (u.cron/cron-string->schedule-map (:cron_schedule subscription))
                                                    [:schedule_type :schedule_hour :schedule_day :schedule_frame]))
                                     {:id           (:id handler)
                                      :recipients   (if (= :channel/email (:channel_type handler))
                                                      (concat (map #(set/rename-keys % {:value :email}) value-recipients) user-recipients)
                                                      [])
                                      :channel_type (name (:channel_type handler))
                                      :channel_id   (:channel_id handler)
                                      :enabled      (:active handler)
                                      :details      (case (:channel_type handler)
                                                      :channel/slack
                                                      {:channel (-> value-recipients first :value)}
                                                      :channel/email
                                                      {:emails (map :value value-recipients)}
                                                      {})})))
                                (:handlers notification))})))
(api.macros/defendpoint :get "/"
  "Fetch alerts which the current user has created or will receive, or all alerts if the user is an admin.
  The optional `user_id` will return alerts created by the corresponding user, but is ignored for non-admin users."
  [_route-params
   {:keys [archived user_id]} :- [:map
                                  [:archived {:default false} [:maybe ms/BooleanValue]]
                                  [:user_id  {:optional true} [:maybe ms/PositiveInt]]]]
  (let [user-id (if api/*is-superuser?*
                  user_id
                  api/*current-user-id*)]
    (->> (api.notification/list-notifications
          {:payload_type   :notification/card
           :legacy-user-id user-id
           :legacy-active  (not archived)})
         (map notification->pulse)
         (remove nil?))))
(api.macros/defendpoint :get "/:id"
  "Fetch an alert by ID"
  [{:keys [id]} :- [:map
                    [:id ms/PositiveInt]]]
  (-> (api.notification/get-notification id)
      api/read-check
      notification->pulse))
(api.macros/defendpoint :delete "/:id/subscription"
  "For users to unsubscribe themselves from the given alert."
  [{:keys [id]} :- [:map
                    [:id ms/PositiveInt]]]
  (api.notification/unsubscribe-user! id api/*current-user-id*)
  api/generic-204-no-content)