Various endpoints that use JSON web tokens to fetch Cards and Dashboards.
The endpoints are the same as the ones in To use these endpoints:
| (ns metabase.api.embed (:require [compojure.core :refer [GET]] [medley.core :as m] [metabase.api.common :as api] [metabase.api.dataset :as api.dataset] [metabase.api.embed.common :as api.embed.common] [metabase.api.public :as api.public] [metabase.events :as events] [metabase.models.card :refer [Card]] [metabase.models.dashboard :refer [Dashboard]] [metabase.query-processor.card :as qp.card] [metabase.query-processor.middleware.constraints :as qp.constraints] [metabase.query-processor.pivot :as qp.pivot] [metabase.util :as u] [metabase.util.embed :as embed] [metabase.util.malli :as mu] [metabase.util.malli.schema :as ms] [toucan2.core :as t2])) |
(set! *warn-on-reflection* true) | |
(def ^:private ResourceId [:or ms/PositiveInt ms/NanoIdString]) (def ^:private Token [:map [:resource [:map [:question {:optional true} ResourceId] [:dashboard {:optional true} ResourceId]]] [:params :any]]) | |
If there's a value at | (defn- conditional-update-in [m path f] (if-let [value (get-in m path)] (assoc-in m path (f value)) m)) |
(mu/defn translate-token-ids :- Token "Translate `entity_id` keys to `card_id` and `dashboard_id` respectively." [unsigned :- Token] (-> unsigned (conditional-update-in [:resource :question] #(api.embed.common/->id :model/Card %)) (conditional-update-in [:resource :dashboard] #(api.embed.common/->id :model/Dashboard %)))) | |
Unsign a JWT and translate | (defn unsign-and-translate-ids [message] (translate-token-ids (embed/unsign message))) |
------------------------------------------- /api/embed/card endpoints -------------------------------------------- | |
/card/:token | (api/defendpoint GET "Fetch a Card via a JSON Web Token signed with the `embedding-secret-key`. Token should have the following format: {:resource {:question <card-id>}}" [token] (let [unsigned (unsign-and-translate-ids token)] (api.embed.common/check-embedding-enabled-for-card (embed/get-in-unsigned-token-or-throw unsigned [:resource :question])) (u/prog1 (api.embed.common/card-for-unsigned-token unsigned, :constraints [:enable_embedding true]) (events/publish-event! :event/card-read {:object-id (:id <>), :user-id api/*current-user-id*, :context :question})))) |
Run the query belonging to Card identified by | (defn ^:private run-query-for-unsigned-token-async [unsigned-token export-format query-params & {:keys [constraints qp] :or {constraints (qp.constraints/default-query-constraints) qp qp.card/process-query-for-card-default-qp} :as options}] (let [card-id (embed/get-in-unsigned-token-or-throw unsigned-token [:resource :question])] (api.embed.common/check-embedding-enabled-for-card card-id) (api.embed.common/process-query-for-card-with-params :export-format export-format :card-id card-id :token-params (embed/get-in-unsigned-token-or-throw unsigned-token [:params]) :embedding-params (t2/select-one-fn :embedding_params Card :id card-id) :query-params (api.embed.common/parse-query-params (dissoc query-params :format_rows :pivot_results)) :qp qp :constraints constraints :options options))) |
/card/:token/query | (api/defendpoint GET "Fetch the results of running a Card using a JSON Web Token signed with the `embedding-secret-key`. Token should have the following format: {:resource {:question <card-id>} :params <parameters>}" [token & query-params] (run-query-for-unsigned-token-async (unsign-and-translate-ids token) :api (api.embed.common/parse-query-params query-params))) |
(api/defendpoint GET ["/card/:token/query/:export-format", :export-format api.dataset/export-format-regex] "Like `GET /api/embed/card/query`, but returns the results as a file in the specified format." [token export-format format_rows pivot_results :as {:keys [query-params]}] {export-format (into [:enum] api.dataset/export-formats) format_rows [:maybe :boolean] pivot_results [:maybe :boolean]} (run-query-for-unsigned-token-async (unsign-and-translate-ids token) export-format (api.embed.common/parse-query-params (dissoc (m/map-keys keyword query-params) :format_rows :pivot_results)) :constraints nil :middleware {:process-viz-settings? true :js-int-to-string? false :format-rows? (or format_rows false) :pivot? (or pivot_results false)})) | |
----------------------------------------- /api/embed/dashboard endpoints ----------------------------------------- | |
/dashboard/:token | (api/defendpoint GET "Fetch a Dashboard via a JSON Web Token signed with the `embedding-secret-key`. Token should have the following format: {:resource {:dashboard <dashboard-id>}}" [token] (let [unsigned (unsign-and-translate-ids token)] (api.embed.common/check-embedding-enabled-for-dashboard (embed/get-in-unsigned-token-or-throw unsigned [:resource :dashboard])) (u/prog1 (api.embed.common/dashboard-for-unsigned-token unsigned, :constraints [:enable_embedding true]) (events/publish-event! :event/dashboard-read {:object-id (:id <>), :user-id api/*current-user-id*})))) |
Fetch the results of running a Card belonging to a Dashboard using a JSON Web Token signed with the
[[Token]] should have the following format: {:resource {:dashboard Additional dashboard parameters can be provided in the query string, but params in the JWT token take precedence. Returns a | (defn- process-query-for-dashcard-with-signed-token [token dashcard-id card-id export-format query-params & {:keys [constraints qp middleware] :or {constraints (qp.constraints/default-query-constraints) qp qp.card/process-query-for-card-default-qp}}] (let [unsigned-token (unsign-and-translate-ids token) dashboard-id (embed/get-in-unsigned-token-or-throw unsigned-token [:resource :dashboard])] (api.embed.common/check-embedding-enabled-for-dashboard dashboard-id) (api.embed.common/process-query-for-dashcard :export-format export-format :dashboard-id dashboard-id :dashcard-id dashcard-id :card-id card-id :embedding-params (t2/select-one-fn :embedding_params Dashboard :id dashboard-id) :token-params (embed/get-in-unsigned-token-or-throw unsigned-token [:params]) :query-params (api.embed.common/parse-query-params (dissoc query-params :format_rows :pivot_results)) :constraints constraints :qp qp :middleware middleware))) |
/dashboard/:token/dashcard/:dashcard-id/card/:card-id | (api/defendpoint GET "Fetch the results of running a Card belonging to a Dashboard using a JSON Web Token signed with the `embedding-secret-key`" [token dashcard-id card-id & query-params] {dashcard-id ms/PositiveInt card-id ms/PositiveInt} (u/prog1 (process-query-for-dashcard-with-signed-token token dashcard-id card-id :api (api.embed.common/parse-query-params query-params)) (events/publish-event! :event/card-read {:object-id card-id, :user-id api/*current-user-id*, :context :dashboard}))) |
+----------------------------------------------------------------------------------------------------------------+ | FieldValues, Search, Remappings | +----------------------------------------------------------------------------------------------------------------+ | |
-------------------------------------------------- Field Values -------------------------------------------------- | |
/card/:token/field/:field-id/values | (api/defendpoint GET "Fetch FieldValues for a Field that is referenced by an embedded Card." [token field-id] {field-id ms/PositiveInt} (let [unsigned-token (unsign-and-translate-ids token) card-id (embed/get-in-unsigned-token-or-throw unsigned-token [:resource :question])] (api.embed.common/check-embedding-enabled-for-card card-id) (api.public/card-and-field-id->values card-id field-id))) |
/dashboard/:token/field/:field-id/values | (api/defendpoint GET "Fetch FieldValues for a Field that is used as a param in an embedded Dashboard." [token field-id] {field-id ms/PositiveInt} (let [unsigned-token (unsign-and-translate-ids token) dashboard-id (embed/get-in-unsigned-token-or-throw unsigned-token [:resource :dashboard])] (api.embed.common/check-embedding-enabled-for-dashboard dashboard-id) (api.public/dashboard-and-field-id->values dashboard-id field-id))) |
--------------------------------------------------- Searching ---------------------------------------------------- | |
/card/:token/field/:field-id/search/:search-field-id | (api/defendpoint GET "Search for values of a Field that is referenced by an embedded Card." [token field-id search-field-id value limit] {field-id ms/PositiveInt search-field-id ms/PositiveInt value ms/NonBlankString limit [:maybe ms/PositiveInt]} (let [unsigned-token (unsign-and-translate-ids token) card-id (embed/get-in-unsigned-token-or-throw unsigned-token [:resource :question])] (api.embed.common/check-embedding-enabled-for-card card-id) (api.public/search-card-fields card-id field-id search-field-id value (when limit (Integer/parseInt limit))))) |
/dashboard/:token/field/:field-id/search/:search-field-id | (api/defendpoint GET "Search for values of a Field that is referenced by a Card in an embedded Dashboard." [token field-id search-field-id value limit] {field-id ms/PositiveInt search-field-id ms/PositiveInt value ms/NonBlankString limit [:maybe ms/PositiveInt]} (let [unsigned-token (unsign-and-translate-ids token) dashboard-id (embed/get-in-unsigned-token-or-throw unsigned-token [:resource :dashboard])] (api.embed.common/check-embedding-enabled-for-dashboard dashboard-id) (api.public/search-dashboard-fields dashboard-id field-id search-field-id value (when limit (Integer/parseInt limit))))) |
--------------------------------------------------- Remappings --------------------------------------------------- | |
/card/:token/field/:field-id/remapping/:remapped-id | (api/defendpoint GET "Fetch remapped Field values. This is the same as `GET /api/field/:id/remapping/:remapped-id`, but for use with embedded Cards." [token field-id remapped-id value] {field-id ms/PositiveInt remapped-id ms/PositiveInt value ms/NonBlankString} (let [unsigned-token (unsign-and-translate-ids token) card-id (embed/get-in-unsigned-token-or-throw unsigned-token [:resource :question])] (api.embed.common/check-embedding-enabled-for-card card-id) (api.public/card-field-remapped-values card-id field-id remapped-id value))) |
/dashboard/:token/field/:field-id/remapping/:remapped-id | (api/defendpoint GET "Fetch remapped Field values. This is the same as `GET /api/field/:id/remapping/:remapped-id`, but for use with embedded Dashboards." [token field-id remapped-id value] {field-id ms/PositiveInt remapped-id ms/PositiveInt value ms/NonBlankString} (let [unsigned-token (unsign-and-translate-ids token) dashboard-id (embed/get-in-unsigned-token-or-throw unsigned-token [:resource :dashboard])] (api.embed.common/check-embedding-enabled-for-dashboard dashboard-id) (api.public/dashboard-field-remapped-values dashboard-id field-id remapped-id value))) |
(api/defendpoint GET ["/dashboard/:token/dashcard/:dashcard-id/card/:card-id/:export-format" :export-format api.dataset/export-format-regex] "Fetch the results of running a Card belonging to a Dashboard using a JSON Web Token signed with the `embedding-secret-key` return the data in one of the export formats" [token export-format dashcard-id card-id format_rows pivot_results :as {:keys [query-params]}] {dashcard-id ms/PositiveInt card-id ms/PositiveInt format_rows [:maybe :boolean] pivot_results [:maybe :boolean] export-format (into [:enum] api.dataset/export-formats)} (process-query-for-dashcard-with-signed-token token dashcard-id card-id export-format (api.embed.common/parse-query-params (dissoc (m/map-keys keyword query-params) :format_rows)) :constraints nil :middleware {:process-viz-settings? true :js-int-to-string? false :format-rows? (or format_rows false) :pivot? (or pivot_results false)})) | |
----------------------------------------------- Param values ------------------------------------------------- | |
embedding parameters in variables whose name includes | |
/dashboard/:token/params/:param-key/values | (api/defendpoint GET "Embedded version of chain filter values endpoint." [token param-key :as {:keys [query-params]}] (api.embed.common/dashboard-param-values token param-key nil (api.embed.common/parse-query-params query-params))) |
/dashboard/:token/params/:param-key/search/:prefix | (api/defendpoint GET "Embedded version of chain filter search endpoint." [token param-key prefix :as {:keys [query-params]}] (api.embed.common/dashboard-param-values token param-key prefix (api.embed.common/parse-query-params query-params))) |
/card/:token/params/:param-key/values | (api/defendpoint GET "Embedded version of api.card filter values endpoint." [token param-key] (let [unsigned (unsign-and-translate-ids token) card-id (embed/get-in-unsigned-token-or-throw unsigned [:resource :question]) card (t2/select-one Card :id card-id)] (api.embed.common/check-embedding-enabled-for-card card-id) (api.embed.common/card-param-values {:unsigned-token unsigned :card card :param-key param-key}))) |
/card/:token/params/:param-key/search/:prefix | (api/defendpoint GET "Embedded version of chain filter search endpoint." [token param-key prefix] (let [unsigned (unsign-and-translate-ids token) card-id (embed/get-in-unsigned-token-or-throw unsigned [:resource :question]) card (t2/select-one Card :id card-id)] (api.embed.common/check-embedding-enabled-for-card card-id) (api.embed.common/card-param-values {:unsigned-token unsigned :card card :param-key param-key :search-prefix prefix}))) |
/pivot/card/:token/query | (api/defendpoint GET "Fetch the results of running a Card using a JSON Web Token signed with the `embedding-secret-key`. Token should have the following format: {:resource {:question <card-id>} :params <parameters>}" [token & query-params] (run-query-for-unsigned-token-async (unsign-and-translate-ids token) :api (api.embed.common/parse-query-params query-params) :qp qp.pivot/run-pivot-query)) |
/pivot/dashboard/:token/dashcard/:dashcard-id/card/:card-id | (api/defendpoint GET "Fetch the results of running a Card belonging to a Dashboard using a JSON Web Token signed with the `embedding-secret-key`" [token dashcard-id card-id & query-params] {dashcard-id ms/PositiveInt card-id ms/PositiveInt} (u/prog1 (process-query-for-dashcard-with-signed-token token dashcard-id card-id :api (api.embed.common/parse-query-params query-params) :qp qp.pivot/run-pivot-query) (events/publish-event! :event/card-read {:object-id card-id, :user-id api/*current-user-id*, :context :dashboard}))) |
(api/define-routes) | |