diff --git a/backend/src/app/http/middleware.clj b/backend/src/app/http/middleware.clj index 5f687be12..23e229652 100644 --- a/backend/src/app/http/middleware.clj +++ b/backend/src/app/http/middleware.clj @@ -32,6 +32,12 @@ {:name ::params :compile (constantly ymw/wrap-params)}) +(def ^:private json-mapper + (json/mapper + {:encode-key-fn str/camel + :decode-key-fn (comp keyword str/kebab) + :pretty true})) + (defn wrap-parse-request [handler] (letfn [(process-request [request] @@ -46,7 +52,7 @@ (str/starts-with? header "application/json") (with-open [is (yrq/body request)] - (let [params (json/decode is)] + (let [params (json/decode is json-mapper)] (-> request (assoc :body-params params) (update :params merge params)))) @@ -117,7 +123,32 @@ (finally (.close ^OutputStream output-stream)))))) - (format-response [response request] + (json-streamable-body [data] + (reify yrs/StreamableResponseBody + (-write-body-to-stream [_ _ output-stream] + (try + + (with-open [bos (buffered-output-stream output-stream buffer-size)] + (json/write! bos data json-mapper)) + + (catch java.io.IOException _cause + ;; Do nothing, EOF means client closes connection abruptly + nil) + (catch Throwable cause + (l/warn :hint "unexpected error on encoding response" + :cause cause)) + (finally + (.close ^OutputStream output-stream)))))) + + (format-response-with-json [response _] + (let [body (yrs/body response)] + (if (or (boolean? body) (coll? body)) + (-> response + (update :headers assoc "content-type" "application/json") + (assoc :body (json-streamable-body body))) + response))) + + (format-response-with-transit [response request] (let [body (yrs/body response)] (if (or (boolean? body) (coll? body)) (let [qs (yrq/query request) @@ -130,6 +161,20 @@ (assoc :body (transit-streamable-body body opts)))) response))) + (format-response [response request] + (let [accept (yrq/get-header request "accept")] + (cond + (or (= accept "application/transit+json") + (str/includes? accept "application/transit+json")) + (format-response-with-transit response request) + + (or (= accept "application/json") + (str/includes? accept "application/json")) + (format-response-with-json response request) + + :else + (format-response-with-transit response request)))) + (process-response [response request] (cond-> response (map? response) (format-response request)))] diff --git a/frontend/src/app/main/repo.cljs b/frontend/src/app/main/repo.cljs index 67993a419..aea3ed169 100644 --- a/frontend/src/app/main/repo.cljs +++ b/frontend/src/app/main/repo.cljs @@ -60,6 +60,7 @@ http/conditional-decode-transit)] (->> (http/send! {:method :get :uri (u/join @cf/public-uri "api/rpc/query/" (name id)) + :headers {"accept" "application/transit+json"} :credentials "include" :query params}) (rx/map decode-transit) @@ -71,6 +72,7 @@ [id params] (->> (http/send! {:method :post :uri (u/join @cf/public-uri "api/rpc/mutation/" (name id)) + :headers {"accept" "application/transit+json"} :credentials "include" :body (http/transit-data params)}) (rx/map http/conditional-decode-transit) @@ -88,6 +90,7 @@ (->> (http/send! {:method method :uri (u/join @cf/public-uri "api/rpc/command/" (name id)) :credentials "include" + :headers {"accept" "application/transit+json"} :body (when (= method :post) (if form-data? (http/form-data params)