Random utilty endpoints for things that don't belong anywhere else in particular, e.g. endpoints for certain admin page tasks.

(ns metabase.api.util
  (:require
   [clj-http.client :as http]
   [compojure.core :refer [GET POST]]
   [crypto.random :as crypto-random]
   [environ.core :refer [env]]
   [metabase.analytics.prometheus :as prometheus]
   [metabase.analytics.stats :as stats]
   [metabase.api.common :as api]
   [metabase.api.common.validation :as validation]
   [metabase.api.embed.common :as api.embed.common]
   [metabase.config :as config]
   [metabase.logger :as logger]
   [metabase.public-settings.premium-features :as premium-features]
   [metabase.troubleshooting :as troubleshooting]
   [metabase.util.json :as json]
   [metabase.util.log :as log]
   [metabase.util.malli :as mu]
   [metabase.util.malli.schema :as ms]
   [ring.util.response :as response]))

/password_check

(api/defendpoint POST 
  "Endpoint that checks if the supplied password meets the currently configured password complexity rules."
  [:as {{:keys [password]} :body}]
  {password ms/ValidPassword} ;; if we pass the su/ValidPassword test we're g2g
  {:valid true})

/logs

(api/defendpoint GET 
  "Logs."
  []
  (validation/check-has-application-permission :monitoring)
  (logger/messages))

/stats

(api/defendpoint GET 
  "Anonymous usage stats. Endpoint for testing, and eventually exposing this to instance admins to let them see
  what is being phoned home."
  []
  (validation/check-has-application-permission :monitoring)
  (stats/legacy-anonymous-usage-stats))

/random_token

(api/defendpoint GET 
  "Return a cryptographically secure random 32-byte token, encoded as a hexadecimal string.
   Intended for use when creating a value for `embedding-secret-key`."
  []
  {:token (crypto-random/hex 32)})

Product feedback url. When not prod, reads MB_PRODUCT_FEEDBACK_URL from the environment to prevent development feedback from hitting the endpoint.

(defn- product-feedback-url
  []
  (if config/is-prod?
    "https://prod-feedback.metabase.com/api/v1/crm/product-feedback"
    (env :mb-product-feedback-url)))

Sends the feedback to the api endpoint

(mu/defn send-feedback!
  [comments :- [:maybe ms/NonBlankString]
   source :- ms/NonBlankString
   email :- [:maybe ms/NonBlankString]]
  (try (http/post (product-feedback-url)
                  {:content-type :json
                   :body         (json/encode {:comments comments
                                               :source   source
                                               :email    email})})
       (catch Exception e
         (log/warn e)
         (throw e))))

/product-feedback

(api/defendpoint POST 
  "Endpoint to provide feedback from the product"
  [:as {{:keys [comments source email]} :body}]
  {comments [:maybe ms/NonBlankString]
   source ms/NonBlankString
   email [:maybe ms/NonBlankString]}
  (future (send-feedback! comments source email))
  api/generic-204-no-content)

/bugreportdetails

(api/defendpoint GET 
  "Returns version and system information relevant to filing a bug report against Metabase."
  []
  (validation/check-has-application-permission :monitoring)
  (cond-> {:metabase-info (troubleshooting/metabase-info)}
    (not (premium-features/is-hosted?))
    (assoc :system-info (troubleshooting/system-info))))

/diagnosticinfo/connectionpool_info

(api/defendpoint GET 
  "Returns database connection pool info for the current Metabase instance."
  []
  (validation/check-has-application-permission :monitoring)
  (let [pool-info (prometheus/connection-pool-info)
        headers   {"Content-Disposition" "attachment; filename=\"connection_pool_info.json\""}]
    (assoc (response/response {:connection-pools pool-info}) :headers headers, :status 200)))

/entity_id

(api/defendpoint POST 
  "Translate entity IDs to model IDs."
  [:as {{:keys [entity_ids]} :body}]
  {entity_ids :map}
  {:entity_ids (api.embed.common/model->entity-ids->ids entity_ids)})
(api/define-routes)