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 [metabase.api.common :as api] [metabase.api.dataset :as api.dataset] [metabase.api.embed.common :as api.embed.common] [metabase.api.macros :as api.macros] [metabase.events :as events] [metabase.public-sharing.api :as api.public] [metabase.query-processor.card :as qp.card] [metabase.query-processor.middleware.constraints :as qp.constraints] [metabase.query-processor.pivot :as qp.pivot] [metabase.tiles.api :as api.tiles] [metabase.util :as u] [metabase.util.embed :as embed] [metabase.util.json :as json] [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 -------------------------------------------- | |
(api.macros/defendpoint :get "/card/:token" "Fetch a Card via a JSON Web Token signed with the `embedding-secret-key`. Token should have the following format: {:resource {:question <card-id>}}" [{:keys [token]} :- [:map [:token string?]]] (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 :model/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))) |
(api.macros/defendpoint :get "/card/:token/query" "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>}" [{:keys [token]} :- [:map [:token string?]] query-params :- :map] (run-query-for-unsigned-token-async (unsign-and-translate-ids token) :api (api.embed.common/parse-query-params query-params))) | |
(api.macros/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." [{:keys [token export-format]} :- [:map [:token string?] [:export-format (into [:enum] api.dataset/export-formats)]] {format-rows? :format_rows pivot? :pivot_results :as query-params} :- [:map [:format_rows {:default false} :boolean] [:pivot_results {:default false} :boolean]]] (run-query-for-unsigned-token-async (unsign-and-translate-ids token) export-format (api.embed.common/parse-query-params (dissoc query-params :format_rows :pivot_results)) :constraints nil :middleware {:process-viz-settings? true :js-int-to-string? false :format-rows? format-rows? :pivot? pivot?})) | |
----------------------------------------- /api/embed/dashboard endpoints ----------------------------------------- | |
(api.macros/defendpoint :get "/dashboard/:token" "Fetch a Dashboard via a JSON Web Token signed with the `embedding-secret-key`. Token should have the following format: {:resource {:dashboard <dashboard-id>}}" [{:keys [token]} :- [:map [:token string?]]] (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 :model/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))) |
(api.macros/defendpoint :get "/dashboard/:token/dashcard/:dashcard-id/card/:card-id" "Fetch the results of running a Card belonging to a Dashboard using a JSON Web Token signed with the `embedding-secret-key`" [{:keys [token dashcard-id card-id]} :- [:map [:token string?] [:dashcard-id ms/PositiveInt] [:card-id ms/PositiveInt]] query-params :- :map] (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 -------------------------------------------------- | |
(api.macros/defendpoint :get "/card/:token/field/:field-id/values" "Fetch FieldValues for a Field that is referenced by an embedded Card." [{:keys [token field-id]} :- [:map [:token string?] [: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))) | |
(api.macros/defendpoint :get "/dashboard/:token/field/:field-id/values" "Fetch FieldValues for a Field that is used as a param in an embedded Dashboard." [{:keys [token field-id]} :- [:map [:token string?] [: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 ---------------------------------------------------- | |
(api.macros/defendpoint :get "/card/:token/field/:field-id/search/:search-field-id" "Search for values of a Field that is referenced by an embedded Card." [{:keys [token field-id search-field-id]} :- [:map [:token string?] [:field-id ms/PositiveInt] [:search-field-id ms/PositiveInt]] {:keys [value limit]} :- [:map [:value ms/NonBlankString] [:limit {:optional true} [: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))))) | |
(api.macros/defendpoint :get "/dashboard/:token/field/:field-id/search/:search-field-id" "Search for values of a Field that is referenced by a Card in an embedded Dashboard." [{:keys [token field-id search-field-id]} :- [:map [:token string?] [:field-id ms/PositiveInt] [:search-field-id ms/PositiveInt]] {:keys [value limit]} :- [:map [:value ms/NonBlankString] [:limit {:optional true} [: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 --------------------------------------------------- | |
(api.macros/defendpoint :get "/card/:token/field/:field-id/remapping/:remapped-id" "Fetch remapped Field values. This is the same as `GET /api/field/:id/remapping/:remapped-id`, but for use with embedded Cards." [{:keys [token field-id remapped-id]} :- [:map [:token string?] [:field-id ms/PositiveInt] [:remapped-id ms/PositiveInt]] {:keys [value]} :- [:map [: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))) | |
(api.macros/defendpoint :get "/dashboard/:token/field/:field-id/remapping/:remapped-id" "Fetch remapped Field values. This is the same as `GET /api/field/:id/remapping/:remapped-id`, but for use with embedded Dashboards." [{:keys [token field-id remapped-id]} :- [:map [:token string?] [:field-id ms/PositiveInt] [:remapped-id ms/PositiveInt]] {:keys [value]} :- [:map [: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.macros/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" [{:keys [token dashcard-id card-id export-format]} :- [:map [:dashcard-id ms/PositiveInt] [:card-id ms/PositiveInt] [:export-format (into [:enum] api.dataset/export-formats)]] {format-rows? :format_rows pivot? :pivot_results :as query-params} :- [:map [:format_rows {:default false} :boolean] [:pivot_results {:default false} :boolean]]] (process-query-for-dashcard-with-signed-token token dashcard-id card-id export-format (api.embed.common/parse-query-params (dissoc query-params :format_rows :pivot_results)) :constraints nil :middleware {:process-viz-settings? true :js-int-to-string? false :format-rows? format-rows? :pivot? pivot?})) | |
----------------------------------------------- Param values ------------------------------------------------- | |
embedding parameters in variables whose name includes | |
(api.macros/defendpoint :get "/dashboard/:token/params/:param-key/values" "Embedded version of chain filter values endpoint." [{:keys [token param-key]} :- [:map [:token string?] [:param-key string?]] query-params] (api.embed.common/dashboard-param-values token param-key nil (api.embed.common/parse-query-params query-params))) | |
(api.macros/defendpoint :get "/dashboard/:token/params/:param-key/search/:prefix" "Embedded version of chain filter search endpoint." [{:keys [token param-key prefix]} query-params] (api.embed.common/dashboard-param-values token param-key prefix (api.embed.common/parse-query-params query-params))) | |
(api.macros/defendpoint :get "/card/:token/params/:param-key/values" "Embedded version of api.card filter values endpoint." [{:keys [token param-key]} :- [:map [:token string?] [:param-key string?]]] (let [unsigned (unsign-and-translate-ids token) card-id (embed/get-in-unsigned-token-or-throw unsigned [:resource :question]) card (t2/select-one :model/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}))) | |
(api.macros/defendpoint :get "/card/:token/params/:param-key/search/:prefix" "Embedded version of chain filter search endpoint." [{:keys [token param-key prefix]}] :- [:map [:token string?] [:param-key string?] [:prefix string?]] (let [unsigned (unsign-and-translate-ids token) card-id (embed/get-in-unsigned-token-or-throw unsigned [:resource :question]) card (t2/select-one :model/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}))) | |
(api.macros/defendpoint :get "/pivot/card/:token/query" "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>}" [{:keys [token]} :- [:map [:token string?]] query-params :- :map] (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)) | |
(api.macros/defendpoint :get "/pivot/dashboard/:token/dashcard/:dashcard-id/card/:card-id" "Fetch the results of running a Card belonging to a Dashboard using a JSON Web Token signed with the `embedding-secret-key`" [{:keys [token dashcard-id card-id]} :- [:map [:token string?] [:dashcard-id ms/PositiveInt] [:card-id ms/PositiveInt]] query-params :- :map] (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.macros/defendpoint :get "/tiles/card/:token/:zoom/:x/:y/:lat-field/:lon-field" "Generates a single tile image for an embedded Card using the map visualization." [{:keys [token zoom x y lat-field lon-field]} :- [:merge :api.tiles/route-params [:map [:token string?]]] {:keys [parameters]} :- [:map [:parameters {:optional true} ms/JSONString]]] (let [unsigned (unsign-and-translate-ids token) card-id (embed/get-in-unsigned-token-or-throw unsigned [:resource :question]) parameters (json/decode+kw parameters)] (api.embed.common/check-embedding-enabled-for-card card-id) (api.tiles/process-tiles-query-for-card card-id parameters zoom x y lat-field lon-field))) | |
(api.macros/defendpoint :get "/tiles/dashboard/:token/dashcard/:dashcard-id/card/:card-id/:zoom/:x/:y/:lat-field/:lon-field" "Generates a single tile image for a Card on an embedded Dashboard using the map visualization." [{:keys [token dashcard-id card-id zoom x y lat-field lon-field]} :- [:merge :api.tiles/route-params [:map [:token string?] [:dashcard-id ms/PositiveInt] [:card-id ms/PositiveInt]]] {:keys [parameters]} :- [:map [:parameters {:optional true} ms/JSONString]]] (let [unsigned (unsign-and-translate-ids token) dashboard-id (embed/get-in-unsigned-token-or-throw unsigned [:resource :dashboard]) parameters (json/decode+kw parameters)] (api.tiles/process-tiles-query-for-dashcard dashboard-id dashcard-id card-id parameters zoom x y lat-field lon-field))) | |