From 18bc3f29cfdd95c1215b9a91ddb3d5105524c3b5 Mon Sep 17 00:00:00 2001
From: Tim Macdonald <tim@metabase.com>
Date: Fri, 17 Feb 2023 11:56:47 +0000
Subject: [PATCH] Add action/:id/execute endpoint (#28335)

---
 src/metabase/api/action.clj       | 12 +++++++++++
 src/metabase/api/public.clj       |  6 ++----
 test/metabase/api/action_test.clj | 36 +++++++++++++++++++++++++++++--
 3 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/src/metabase/api/action.clj b/src/metabase/api/action.clj
index 379a359b550..88241b93f84 100644
--- a/src/metabase/api/action.clj
+++ b/src/metabase/api/action.clj
@@ -3,6 +3,7 @@
   (:require
    [compojure.core :as compojure :refer [POST]]
    [metabase.actions :as actions]
+   [metabase.actions.execution :as actions.execution]
    [metabase.actions.http-action :as http-action]
    [metabase.api.common :as api]
    [metabase.api.common.validation :as validation]
@@ -163,4 +164,15 @@
   (db/update! Action id :public_uuid nil, :made_public_by_id nil)
   {:status 204, :body nil})
 
+(api/defendpoint POST "/:id/execute"
+  "Execute the Action.
+
+   `parameters` should be the mapped dashboard parameters with values."
+  [id :as {{:keys [parameters], :as _body} :body}]
+  {id       pos-int?
+   parameters [:maybe [:map-of :keyword any?]]}
+  (-> (action/select-action :id id :archived false)
+      (api/check-404)
+      (actions.execution/execute-action! (update-keys parameters name))))
+
 (api/define-routes)
diff --git a/src/metabase/api/public.clj b/src/metabase/api/public.clj
index 270e3c99e8f..86ffaa343c0 100644
--- a/src/metabase/api/public.clj
+++ b/src/metabase/api/public.clj
@@ -264,8 +264,7 @@
 (api/defendpoint-schema POST "/dashboard/:uuid/dashcard/:dashcard-id/execute"
   "Execute the associated Action in the context of a `Dashboard` and `DashboardCard` that includes it.
 
-   `parameters` should be the mapped dashboard parameters with values.
-   `extra_parameters` should be the extra, user entered parameter values."
+   `parameters` should be the mapped dashboard parameters with values."
   [uuid dashcard-id :as {{:keys [parameters], :as _body} :body}]
   {dashcard-id su/IntGreaterThanZero
    parameters (s/maybe {s/Keyword s/Any})}
@@ -564,8 +563,7 @@
 (api/defendpoint POST "/action/:uuid/execute"
   "Execute the Action.
 
-   `parameters` should be the mapped dashboard parameters with values.
-   `extra_parameters` should be the extra, user entered parameter values."
+   `parameters` should be the mapped dashboard parameters with values."
   [uuid :as {{:keys [parameters], :as _body} :body}]
   {uuid       ms/UUIDString
    parameters [:maybe [:map-of :keyword any?]]}
diff --git a/test/metabase/api/action_test.clj b/test/metabase/api/action_test.clj
index a6342f3ea86..96b7d98fa3d 100644
--- a/test/metabase/api/action_test.clj
+++ b/test/metabase/api/action_test.clj
@@ -2,8 +2,7 @@
   (:require
    [clojure.test :refer :all]
    [metabase.api.action :as api.action]
-   [metabase.models :refer [Card]]
-   [metabase.models.action :refer [Action]]
+   [metabase.models :refer [Action Card Database]]
    [metabase.models.user :as user]
    [metabase.test :as mt]
    [metabase.util :as u]
@@ -364,3 +363,36 @@
         (testing "Test that we get a 404 if Action doesn't exist"
           (is (= "Not found."
                  (mt/user-http-request :crowberto :delete 404 (format "action/%d/public_link" Integer/MAX_VALUE)))))))))
+
+(deftest execute-action-test
+  (mt/with-actions-test-data-and-actions-enabled
+    (mt/with-actions [{:keys [action-id]} unshared-action-opts]
+      (testing "Action execution"
+        (is (=? {:rows-affected 1}
+                (mt/user-http-request :crowberto
+                                      :post 200
+                                      (format "action/%s/execute" action-id)
+                                      {:parameters {:id 1 :name "European"}})))))
+    (mt/with-actions [{:keys [action-id]} (assoc unshared-action-opts :archived true)]
+      (testing "Check that we get a 404 if the action is archived"
+        (is (= "Not found."
+               (mt/user-http-request :crowberto
+                                     :post 404
+                                     (format "action/%s/execute" action-id)
+                                     {:parameters {:id 1 :name "European"}})))))
+    (mt/with-actions [{:keys [action-id]} unshared-action-opts]
+      (let [nonexistent-id (inc (db/select-one-id Action {:order-by [[:id :desc]]}))]
+        (testing "Check that we get a 404 if the action doesn't exist"
+          (is (= "Not found."
+                 (mt/user-http-request :crowberto
+                                       :post 404
+                                       (format "action/%s/execute" nonexistent-id)
+                                       {:parameters {:id 1 :name "European"}})))))
+      (testing "Check that we get a 400 if actions are disabled for the database."
+        (mt/with-temp-vals-in-db Database (mt/id) {:settings {:database-enable-actions false}}
+          (is (= "Actions are not enabled."
+                 (:cause
+                  (mt/user-http-request :crowberto
+                                        :post 400
+                                        (format "action/%s/execute" action-id)
+                                        {:parameters {:id 1 :name "European"}})))))))))
-- 
GitLab