Skip to content
Snippets Groups Projects
Commit d163e5fe authored by Allen Gilliland's avatar Allen Gilliland
Browse files

Merge pull request #60 from metabase/ag-csv-support

Implement our few CSV download endpoints
parents 357e20b9 eded5b65
No related branches found
No related tags found
No related merge requests found
......@@ -11,6 +11,7 @@
[org.clojure/core.match "0.3.0-alpha4"] ; optimized pattern matching library for Clojure
[org.clojure/math.numeric-tower "0.0.4"] ; math functions like `ceil`
[org.clojure/core.memoize "0.5.6"] ; needed by core.match; has useful FIFO, LRU, etc. caching mechanisms
[org.clojure/data.csv "0.1.2"] ; CSV parsing / generation
[org.clojure/data.json "0.2.5"] ; JSON parsing / generation
[org.clojure/java.jdbc "0.3.6"] ; basic jdbc access from clojure
[org.clojure/tools.logging "0.3.1"] ; logging framework
......
(ns metabase.api.qs
(:require [compojure.core :refer [defroutes GET POST]]
(:require [clojure.data.csv :as csv]
[compojure.core :refer [defroutes GET POST]]
[metabase.api.common :refer :all]
[metabase.db :refer :all]
[metabase.util :refer [contains-many?]]
[metabase.util :refer [contains-many? now-iso8601]]
(metabase.models
[hydrate :refer :all]
[query-execution :refer [all-fields]])))
......@@ -34,12 +35,15 @@
(assoc {:status 200} :body)))))
(defendpoint GET "/:query-uuid/csv" [query-uuid]
;; TODO - implementation (execute a query)
{:TODO "TODO"})
(def query-result-csv
(GET "/:uuid/csv" [uuid]
(let-404 [{:keys [result_data] :as query-execution} (eval `(sel :one ~all-fields :uuid ~uuid))]
{:status 200
:body (with-out-str (csv/write-csv *out* (into [(:columns result_data)] (:rows result_data))))
:headers {"Content-Type" "text/csv", "Content-Disposition" (str "attachment; filename=\"query_result_" (now-iso8601) ".csv\"")}})))
(define-routes query-result)
(define-routes query-result query-result-csv)
;; ===============================================================================================================
......
(ns metabase.api.query
(:require [korma.core :refer [where subselect fields order limit]]
(:require [clojure.data.csv :as csv]
[korma.core :refer [where subselect fields order limit]]
[compojure.core :refer [defroutes GET PUT POST DELETE]]
[clojure.data.json :as json]
[medley.core :refer :all]
......@@ -9,7 +10,7 @@
[hydrate :refer :all]
[database :refer [Database databases-for-org]] [org :refer [Org]]
[query :refer [Query]]
[query-execution :refer [QueryExecution]])
[query-execution :refer [QueryExecution all-fields]])
[metabase.util :as util]))
......@@ -97,9 +98,14 @@
(sel :many QueryExecution :query_id id (order :finished_at :DESC) (limit 10))))
(defendpoint POST "/:id/csv" [id]
;; TODO - this should return a CSV file instead of JSON
{:TODO "TODO"})
(def query-csv
(GET "/:id/csv" [id]
(let-404 [{:keys [result_data query_id] :as query-execution} (eval `(sel :one ~all-fields :query_id ~id (order :started_at :DESC) (limit 1)))]
(let-404 [{{can_read :can_read name :name} :query} (hydrate query-execution :query)]
(check-403 @can_read)
{:status 200
:body (with-out-str (csv/write-csv *out* (into [(:columns result_data)] (:rows result_data))))
:headers {"Content-Type" "text/csv", "Content-Disposition" (str "attachment; filename=\"" name ".csv\"")}}))))
(define-routes)
(define-routes query-csv)
(ns metabase.api.result
"/api/result/* endpoints representing saved Query executions."
(:require [korma.core :refer [where subselect fields]]
(:require [clojure.data.csv :as csv]
[korma.core :refer [where subselect fields]]
[compojure.core :refer [defroutes GET PUT POST DELETE]]
[medley.core :refer [mapply]]
(metabase.api [common :refer :all]
......@@ -33,10 +34,16 @@
;; Returns the data response for a given query result as a CSV file
(defendpoint GET "/:id/csv" [id]
;; TODO - this is actually a CSV instead of a JSON response!!
{:status 999 ;; official http status code for "i'll do it later"
:body "TODO"})
(define-routes)
(def query-result-csv
(GET "/:id/csv" [id]
(let-404 [{:keys [result_data query_id] :as query-execution} (eval `(sel :one ~all-fields :id ~id))]
;; NOTE - this endpoint requires there to be a saved query associated with this execution
(check-404 query_id)
(let-404 [{{can_read :can_read name :name} :query} (hydrate query-execution :query)]
(check-403 @can_read)
{:status 200
:body (with-out-str (csv/write-csv *out* (into [(:columns result_data)] (:rows result_data))))
:headers {"Content-Type" "text/csv", "Content-Disposition" (str "attachment; filename=\"" name ".csv\"")}}))))
(define-routes query-result-csv)
......@@ -96,6 +96,13 @@
(coerce/to-long)
(java.sql.Date.))))
(defn now-iso8601
"format the current time as iso8601 date/time string."
[]
(time/unparse (time/formatters :date-time-no-ms) (coerce/from-long (System/currentTimeMillis))))
(defn jdbc-clob-to-str
"Convert a `JdbcClob` to a `String` so it can be serialized to JSON."
[^org.h2.jdbc.JdbcClob clob]
......
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