Skip to content
Snippets Groups Projects
Unverified Commit f4ad5e09 authored by Cam Saul's avatar Cam Saul
Browse files

merge release-0.27.0 into master

parent 4bf97c27
No related branches found
No related tags found
No related merge requests found
#!/usr/bin/env bash
VERSION="v0.27.0-snapshot"
VERSION="v0.28.0-snapshot"
# dynamically pull more interesting stuff from latest git commit
HASH=$(git show-ref --head --hash=7 head) # first 7 letters of hash should be enough; that's what GitHub uses
......
......@@ -5,7 +5,7 @@ import Button from "metabase/components/Button.jsx";
const DownloadButton = ({ className, style, children, method, url, params, extensions, ...props }) =>
<form className={className} style={style} method={method} action={url}>
{ Object.entries(params).map(([name, value]) =>
{ params && Object.entries(params).map(([name, value]) =>
<input key={name} type="hidden" name={name} value={value} />
)}
<Button
......
import React from "react";
import PropTypes from "prop-types";
import { t } from 'c-3po';
import { parse as urlParse } from "url";
import querystring from "querystring";
import PopoverWithTrigger from "metabase/components/PopoverWithTrigger.jsx";
import Icon from "metabase/components/Icon.jsx";
import DownloadButton from "metabase/components/DownloadButton.jsx";
......@@ -35,7 +39,7 @@ const QueryDownloadWidget = ({ className, card, result, uuid, token }) =>
<div className="flex flex-row mt2">
{EXPORT_FORMATS.map(type =>
uuid ?
<PublicQueryButton key={type} type={type} uuid={uuid} className="mr1 text-uppercase text-default" />
<PublicQueryButton key={type} type={type} uuid={uuid} result={result} className="mr1 text-uppercase text-default" />
: token ?
<EmbedQueryButton key={type} type={type} token={token} className="mr1 text-uppercase text-default" />
: card && card.id ?
......@@ -69,25 +73,38 @@ const SavedQueryButton = ({ className, type, result: { json_query }, card }) =>
{type}
</DownloadButton>
const PublicQueryButton = ({ className, type, uuid }) =>
const PublicQueryButton = ({ className, type, uuid, result: { json_query }}) =>
<DownloadButton
className={className}
method="GET"
url={Urls.publicCard(uuid, type)}
params={{ parameters: JSON.stringify(json_query.parameters) }}
extensions={[type]}
>
{type}
</DownloadButton>
const EmbedQueryButton = ({ className, type, token }) =>
<DownloadButton
className={className}
method="GET"
url={Urls.embedCard(token, type)}
extensions={[type]}
>
{type}
</DownloadButton>
const EmbedQueryButton = ({ className, type, token }) => {
// Parse the query string part of the URL (e.g. the `?key=value` part) into an object. We need to pass them this
// way to the `DownloadButton` because it's a form which means we need to insert a hidden `<input>` for each param
// we want to pass along. For whatever wacky reason the /api/embed endpoint expect params like ?key=value instead
// of like ?params=<json-encoded-params-array> like the other endpoints do.
const query = urlParse(window.location.href).query; // get the part of the URL that looks like key=value
const params = query && querystring.parse(query); // expand them out into a map
return (
<DownloadButton
className={className}
method="GET"
url={Urls.embedCard(token, type)}
params={params}
extensions={[type]}
>
{type}
</DownloadButton>
);
}
QueryDownloadWidget.propTypes = {
className: PropTypes.string,
......
......@@ -19,7 +19,7 @@
[activity :refer [Activity]]
[card :refer [Card]]
[dashboard-card :refer [DashboardCard]]
[database :refer [Database]]
[database :refer [Database virtual-id]]
[field :refer [Field]]
[permissions :as perms :refer [Permissions]]
[permissions-group :as perm-group]
......@@ -329,8 +329,9 @@
;; There was a bug (#5998) preventing database_id from being persisted with
;; native query type cards. This migration populates all of the Cards
;; missing those database ids
(defmigration ^{:author "senior", :added "0.26.1"} populate-card-database-id
(defmigration ^{:author "senior", :added "0.27.0"} populate-card-database-id
(doseq [[db-id cards] (group-by #(get-in % [:dataset_query :database])
(db/select [Card :dataset_query :id] :database_id [:= nil]))]
(db/select [Card :dataset_query :id] :database_id [:= nil]))
:when (not= db-id virtual-id)]
(db/update-where! Card {:id [:in (map :id cards)]}
:database_id db-id)))
......@@ -67,16 +67,25 @@
(def ^:private public (partial entrypoint "public" :embeddable))
(def ^:private embed (partial entrypoint "embed" :embeddable))
(defn- redirect-including-query-string
"Like `resp/redirect`, but passes along query string URL params as well. This is important because the public and
embedding routes below pass query params (such as template tags) as part of the URL."
[url]
(fn [{:keys [query-string]}]
(resp/redirect (str url "?" query-string))))
;; /public routes. /public/question/:uuid.:export-format redirects to /api/public/card/:uuid/query/:export-format
(defroutes ^:private public-routes
(GET ["/question/:uuid.:export-format", :uuid u/uuid-regex, :export-format dataset-api/export-format-regex]
[uuid export-format]
(resp/redirect (format "/api/public/card/%s/query/%s" uuid export-format)))
(redirect-including-query-string (format "/api/public/card/%s/query/%s" uuid export-format)))
(GET "*" [] public))
;; /embed routes. /embed/question/:token.:export-format redirects to /api/public/card/:token/query/:export-format
(defroutes ^:private embed-routes
(GET ["/question/:token.:export-format", :export-format dataset-api/export-format-regex]
[token export-format]
(resp/redirect (format "/api/embed/card/%s/query/%s" token export-format)))
(redirect-including-query-string (format "/api/embed/card/%s/query/%s" token export-format)))
(GET "*" [] embed))
;; Redirect naughty users who try to visit a page other than setup if setup is not yet complete
......
......@@ -5,6 +5,7 @@
[dk.ative.docjure.spreadsheet :as spreadsheet]
[expectations :refer :all]
[metabase
[config :as config]
[http-client :as http]
[util :as u]]
[metabase.api
......@@ -273,6 +274,36 @@
(http/client :get 200 (str (card-query-url card response-format) "?abc=200") request-options))))
;; make sure CSV (etc.) downloads take editable params into account (#6407)
(defn- card-with-date-field-filter []
{:dataset_query {:database (data/id)
:type :native
:native {:query "SELECT COUNT(*) AS \"count\" FROM CHECKINS WHERE {{date}}"
:template_tags {:date {:name "date"
:display_name "Date"
:type "dimension"
:dimension [:field-id (data/id :checkins :date)]
:widget_type "date/quarter-year"}}}}
:enable_embedding true
:embedding_params {:date :enabled}})
(expect
"count\n107\n"
(with-embedding-enabled-and-new-secret-key
(tt/with-temp Card [card (card-with-date-field-filter)]
(http/client :get 200 (str (card-query-url card "/csv") "?date=Q1-2014")))))
;; make sure it also works with the forwarded URL
(expect
"count\n107\n"
(with-embedding-enabled-and-new-secret-key
(tt/with-temp Card [card (card-with-date-field-filter)]
;; make sure the URL doesn't include /api/ at the beginning like it normally would
(binding [http/*url-prefix* (str "http://localhost:" (config/config-str :mb-jetty-port) "/")]
(http/client :get 200 (str "embed/question/" (card-token card) ".csv?date=Q1-2014"))))))
;;; ---------------------------------------- GET /api/embed/dashboard/:token -----------------------------------------
(defn- dashboard-url [dashboard & [additional-token-params]]
......
......@@ -4,6 +4,7 @@
[dk.ative.docjure.spreadsheet :as spreadsheet]
[expectations :refer :all]
[metabase
[config :as config]
[http-client :as http]
[query-processor-test :as qp-test]
[util :as u]]
......@@ -178,6 +179,39 @@
;; make sure CSV (etc.) downloads take editable params into account (#6407)
(defn- card-with-date-field-filter []
(assoc (shared-obj)
:dataset_query {:database (data/id)
:type :native
:native {:query "SELECT COUNT(*) AS \"count\" FROM CHECKINS WHERE {{date}}"
:template_tags {:date {:name "date"
:display_name "Date"
:type "dimension"
:dimension [:field-id (data/id :checkins :date)]
:widget_type "date/quarter-year"}}}}))
(expect
"count\n107\n"
(tu/with-temporary-setting-values [enable-public-sharing true]
(tt/with-temp Card [{uuid :public_uuid} (card-with-date-field-filter)]
(http/client :get 200 (str "public/card/" uuid "/query/csv")
:parameters (json/encode [{:type :date/quarter-year
:target [:dimension [:template-tag :date]]
:value "Q1-2014"}])))))
;; make sure it also works with the forwarded URL
(expect
"count\n107\n"
(tu/with-temporary-setting-values [enable-public-sharing true]
(tt/with-temp Card [{uuid :public_uuid} (card-with-date-field-filter)]
;; make sure the URL doesn't include /api/ at the beginning like it normally would
(binding [http/*url-prefix* (str "http://localhost:" (config/config-str :mb-jetty-port) "/")]
(http/client :get 200 (str "public/question/" uuid ".csv")
:parameters (json/encode [{:type :date/quarter-year
:target [:dimension [:template-tag :date]]
:value "Q1-2014"}]))))))
;;; ---------------------------------------- GET /api/public/dashboard/:uuid -----------------------------------------
;; Check that we *cannot* fetch PublicDashboard if setting is disabled
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment