(ns performance.server "The routes for the web server." (:require [cheshire.core :as json] [clj-time.core :refer [now]] [clojure.tools.logging :refer [infof warn warnf error errorf]] [compojure [core :refer [defroutes GET POST]] [handler :as handler]] [ring.middleware.jsonp :refer [wrap-json-with-padding]] [ring.middleware.params :refer [wrap-params]] [ring.util.io :refer [piped-input-stream]]) (:import [java.io BufferedWriter OutputStreamWriter])) ;; extend cheshire to allow for the encoding of Joda DateTime values... (extend-protocol cheshire.generate/JSONable org.joda.time.DateTime (to-json [t jg] (cheshire.generate/write-string jg (str t)))) (defn safe-parse-json "Function to attempt to parse a JSON string from the user - and we have to be VERY careful about this, as we have NO trust in this user - so check for all possible problems, and if it's invalid JSON, return a nil." [s] (cond (or (empty? s) (not (string? s))) nil :else (try (json/parse-string s true) (catch com.fasterxml.jackson.core.JsonParseException jpe (warnf "JSON parse exception on: %s => %s" s (.getMessage jpe)) nil)))) (defn return-code "Creates a ring response for returning the given return code." [code] {:status code :headers {"Content-Type" "application/json; charset=UTF-8"}}) (defn return-json "Creates a ring response for returning the given object as JSON." ([ob] (return-json ob (now) 200)) ([ob lastm] (return-json ob lastm 200)) ([ob lastm code] {:status code :headers {"Content-Type" "application/json; charset=UTF-8" "Last-Modified" (str (or lastm (now)))} ;; TODO: the input-stream tactic is foiled by ring.middleware.jsonp ;; which slurps the whole stream before adding the callback. Could ;; fix by patching that lib. We should still be getting the benefit ;; for the non-browser case though. :body (piped-input-stream (fn [out] (->> out (OutputStreamWriter.) (BufferedWriter.) (json/generate-stream ob))))})) (defroutes app-routes "Primary routes for the webserver." (GET "/" [] (return-json {:app "performance", :hello? "World!", :code (or (git-commit) "unknown commit")})) (POST "/v1/deals_performance.json" [:as {body :body}] (let [deals (json/parse-json (slurp body))] (cond (empty? deals) (return-code 400) (not (coll? deals)) (return-code 400) :else (return-json deals)))))