Skip to content
Snippets Groups Projects
Commit 9a077f56 authored by Cam Saül's avatar Cam Saül
Browse files

Use the JSON encoders for XLSX objects with no encoders

parent ea688299
No related branches found
No related tags found
No related merge requests found
......@@ -19,7 +19,8 @@
[metabase.query-processor.util :as qputil]
[metabase.util.schema :as su]
[schema.core :as s])
(:import [java.io ByteArrayInputStream ByteArrayOutputStream]))
(:import [java.io ByteArrayInputStream ByteArrayOutputStream]
org.apache.poi.ss.usermodel.Cell))
;;; ------------------------------------------------------------ Constants ------------------------------------------------------------
......@@ -65,18 +66,30 @@
;;; ------------------------------------------------------------ Downloading Query Results in Other Formats ------------------------------------------------------------
(defn- export-to-csv [columns rows]
(with-out-str
;; turn keywords into strings, otherwise we get colons in our output
(csv/write-csv *out* (into [(mapv name columns)] rows))))
;; add a generic implementation for the method that writes values to XLSX cells that just piggybacks off the implementations
;; we've already defined for encoding things as JSON. These implementations live in `metabase.middleware`.
(defmethod spreadsheet/set-cell! Object [^Cell cell, value]
(when (= (.getCellType cell) Cell/CELL_TYPE_FORMULA)
(.setCellType cell Cell/CELL_TYPE_STRING))
;; stick the object in a JSON map and encode it, which will force conversion to a string. Then unparse that JSON and use the resulting value
;; as the cell's new String value.
;; There might be some more efficient way of doing this but I'm not sure what it is.
(.setCellValue cell (str (-> (json/generate-string {:v value})
(json/parse-string keyword)
:v))))
(defn- export-to-xlsx [columns rows]
(let [wb (spreadsheet/create-workbook "Query result" (conj rows (mapv name columns)))
(let [wb (spreadsheet/create-workbook "Query result" (cons (mapv name columns) rows))
;; note: byte array streams don't need to be closed
out (ByteArrayOutputStream.)]
(spreadsheet/save-workbook! out wb)
(ByteArrayInputStream. (.toByteArray out))))
(defn- export-to-csv [columns rows]
(with-out-str
;; turn keywords into strings, otherwise we get colons in our output
(csv/write-csv *out* (into [(mapv name columns)] rows))))
(defn- export-to-json [columns rows]
(for [row rows]
(zipmap columns row)))
......
(ns metabase.api.dataset-test
"Unit tests for /api/dataset endpoints."
(:require [expectations :refer :all]
(:require [cheshire.generate :as generate]
[dk.ative.docjure.spreadsheet :as spreadsheet]
[expectations :refer :all]
[medley.core :as m]
[metabase.api.dataset :refer [default-query-constraints]]
[metabase.models.query-execution :refer [QueryExecution]]
......@@ -128,3 +130,26 @@
:native {:query "foobar"}})]
[(check-error-message (format-response result))
(check-error-message (format-response (most-recent-query-execution)))]))
;;; Make sure that we're piggybacking off of the JSON encoding logic when encoding strange values in XLSX (#5145, #5220, #5459)
(defrecord ^:private SampleNastyClass [^String v])
(generate/add-encoder
SampleNastyClass
(fn [obj, ^com.fasterxml.jackson.core.JsonGenerator json-generator]
(.writeString json-generator (:v obj))))
(defrecord ^:private AnotherNastyClass [^String v])
(expect
[{"Values" "values"}
{"Values" "Hello XLSX World!"} ; should use the JSON encoding implementation for object
{"Values" "{:v \"No Encoder\"}"} ; fall back to the implementation of `str` for an object if no JSON encoder exists rather than barfing
{"Values" "ABC"}]
(->> (spreadsheet/create-workbook "Results" [["values"]
[(SampleNastyClass. "Hello XLSX World!")]
[(AnotherNastyClass. "No Encoder")]
["ABC"]])
(spreadsheet/select-sheet "Results")
(spreadsheet/select-columns {:A "Values"})))
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