(ns poky.kv.jdbc.util
  (:require [clojure.java.jdbc :as sql]
            [clojure.string :as string])
  (:import com.mchange.v2.c3p0.ComboPooledDataSource))

(def ^:private default-min-pool-size 3)
(def ^:private default-max-pool-size 3)
(def ^:private default-driver "org.postgresql.Driver")

(defn create-db-spec
  "Given a dsn and optionally a driver create a db spec that can be used with pool to
  create a connection pool."
  ([dsn driver]
   (let [uri (java.net.URI. dsn)
         host (.getHost uri)
         scheme (.getScheme uri)
         [user pass] (clojure.string/split (.getUserInfo uri) #":")
         port (.getPort uri)
         port (if (= port -1) 5432 port)
         path (.substring (.getPath uri) 1)]
     {:classname driver
      :subprotocol scheme
      :subname (str "//" host ":" port "/" path)
      :user user
      :password pass}))
  ([dsn]
   (create-db-spec dsn default-driver)))

(defn pool
  "Create a connection pool."
  [spec &{:keys [min-pool-size] :or {min-pool-size default-min-pool-size}}]
  (let [cpds (doto (ComboPooledDataSource.)
               (.setDriverClass (:classname spec))
               (.setJdbcUrl (str "jdbc:" (:subprotocol spec) ":" (:subname spec)))
               (.setUser (:user spec))
               (.setPassword (:password spec))
               (.setMinPoolSize min-pool-size)
               (.setMaxPoolSize default-max-pool-size)
               (.setMaxIdleTimeExcessConnections (* 30 60))
               (.setMaxIdleTime (* 3 60 60)))]
      {:datasource cpds}))

(defn create-connection
  "Create a connection and delay it."
  [dsn]
  (delay (pool (create-db-spec dsn))))

(defn close-connection
  "Close the connection of a JdbcKeyValue object."
  [connection-object]
  (.close (:datasource connection-object)))

(defn purge-bucket
  "Should only be used in testing."
  [conn b]
  (sql/with-connection conn
    (sql/delete-rows "poky"
       ["bucket=?" b])))

(defn jdbc-get
  "Get the tuple at bucket b and key k. Returns a map with the attributes of the table."
  [conn b k]
  (sql/with-connection conn
    (sql/with-query-results
      results
      ["SELECT * FROM poky WHERE bucket=? AND key=?" b k]
      (first results))))

(defn jdbc-mget
  "Deprecated."
  [conn b ks]
  (sql/with-connection conn
    (sql/with-query-results results
      (vec (concat [(format "SELECT * FROM poky WHERE bucket=? AND key IN (%s)"
                            (string/join "," (repeat (count ks) "?")))
                    b]
                   ks))
      (doall results))))

(defn jdbc-set
  "Set a bucket b and key k to value v. Returns true on success and false on failure."
  [conn b k v]
  (sql/with-connection conn
    (sql/update-or-insert-values "poky"
       ["bucket=? AND key=?" b k]
       {:bucket b :key k :data v})))

(defn jdbc-delete
  "Delete the value at bucket b and key k. Returns true on success and false if the
  tuple does not exist."
  [conn b k]
  (sql/with-connection conn
    (sql/delete-rows "poky"
       ["bucket=? AND key=?" b k])))

(defn compare-seq-first
  "Compare the first value in s to v using =. Complements set and delete.
  The clojure.java.jdbc methods they use return a tuple where the first element is the
  number of records updated. This helper can be used to test that element for the number
  expected."
  [s v]
  (when (seq? s)
    (= (first s) v)))

(ns poky.kv.jdbc.util
  (:require [clojure.java.jdbc :as sql]
            [clojure.string :as string]
            [environ.core :refer [env]])
  (:import com.mchange.v2.c3p0.ComboPooledDataSource))

(def ^:private default-min-pool-size 3)
(def ^:private default-max-pool-size 3)
(def ^:private default-driver "org.postgresql.Driver")

(defn create-db-spec
  "Given a dsn and optionally a driver create a db spec that can be used with pool to
  create a connection pool."
  ([dsn driver]
   (let [uri (java.net.URI. dsn)
         host (.getHost uri)
         scheme (.getScheme uri)
         [user pass] (clojure.string/split (.getUserInfo uri) #":")
         port (.getPort uri)
         port (if (= port -1) 5432 port)
         path (.substring (.getPath uri) 1)]
     {:classname driver
      :subprotocol scheme
      :subname (str "//" host ":" port "/" path)
      :user user
      :password pass}))
  ([dsn]
   (create-db-spec dsn default-driver)))

(defn pool
  "Create a connection pool."
  [spec &{:keys [min-pool-size max-pool-size]
          :or {min-pool-size default-min-pool-size max-pool-size default-max-pool-size}}]
  (let [cpds (doto (ComboPooledDataSource.)
               (.setDriverClass (:classname spec))
               (.setJdbcUrl (str "jdbc:" (:subprotocol spec) ":" (:subname spec)))
               (.setUser (:user spec))
               (.setPassword (:password spec))
               (.setMinPoolSize min-pool-size)
               (.setMaxPoolSize max-pool-size)
               (.setMaxIdleTimeExcessConnections (* 30 60))
               (.setMaxIdleTime (* 3 60 60)))]
      {:datasource cpds}))

(defn create-connection
  "Create a connection and delay it."
  [dsn]
  (delay (pool (create-db-spec dsn) :max-pool-size (env :max-pool-size default-max-pool-size))))

(defn close-connection
  "Close the connection of a JdbcKeyValue object."
  [connection-object]
  (.close (:datasource connection-object)))

(defn purge-bucket
  "Should only be used in testing."
  [conn b]
  (sql/with-connection conn
    (sql/delete-rows "poky"
       ["bucket=?" b])))

(defn jdbc-get
  "Get the tuple at bucket b and key k. Returns a map with the attributes of the table."
  [conn b k]
  (sql/with-connection conn
    (sql/with-query-results
      results
      ["SELECT * FROM poky WHERE bucket=? AND key=?" b k]
      (first results))))

(defn jdbc-mget
  "Deprecated."
  [conn b ks]
  (sql/with-connection conn
    (sql/with-query-results results
      (vec (concat [(format "SELECT * FROM poky WHERE bucket=? AND key IN (%s)"
                            (string/join "," (repeat (count ks) "?")))
                    b]
                   ks))
      (doall results))))

(defn jdbc-set
  "Set a bucket b and key k to value v. Returns true on success and false on failure."
  [conn b k v]
  (sql/with-connection conn
    (sql/update-or-insert-values "poky"
       ["bucket=? AND key=?" b k]
       {:bucket b :key k :data v})))

(defn jdbc-delete
  "Delete the value at bucket b and key k. Returns true on success and false if the
  tuple does not exist."
  [conn b k]
  (sql/with-connection conn
    (sql/delete-rows "poky"
       ["bucket=? AND key=?" b k])))

(defn compare-seq-first
  "Compare the first value in s to v using =. Complements set and delete.
  The clojure.java.jdbc methods they use return a tuple where the first element is the
  number of records updated. This helper can be used to test that element for the number
  expected."
  [s v]
  (when (seq? s)
    (= (first s) v)))

Fix and use max-pool-size.

Fix and use max-pool-size.


(ns reabledit.cells.dropdown
  (:require [reabledit.util :as util]
            [clojure.string :as str]
            [reagent.core :as reagent]))

(defn set-state!
  [state v]
  (reset! state {:edit? true
                 :selected v}))

(defn handle-key-down
  [e state options k commit!]
  (let [keycode (.-keyCode e)
        {:keys [edit? selected]} @state
        position (util/find-index (map :key options) selected)
        set-selected! (fn [k]
                        (.preventDefault e)
                        (.stopPropagation e)
                        (set-state! state k))]
    (cond

      ;; Enter and F2 start edit mode with old value
      (and (not edit?) (or (= keycode 13) (= keycode 113)))
      (set-selected! k)

      ;; Arrow keys navigate the dropdown up and down
      (and edit? (= keycode 38))
      (if (zero? position)
        (set-selected! (-> options last :key))
        (set-selected! (:key (nth options (dec position)))))

      (and edit? (= keycode 40))
      (if (= position (-> options count dec))
        (set-selected! (-> options first :key))
        (set-selected! (:key (nth options (inc position)))))

      ;; Enter commits the changes when dropdown is edit
      (and edit? (= keycode 13))
      (commit!)

      ;; Navigation with arrow keys is blocked when dropdown is edit
      (and edit? (contains? #{37 39} keycode))
      (.stopPropagation e)

      :else nil)))

(defn handle-on-change
  [e state options k]
  (let [input (str/lower-case (-> e .-target .-value))
        option (first (filter #(str/starts-with? (-> % :value str/lower-case)
                                                 input)
                              options))]
    (set-state! state (or (:key option) k))))

(defn handle-paste
  [e state options commit!]
  (let [input (str/lower-case (util/get-clipboard-data e))
        option (first (filter #(= (-> % :value str/lower-case)
                                  input)
                              options))]
    (when option
      (set-state! state (:key option))
      (commit!))))

(defn dropdown-cell
  [{:keys [row-data column-key commit! opts]}]
  (let [state (reagent/atom nil)]
    (fn [{:keys [row-data column-key commit! opts]}]
      (let [options (:options opts)
            k (get row-data column-key)
            v (-> (filter #(= (:key %) k) options) first :value)
            commit! (fn []
                      (if (and (:edit? @state) (not= (:selected @state) k))
                        (commit! (assoc row-data
                                        column-key
                                        (:selected @state))))
                      (reset! state nil))
            toggle-options! #(if (:edit? @state)
                               (reset! state nil)
                               (set-state! state k))]
        [:div.reabledit-dropdown-cell
         {:on-double-click toggle-options!
          :title v}
         [:input.reabledit-dropdown-cell__input.reabledit-focused
          {:type "text"
           :value ""
           :on-key-down #(handle-key-down % state options k commit!)
           :on-change #(handle-on-change % state options k)
           :on-blur #(if-not (= k (:selected @state)) (commit!))
           :on-copy #(util/set-clipboard-data % v)
           :on-paste #(handle-paste % state options commit!)
           :on-cut #(util/set-clipboard-data % v)}]
         [:div.reabledit-dropdown-cell-view
          [:span.reabledit-dropdown-cell-view__text v]
          [:span.reabledit-dropdown-cell-view__caret
           {:on-click toggle-options!}
           "▼"]]
         (if (:edit? @state)
           (let [selected-key (:selected @state)]
             [:div.reabledit-dropdown-cell-options
              (for [{:keys [key value]} options]
                ^{:key key}
                [:div.reabledit-dropdown-cell-options__item
                 {:class (if (= selected-key key)
                           "reabledit-dropdown-cell-options__item--selected")
                  :on-click (fn [e]
                              (set-state! state key)
                              (commit!))}
                 value])]))]))))

(ns reabledit.cells.dropdown
  (:require [reabledit.util :as util]
            [clojure.string :as str]
            [reagent.core :as reagent]))

(defn set-state!
  [state v]
  (reset! state {:edit? true
                 :selected v}))

(defn handle-key-down
  [e state options k commit!]
  (let [keycode (.-keyCode e)
        {:keys [edit? selected]} @state
        position (util/find-index (map :key options) selected)
        set-selected! (fn [k]
                        (.preventDefault e)
                        (.stopPropagation e)
                        (set-state! state k))]
    (cond

      ;; Enter and F2 start edit mode with old value
      (and (not edit?) (or (= keycode 13) (= keycode 113)))
      (set-selected! k)

      ;; Arrow keys navigate the dropdown up and down
      (and edit? (= keycode 38))
      (if (zero? position)
        (set-selected! (-> options last :key))
        (set-selected! (:key (nth options (dec position)))))

      (and edit? (= keycode 40))
      (if (= position (-> options count dec))
        (set-selected! (-> options first :key))
        (set-selected! (:key (nth options (inc position)))))

      ;; Enter commits the changes when dropdown is edit
      (and edit? (= keycode 13))
      (commit!)

      ;; Navigation with arrow keys is blocked when dropdown is edit
      (and edit? (contains? #{37 39} keycode))
      (.stopPropagation e)

      :else nil)))

(defn handle-on-change
  [e state options k]
  (let [input (str/lower-case (-> e .-target .-value))
        option (first (filter #(str/starts-with? (-> % :value str/lower-case)
                                                 input)
                              options))]
    (set-state! state (or (:key option) k))))

(defn handle-paste
  [e state options commit!]
  (let [input (str/lower-case (util/get-clipboard-data e))
        option (first (filter #(= (-> % :value str/lower-case)
                                  input)
                              options))]
    (when option
      (set-state! state (:key option))
      (commit!))))

(defn dropdown-cell
  [{:keys [row-data column-key commit! opts]}]
  (let [state (reagent/atom nil)]
    (fn [{:keys [row-data column-key commit! opts]}]
      (let [options (:options opts)
            k (get row-data column-key)
            v (-> (filter #(= (:key %) k) options) first :value)
            commit! (fn []
                      (if (and (:edit? @state) (not= (:selected @state) k))
                        (commit! (assoc row-data
                                        column-key
                                        (:selected @state))))
                      (reset! state nil))
            toggle-options! #(if (:edit? @state)
                               (reset! state nil)
                               (set-state! state k))]
        [:div.reabledit-dropdown-cell
         {:on-double-click toggle-options!
          :title v}
         [:input.reabledit-dropdown-cell__input.reabledit-focused
          {:type "text"
           :value ""
           :on-key-down #(handle-key-down % state options k commit!)
           :on-change #(handle-on-change % state options k)

           ;; A hack. Should use relatedTarget, but Firefox
           ;; does not support it yet. Fix in the future.
           :on-blur #(if (:edit? @state) (js/setTimeout commit! 100))
           :on-copy #(util/set-clipboard-data % v)
           :on-paste #(handle-paste % state options commit!)
           :on-cut #(util/set-clipboard-data % v)}]
         [:div.reabledit-dropdown-cell-view
          [:span.reabledit-dropdown-cell-view__text v]
          [:span.reabledit-dropdown-cell-view__caret
           {:on-click toggle-options!}
           "▼"]]
         (if (:edit? @state)
           (let [selected-key (:selected @state)]
             [:div.reabledit-dropdown-cell-options
              (for [{:keys [key value]} options]
                ^{:key key}
                [:div.reabledit-dropdown-cell-options__item
                 {:class (if (= selected-key key)
                           "reabledit-dropdown-cell-options__item--selected")
                  :on-click (fn [e]
                              (set-state! state key)
                              (commit!))}
                 value])]))]))))

Fix bug where dropdown stays open on blur


(ns reabledit.cells.dropdown
  (:require [reabledit.util :as util]
            [clojure.string :as str]
            [reagent.core :as reagent]))

(defn set-state!
  [state v]
  (reset! state {:edit? true
                 :selected v}))

(defn handle-key-down
  [e state options k commit!]
  (let [keycode (.-keyCode e)
        {:keys [edit? selected]} @state
        position (util/find-index (map :key options) selected)
        set-selected! (fn [k]
                        (.preventDefault e)
                        (.stopPropagation e)
                        (set-state! state k))]
    (cond

      ;; Enter and F2 start edit mode with old value
      (and (not edit?) (or (= keycode 13) (= keycode 113)))
      (set-selected! k)

      ;; Arrow keys navigate the dropdown up and down
      (and edit? (= keycode 38))
      (if (zero? position)
        (set-selected! (-> options last :key))
        (set-selected! (:key (nth options (dec position)))))

      (and edit? (= keycode 40))
      (if (= position (-> options count dec))
        (set-selected! (-> options first :key))
        (set-selected! (:key (nth options (inc position)))))

      ;; Enter commits the changes when dropdown is edit
      (and edit? (= keycode 13))
      (commit!)

      ;; Navigation with arrow keys is blocked when dropdown is edit
      (and edit? (contains? #{37 39} keycode))
      (.stopPropagation e)

      :else nil)))

(defn handle-on-change
  [e state options k]
  (let [input (str/lower-case (-> e .-target .-value))
        option (first (filter #(str/starts-with? (-> % :value str/lower-case)
                                                 input)
                              options))]
    (set-state! state (or (:key option) k))))

(defn handle-paste
  [e state options commit!]
  (let [input (str/lower-case (util/get-clipboard-data e))
        option (first (filter #(= (-> % :value str/lower-case)
                                  input)
                              options))]
    (when option
      (set-state! state (:key option))
      (commit!))))

(defn dropdown-cell
  [{:keys [row-data column-key commit! opts]}]
  (let [state (reagent/atom nil)]
    (fn [{:keys [row-data column-key commit! opts]}]
      (let [options (:options opts)
            k (get row-data column-key)
            v (-> (filter #(= (:key %) k) options) first :value)
            commit! (fn []
                      (if (and (:edit? @state) (not= (:selected @state) k))
                        (commit! (assoc row-data
                                        column-key
                                        (:selected @state))))
                      (reset! state nil))
            toggle-options! #(if (:edit? @state)
                               (reset! state nil)
                               (set-state! state k))]
        [:div.reabledit-dropdown-cell
         {:on-double-click toggle-options!
          :title v}
         [:input.reabledit-dropdown-cell__input.reabledit-focused
          {:type "text"
           :value ""
           :on-key-down #(handle-key-down % state options k commit!)
           :on-change #(handle-on-change % state options k)

           ;; A hack. Should use relatedTarget, but Firefox
           ;; does not support it yet. Fix in the future.
           :on-blur #(if (:edit? @state) (js/setTimeout commit! 100))
           :on-copy #(util/set-clipboard-data % v)
           :on-paste #(handle-paste % state options commit!)
           :on-cut #(util/set-clipboard-data % v)}]
         [:div.reabledit-dropdown-cell-view
          [:span.reabledit-dropdown-cell-view__text v]
          [:span.reabledit-dropdown-cell-view__caret
           {:on-click toggle-options!}
           "▼"]]
         (if (:edit? @state)
           (let [selected-key (:selected @state)]
             [:div.reabledit-dropdown-cell-options
              (for [{:keys [key value]} options]
                ^{:key key}
                [:div.reabledit-dropdown-cell-options__item
                 {:class (if (= selected-key key)
                           "reabledit-dropdown-cell-options__item--selected")
                  :on-click (fn [e]
                              (set-state! state key)
                              (commit!))}
                 value])]))]))))

(ns reabledit.cells.dropdown
  (:require [reabledit.util :as util]
            [clojure.string :as str]
            [reagent.core :as reagent]))

(defn set-state!
  [state v]
  (reset! state {:edit? true
                 :selected v}))

(defn handle-key-down
  [e state options k commit!]
  (let [keycode (.-keyCode e)
        {:keys [edit? selected]} @state
        position (util/find-index (map :key options) selected)
        set-selected! (fn [k]
                        (.preventDefault e)
                        (.stopPropagation e)
                        (set-state! state k))]
    (cond

      ;; Enter and F2 start edit mode with old value
      (and (not edit?) (or (= keycode 13) (= keycode 113)))
      (set-selected! k)

      ;; Arrow keys navigate the dropdown up and down
      (and edit? (= keycode 38))
      (if (zero? position)
        (set-selected! (-> options last :key))
        (set-selected! (:key (nth options (dec position)))))

      (and edit? (= keycode 40))
      (if (= position (-> options count dec))
        (set-selected! (-> options first :key))
        (set-selected! (:key (nth options (inc position)))))

      ;; Enter commits the changes when dropdown is edit
      (and edit? (= keycode 13))
      (commit!)

      ;; Navigation with arrow keys is blocked when dropdown is edit
      (and edit? (contains? #{37 39} keycode))
      (.stopPropagation e)

      :else nil)))

(defn handle-on-change
  [e state options k]
  (let [input (str/lower-case (-> e .-target .-value))
        option (first (filter #(str/starts-with? (-> % :value str/lower-case)
                                                 input)
                              options))]
    (set-state! state (or (:key option) k))))

(defn handle-paste
  [e state options commit!]
  (let [input (str/lower-case (util/get-clipboard-data e))
        option (first (filter #(= (-> % :value str/lower-case)
                                  input)
                              options))]
    (when option
      (set-state! state (:key option))
      (commit!))))

(defn dropdown-cell
  [{:keys [row-data column-key commit! opts]}]
  (let [state (reagent/atom nil)]
    (fn [{:keys [row-data column-key commit! opts]}]
      (let [options (:options opts)
            k (get row-data column-key)
            v (-> (filter #(= (:key %) k) options) first :value)
            commit! (fn []
                      (if (and (:edit? @state) (not= (:selected @state) k))
                        (commit! (assoc row-data
                                        column-key
                                        (:selected @state))))
                      (reset! state nil))
            toggle-options! #(if (:edit? @state)
                               (reset! state nil)
                               (set-state! state k))]
        [:div.reabledit-dropdown-cell
         {:on-double-click toggle-options!
          :title v}
         [:input.reabledit-dropdown-cell__input.reabledit-focused
          {:type "text"
           :value ""
           :on-key-down #(handle-key-down % state options k commit!)
           :on-change #(handle-on-change % state options k)

           ;; A hack. Should use relatedTarget, but Firefox
           ;; does not support it yet. Fix in the future.
           :on-blur #(if (:edit? @state) (js/setTimeout commit! 500))
           :on-copy #(util/set-clipboard-data % v)
           :on-paste #(handle-paste % state options commit!)
           :on-cut #(util/set-clipboard-data % v)}]
         [:div.reabledit-dropdown-cell-view
          [:span.reabledit-dropdown-cell-view__text v]
          [:span.reabledit-dropdown-cell-view__caret
           {:on-click toggle-options!}
           "▼"]]
         (if (:edit? @state)
           (let [selected-key (:selected @state)]
             [:div.reabledit-dropdown-cell-options
              (for [{:keys [key value]} options]
                ^{:key key}
                [:div.reabledit-dropdown-cell-options__item
                 {:class (if (= selected-key key)
                           "reabledit-dropdown-cell-options__item--selected")
                  :on-click (fn [e]
                              (set-state! state key)
                              (commit!))}
                 value])]))]))))

Make the dropdown cell onblur timeout even bigger


 (ns silly-image-store.handler
  (:require [silly-image-store.store :as store]
            [silly-image-store.logging :refer :all]
            [environ.core :refer [env]]
            [compojure.core :refer :all]
            [ring.middleware.json :refer :all]
            [compojure.handler :as handler]
            [compojure.route :as route]))

(def images-dir
  (env :base-store-dir))

; Request/Response utils
(defn- request-url [{scheme :scheme, {host "host"} :headers, uri :uri}]
  (str (name scheme) "://" host uri))

(defn- json-list-response-builder [request]
  (let [base-url (request-url request)
        to-json (fn [n] {:name n :url (str base-url "/" n)})]
    (fn [names] (map to-json names))))

; Routes
(defn- not-found [thing]
  (route/not-found (str "No thing '" thing "' found"))

(defn- list-images-route [request bucket]
  (let [json-list-response (json-list-response-builder request)
        image-names (store/list-images images-dir bucket)]
    (if image-names
      (json-list-response image-names)
      (not-found bucket))))

(defn- serve-image-route [{{image :image} :params}]
  (let [image-file (store/load-image images-dir image)]
    (or image-file (not-found image))))

(defn- serve-random-image-route [request]
  (let [image-file (store/random-image images-dir)]
    (or image-file (not-found "random"))))

(defn- list-buckets-route [request]
  (let [json-list-response (json-list-response-builder request)
        bucket-names (store/list-image-dirs images-dir)]
    (json-list-response bucket-names)))

(defroutes app-routes
  (GET "/images" request (list-images-route request ""))
  (GET "/images/:image" request serve-image-route)
  (GET "/random" request serve-random-image-route)
  (GET "/buckets" request list-buckets-route)
  (GET "/buckets/:bucket/images" [bucket :as request] (list-images-route request bucket))
  (route/resources "/")
  (route/not-found "Not Found"))

(def app
  (-> (handler/site app-routes)
      (wrap-json-response)
      (wrap-request-logging)))

 (ns silly-image-store.handler
  (:require [silly-image-store.store :as store]
            [silly-image-store.logging :refer :all]
            [environ.core :refer [env]]
            [compojure.core :refer :all]
            [ring.middleware.json :refer :all]
            [compojure.handler :as handler]
            [compojure.route :as route]))

(def images-dir
  (env :base-store-dir))

; Request/Response utils
(defn- request-url [{scheme :scheme, {host "host"} :headers, uri :uri}]
  (str (name scheme) "://" host uri))

(defn- json-list-response-builder [request]
  (let [base-url (request-url request)
        to-json (fn [n] {:name n :url (str base-url "/" n)})]
    (fn [names] (map to-json names))))

; Routes
(defn- not-found [thing]
  (route/not-found (str "No thing '" thing "' found")))

(defn- list-images-route [request bucket]
  (let [json-list-response (json-list-response-builder request)
        image-names (store/list-images images-dir bucket)]
    (if image-names
      (json-list-response image-names)
      (not-found bucket))))

(defn- serve-image-route [{{image :image} :params}]
  (let [image-file (store/load-image images-dir image)]
    (or image-file (not-found image))))

(defn- serve-random-image-route [request]
  (let [image-file (store/random-image images-dir)]
    (or image-file (not-found "random"))))

(defn- list-buckets-route [request]
  (let [json-list-response (json-list-response-builder request)
        bucket-names (store/list-image-dirs images-dir)]
    (json-list-response bucket-names)))

(defroutes app-routes
  (GET "/images" request (list-images-route request ""))
  (GET "/images/:image" request serve-image-route)
  (GET "/random" request serve-random-image-route)
  (GET "/buckets" request list-buckets-route)
  (GET "/buckets/:bucket/images" [bucket :as request] (list-images-route request bucket))
  (route/resources "/")
  (route/not-found "Not Found"))

(def app
  (-> (handler/site app-routes)
      (wrap-json-response)
      (wrap-request-logging)))

Fix missing )

Fix missing )


;;
;; Copyright (c) Two Sigma Open Source, LLC
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;;  http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;;
(defproject cook "0.1.0-SNAPSHOT"
  :description "This launches jobs on a Mesos cluster with fair sharing and preemption"
  :license {:name "Apache License, Version 2.0"}
  :dependencies [[org.clojure/clojure "1.6.0"]

                 ;;Data marshalling
                 [org.clojure/data.codec "0.1.0"]
                 [cheshire "5.3.1"]
                 [byte-streams "0.1.4"]
                 [org.clojure/data.json "0.2.2"]
                 [com.taoensso/nippy "2.8.0"
                  :exclusions [org.clojure/tools.reader]]
                 [circleci/clj-yaml "0.5.4"]

                 ;;Utility
                 [amalloy/ring-buffer "1.1"]
                 [lonocloud/synthread "1.0.4"]
                 [org.clojure/tools.namespace "0.2.4"]
                 [org.clojure/core.cache "0.6.3"]
                 [org.clojure/core.memoize "0.5.6"]
                 [clj-time "0.9.0"]
                 [org.clojure/core.async "0.1.346.0-17112a-alpha"]
                 [prismatic/schema "0.2.1"
                  :exclusions [potemkin]]
                 [clojure-miniprofiler "0.4.0"]
                 [jarohen/chime "0.1.6"]
                 [org.clojure/data.priority-map "0.0.5"]
                 [swiss-arrows "1.0.0"]
                 [riddley "0.1.10"]

                 ;;Logging
                 [org.clojure/tools.logging "0.2.6"]
                 [clj-logging-config "1.9.10"
                  :exclusions [log4j]]
                 [org.slf4j/slf4j-log4j12 "1.7.12"]
                 [com.draines/postal "1.11.0"
                  :exclusions [commons-codec]]
                 [prismatic/plumbing "0.1.1"]
                 [instaparse "1.4.0"]
                 [org.codehaus.jsr166-mirror/jsr166y "1.7.0"]
                 [clj-pid "0.1.1"]
                 [jarohen/chime "0.1.6"]

                 ;;Networking
                 [clj-http "2.0.0"]
                 [io.netty/netty "3.10.1.Final"]
                 [cc.qbits/jet "0.5.4"]

                 ;;Metrics
                 [dgrnbrg/metrics-clojure "2.6.1"
                  :exclusions [io.netty/netty
                               org.clojure/clojure]]
                 [io.dropwizard.metrics/metrics-graphite "3.1.2"]
                 [com.aphyr/metrics3-riemann-reporter "0.4.0"]

                 ;;External system integrations
                 [me.raynes/conch "0.5.2"]
                 [clj-mesos "0.22.2"]
                 [com.google.protobuf/protobuf-java "2.6.1"] ; used by clj-mesos
                 [org.clojure/tools.nrepl "0.2.3"]

                 ;;Ring
                 [ring/ring-core "1.4.0"]
                 [ring/ring-devel "1.4.0"]
                 [compojure "1.4.0"]
                 [hiccup "1.0.5"]
                 [ring/ring-json "0.2.0"]
                 [ring-edn "0.1.0"]
                 [com.duelinmarkers/ring-request-logging "0.2.0"]
                 [liberator "0.13"]

                 ;;Databases
                 [com.datomic/datomic-free "0.9.5206"
                  :exclusions [org.slf4j/slf4j-api
                               com.fasterxml.jackson.core/jackson-core
                               org.slf4j/jcl-over-slf4j
                               org.slf4j/jul-to-slf4j
                               org.slf4j/log4j-over-slf4j
                               org.slf4j/slf4j-nop
                               joda-time]]
                 [org.apache.curator/curator-framework "2.7.1"
                  :exclusions [io.netty/netty]]
                 [org.apache.curator/curator-recipes "2.7.1"
                  :exclusions [org.slf4j/slf4j-log4j12
                               org.slf4j/log4j
                               log4j
                               ]]
                 [org.apache.curator/curator-test "2.7.1"]

                 ;; incanter
                 ;[incanter "1.5.4"]
  ]

  :repositories {"maven2" {:url "http://files.couchbase.com/maven2/"}
                 "sonatype-oss-public" "https://oss.sonatype.org/content/groups/public/"}

  :filespecs [{:type :fn
               :fn (fn [p]
                     {:type :bytes :path "git-log"
                      :bytes (.trim (:out (clojure.java.shell/sh
                                            "git" "rev-parse" "HEAD")))})}]

  :profiles
  {:uberjar
   {:aot [cook.components]}

   :dev
   {:dependencies [[clj-http-fake "1.0.1"]
                   [org.clojure/test.check "0.6.1"]]
    :jvm-opts ["-Xms2G"
               "-XX:-OmitStackTraceInFastThrow"
               "-Xmx2G"
               "-Dcom.sun.management.jmxremote.port=5555"
               "-Dcom.sun.management.jmxremote.authenticate=false"
               "-Dcom.sun.management.jmxremote.ssl=false"]
    :source-paths []}}

  :test-selectors {:default (complement :integration)
                   :integration :integration
                   :all (constantly true)}

  :main cook.components

  :jvm-opts ["-Dpython.cachedir.skip=true"
             "-XX:MaxPermSize=500M"
             ;"-Dsun.security.jgss.native=true"
             ;"-Dsun.security.jgss.lib=/opt/mitkrb5/lib/libgssapi_krb5.so"
             ;"-Djavax.security.auth.useSubjectCredsOnly=false"
             "-verbose:gc"
             "-XX:+PrintGCDetails"
             "-Xloggc:gclog"
             "-XX:+UseGCLogFileRotation"
             "-XX:NumberOfGCLogFiles=20"
             "-XX:GCLogFileSize=128M"
             "-XX:+PrintGCDateStamps"
             "-XX:+HeapDumpOnOutOfMemoryError"
             ])

;;
;; Copyright (c) Two Sigma Open Source, LLC
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;;  http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;;
(defproject cook "0.1.0-SNAPSHOT"
  :description "This launches jobs on a Mesos cluster with fair sharing and preemption"
  :license {:name "Apache License, Version 2.0"}
  :dependencies [[org.clojure/clojure "1.6.0"]

                 ;;Data marshalling
                 [org.clojure/data.codec "0.1.0"]
                 [cheshire "5.3.1"]
                 [byte-streams "0.1.4"]
                 [org.clojure/data.json "0.2.2"]
                 [com.taoensso/nippy "2.8.0"
                  :exclusions [org.clojure/tools.reader]]
                 [circleci/clj-yaml "0.5.4"]

                 ;;Utility
                 [amalloy/ring-buffer "1.1"]
                 [lonocloud/synthread "1.0.4"]
                 [org.clojure/tools.namespace "0.2.4"]
                 [org.clojure/core.cache "0.6.3"]
                 [org.clojure/core.memoize "0.5.6"]
                 [clj-time "0.9.0"]
                 [org.clojure/core.async "0.1.346.0-17112a-alpha"]
                 [prismatic/schema "0.2.1"
                  :exclusions [potemkin]]
                 [clojure-miniprofiler "0.4.0"]
                 [jarohen/chime "0.1.6"]
                 [org.clojure/data.priority-map "0.0.5"]
                 [swiss-arrows "1.0.0"]
                 [riddley "0.1.10"]

                 ;;Logging
                 [org.clojure/tools.logging "0.2.6"]
                 [clj-logging-config "1.9.10"
                  :exclusions [log4j]]
                 [org.slf4j/slf4j-log4j12 "1.7.12"]
                 [com.draines/postal "1.11.0"
                  :exclusions [commons-codec]]
                 [prismatic/plumbing "0.1.1"]
                 [instaparse "1.4.0"]
                 [org.codehaus.jsr166-mirror/jsr166y "1.7.0"]
                 [clj-pid "0.1.1"]
                 [jarohen/chime "0.1.6"]

                 ;;Networking
                 [clj-http "2.0.0"]
                 [io.netty/netty "3.10.1.Final"]
                 [cc.qbits/jet "0.5.4"]

                 ;;Metrics
                 [dgrnbrg/metrics-clojure "2.6.1"
                  :exclusions [io.netty/netty
                               org.clojure/clojure]]
                 [io.dropwizard.metrics/metrics-graphite "3.1.2"]
                 [com.aphyr/metrics3-riemann-reporter "0.4.0"]
                 [riemann-clojure-client "0.4.1"]

                 ;;External system integrations
                 [me.raynes/conch "0.5.2"]
                 [clj-mesos "0.22.2"]
                 [com.google.protobuf/protobuf-java "2.6.1"] ; used by clj-mesos
                 [org.clojure/tools.nrepl "0.2.3"]

                 ;;Ring
                 [ring/ring-core "1.4.0"]
                 [ring/ring-devel "1.4.0"]
                 [compojure "1.4.0"]
                 [hiccup "1.0.5"]
                 [ring/ring-json "0.2.0"]
                 [ring-edn "0.1.0"]
                 [com.duelinmarkers/ring-request-logging "0.2.0"]
                 [liberator "0.13"]

                 ;;Databases
                 [com.datomic/datomic-free "0.9.5206"
                  :exclusions [org.slf4j/slf4j-api
                               com.fasterxml.jackson.core/jackson-core
                               org.slf4j/jcl-over-slf4j
                               org.slf4j/jul-to-slf4j
                               org.slf4j/log4j-over-slf4j
                               org.slf4j/slf4j-nop
                               joda-time]]
                 [org.apache.curator/curator-framework "2.7.1"
                  :exclusions [io.netty/netty]]
                 [org.apache.curator/curator-recipes "2.7.1"
                  :exclusions [org.slf4j/slf4j-log4j12
                               org.slf4j/log4j
                               log4j
                               ]]
                 [org.apache.curator/curator-test "2.7.1"]

                 ;; incanter
                 ;[incanter "1.5.4"]
  ]

  :repositories {"maven2" {:url "http://files.couchbase.com/maven2/"}
                 "sonatype-oss-public" "https://oss.sonatype.org/content/groups/public/"}

  :filespecs [{:type :fn
               :fn (fn [p]
                     {:type :bytes :path "git-log"
                      :bytes (.trim (:out (clojure.java.shell/sh
                                            "git" "rev-parse" "HEAD")))})}]

  :profiles
  {:uberjar
   {:aot [cook.components]}

   :dev
   {:dependencies [[clj-http-fake "1.0.1"]
                   [org.clojure/test.check "0.6.1"]]
    :jvm-opts ["-Xms2G"
               "-XX:-OmitStackTraceInFastThrow"
               "-Xmx2G"
               "-Dcom.sun.management.jmxremote.port=5555"
               "-Dcom.sun.management.jmxremote.authenticate=false"
               "-Dcom.sun.management.jmxremote.ssl=false"]
    :source-paths []}}

  :test-selectors {:default (complement :integration)
                   :integration :integration
                   :all (constantly true)}

  :main cook.components

  :jvm-opts ["-Dpython.cachedir.skip=true"
             "-XX:MaxPermSize=500M"
             ;"-Dsun.security.jgss.native=true"
             ;"-Dsun.security.jgss.lib=/opt/mitkrb5/lib/libgssapi_krb5.so"
             ;"-Djavax.security.auth.useSubjectCredsOnly=false"
             "-verbose:gc"
             "-XX:+PrintGCDetails"
             "-Xloggc:gclog"
             "-XX:+UseGCLogFileRotation"
             "-XX:NumberOfGCLogFiles=20"
             "-XX:GCLogFileSize=128M"
             "-XX:+PrintGCDateStamps"
             "-XX:+HeapDumpOnOutOfMemoryError"
             ])

Add riemann clojure client dependency

Add riemann clojure client dependency


(ns cmr.metadata-db.test.services.concept-constraints
  "Unit tests to verify post-commit concept constraints are enforced."
  (:require [clojure.test :refer :all]
            [cmr.metadata-db.services.concept-constraints :as cc]
            [cmr.metadata-db.data.memory-db :as mem-db]
            [cmr.metadata-db.data.concepts :as dc]
            [cmr.metadata-db.services.messages :as msg]))

(defn make-coll-concept
  ([provider-id concept-id revision-id]
   (make-coll-concept provider-id concept-id revision-id {}))
  ([provider-id concept-id revision-id extra-fields]
   {:provider-id provider-id
    :concept-type :collection
    :concept-id concept-id
    :revision-id revision-id
    :deleted false
    :extra-fields extra-fields}))

(defn make-coll-tombstone
  ([provider-id concept-id revision-id]
   (make-coll-tombstone provider-id concept-id revision-id {}))
  ([provider-id concept-id revision-id extra-fields]
   (assoc (make-coll-concept provider-id concept-id revision-id extra-fields)
          :deleted true)))

;; TODO these example concepts are wrong. The entry title is in extra-fields map
(def invalid-concepts
  [{:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C1" :revision-id 1 :entry-title "E1" :deleted false}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C1" :revision-id 2 :entry-title "E1" :deleted false}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C2" :revision-id 1 :entry-title "E1" :deleted false}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C2" :revision-id 2 :entry-title "E1" :deleted true}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C3" :revision-id 1 :entry-title "E1" :deleted false}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C4" :revision-id 1 :entry-title "E1" :deleted false}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C4" :revision-id 2 :entry-title "E1" :deleted true}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C4" :revision-id 20 :entry-title "E1" :deleted false}])

(def valid-concepts
  [{:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C1" :revision-id 1 :entry-title "E1" :deleted false}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C1" :revision-id 2 :entry-title "E1" :deleted true}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C2" :revision-id 1 :entry-title "E1" :deleted false}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C2" :revision-id 2 :entry-title "E1" :deleted true}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C3" :revision-id 1 :entry-title "E1" :deleted false}
   ;; Use this in integration test to make sure entry-title is successfully filtered on
   ; {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
   ; :concept-id "C4" :revision-id 1 :entry-title "E2" :deleted 0}
   ])

;; TODO change this test to check the specific list of concepts that comes back from keep-latest
(deftest verify-keep-latest-non-deleted-concepts
  ;; Make sure that filtering out old revisions and tombstones works as expected.
  (testing "Verifying keep-latest-non-deleted-concepts returns the correct concepts"
    (is (= 3 (count (cc/keep-latest-non-deleted-concepts invalid-concepts))))
    (is (= 1 (count (cc/keep-latest-non-deleted-concepts valid-concepts))))))

(defn run-constraint
  "TODO"
  [constraint saved-concept & existing-concepts]
  (let [db (mem-db/create-db (cons saved-concept existing-concepts))]
    (constraint db saved-concept)))

(defn assert-invalid
  "TODO"
  [error-msg constraint saved-concept & existing-concepts]
  (is (= error-msg
         (apply run-constraint constraint saved-concept existing-concepts))))

(defn assert-valid
  "TODO"
  [constraint saved-concept & existing-concepts]
  (is (nil? (apply run-constraint constraint saved-concept existing-concepts))))

(deftest entry-title-unique-constraint-test
  (let [test-concept (make-coll-concept "PROV1" "C1-PROV1" 5 {:entry-title "ET1"})
        is-valid (partial assert-valid cc/entry-title-unique-constraint test-concept)
        not-valid #(apply assert-invalid %1 cc/entry-title-unique-constraint test-concept %2)]

    (testing "valid cases"
      (testing "with empty database"
        (is-valid))
      (testing "another collection with entry title that is deleted is valid"
        (let [other-tombstone (make-coll-tombstone "PROV1" "C2-PROV1" 2 {:entry-title "ET1"})]
          (is-valid other-tombstone)))
      (testing "another provider with the same entry title is valid "
        (let [other-concept (make-coll-concept "PROV2" "C1-PROV1" 5 {:entry-title "ET1"})]
          (is-valid other-concept)))
      (testing "same concept id but earlier revision id is valid"
        (let [other-concept (make-coll-concept "PROV1" "C1-PROV1" 4 {:entry-title "ET1"})]
          (is-valid other-concept)))
      (testing "different entry titles are valid"
        (let [other-concept (make-coll-concept "PROV1" "C1-PROV1" 5 {:entry-title "ET2"})]
          (is-valid other-concept))))

    (testing "invalid cases"
      (testing "same entry title"
        (let [other-concept (make-coll-concept "PROV1" "C2-PROV1" 1 {:entry-title "ET1"})]
          (not-valid
            (msg/duplicate-entry-titles [test-concept other-concept])
            [other-concept]))))))

(comment

  (let [c1 (make-coll-concept "PROV1" "C1-PROV1" 1 {:entry-title "ET1"})
        c2 (make-coll-concept "PROV1" "C2-PROV1" 1 {:entry-title "ET1"})
        db (mem-db/create-db [c1 c2])]

    (cc/entry-title-unique-constraint
      db c1)
    )

  (dc/find-latest-concepts
    (mem-db/create-db [(make-coll-concept "PROV1" "C1-PROV1" 1 {:entry-title "ET1"})
                       (make-coll-concept "PROV1" "C2-PROV1" 1 {:entry-title "ET1"})])
    {:entry-title "ET1"
     :concept-type :collection
     :provider-id "PROV1"})

  )

(ns cmr.metadata-db.test.services.concept-constraints
  "Unit tests to verify post-commit concept constraints are enforced."
  (:require [clojure.test :refer :all]
            [cmr.metadata-db.services.concept-constraints :as cc]
            [cmr.metadata-db.data.memory-db :as mem-db]
            [cmr.metadata-db.data.concepts :as dc]
            [cmr.metadata-db.services.messages :as msg]))

(defn make-coll-concept
  ([provider-id concept-id revision-id]
   (make-coll-concept provider-id concept-id revision-id {}))
  ([provider-id concept-id revision-id extra-fields]
   {:provider-id provider-id
    :concept-type :collection
    :concept-id concept-id
    :revision-id revision-id
    :deleted false
    :extra-fields extra-fields}))

(defn make-coll-tombstone
  ([provider-id concept-id revision-id]
   (make-coll-tombstone provider-id concept-id revision-id {}))
  ([provider-id concept-id revision-id extra-fields]
   (assoc (make-coll-concept provider-id concept-id revision-id extra-fields)
          :deleted true)))

;; TODO these example concepts are wrong. The entry title is in extra-fields map
(def invalid-concepts
  [{:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C1" :revision-id 1 :entry-title "E1" :deleted false}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C1" :revision-id 2 :entry-title "E1" :deleted false}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C2" :revision-id 1 :entry-title "E1" :deleted false}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C2" :revision-id 2 :entry-title "E1" :deleted true}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C3" :revision-id 1 :entry-title "E1" :deleted false}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C4" :revision-id 1 :entry-title "E1" :deleted false}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C4" :revision-id 2 :entry-title "E1" :deleted true}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C4" :revision-id 20 :entry-title "E1" :deleted false}])

(def valid-concepts
  [{:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C1" :revision-id 1 :entry-title "E1" :deleted false}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C1" :revision-id 2 :entry-title "E1" :deleted true}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C2" :revision-id 1 :entry-title "E1" :deleted false}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C2" :revision-id 2 :entry-title "E1" :deleted true}
   {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
    :concept-id "C3" :revision-id 1 :entry-title "E1" :deleted false}
   ;; Use this in integration test to make sure entry-title is successfully filtered on
   ; {:provider-id "PROV1" :metadata "xml here" :format "echo10" :concept-type :collection
   ; :concept-id "C4" :revision-id 1 :entry-title "E2" :deleted 0}
   ])

;; TODO change this test to check the specific list of concepts that comes back from keep-latest
(deftest verify-keep-latest-non-deleted-concepts
  ;; Make sure that filtering out old revisions and tombstones works as expected.
  (testing "Verifying keep-latest-non-deleted-concepts returns the correct concepts"
    (is (= 3 (count (cc/keep-latest-non-deleted-concepts invalid-concepts))))
    (is (= 1 (count (cc/keep-latest-non-deleted-concepts valid-concepts))))))

(defn run-constraint
  "TODO"
  [constraint saved-concept & existing-concepts]
  (let [db (mem-db/create-db (cons saved-concept existing-concepts))]
    (constraint db saved-concept)))

(defn assert-invalid
  "TODO"
  [error-msg constraint saved-concept & existing-concepts]
  (is (= error-msg
         (apply run-constraint constraint saved-concept existing-concepts))))

(defn assert-valid
  "TODO"
  [constraint saved-concept & existing-concepts]
  (is (nil? (apply run-constraint constraint saved-concept existing-concepts))))

(deftest entry-title-unique-constraint-test
  (let [test-concept (make-coll-concept "PROV1" "C1-PROV1" 5 {:entry-title "ET1"})
        is-valid (partial assert-valid cc/entry-title-unique-constraint)
        not-valid #(apply assert-invalid %1 cc/entry-title-unique-constraint test-concept %2)]

    (testing "valid cases"
      (testing "with empty database"
        (is-valid test-concept))
      (testing "another collection with entry title that is deleted is valid"
        (let [other-tombstone (make-coll-tombstone "PROV1" "C2-PROV1" 2 {:entry-title "ET1"})]
          (is-valid test-concept other-tombstone)))
      (testing "another provider with the same entry title is valid "
        (let [other-concept (make-coll-concept "PROV2" "C1-PROV1" 5 {:entry-title "ET1"})]
          (is-valid test-concept other-concept)))
      (testing "same concept id but earlier revision id is valid"
        (let [other-concept (make-coll-concept "PROV1" "C1-PROV1" 4 {:entry-title "ET1"})]
          (is-valid test-concept other-concept)))
      (testing "different entry titles are valid"
        (let [other-concept (make-coll-concept "PROV1" "C1-PROV1" 5 {:entry-title "ET2"})]
          (is-valid test-concept other-concept)))
      (testing "multiple valid concepts are still valid"
        (is-valid test-concept
                  (make-coll-concept "PROV1" "C2-PROV1" 1 {:entry-title "ET1"})
                  (make-coll-tombstone "PROV1" "C2-PROV1" 2 {:entry-title "ET1"})
                  (make-coll-concept "PROV2" "C1-PROV1" 5 {:entry-title "ET1"})
                  (make-coll-concept "PROV1" "C1-PROV1" 4 {:entry-title "ET1"})
                  (make-coll-concept "PROV1" "C1-PROV1" 5 {:entry-title "ET2"}))))

    (testing "invalid cases"
      (testing "same entry title"
        (let [other-concept (make-coll-concept "PROV1" "C2-PROV1" 1 {:entry-title "ET1"})]
          (not-valid
            (msg/duplicate-entry-titles [test-concept other-concept])
            [other-concept]))))))

(comment

  (let [c1 (make-coll-concept "PROV1" "C1-PROV1" 1 {:entry-title "ET1"})
        c2 (make-coll-concept "PROV1" "C2-PROV1" 1 {:entry-title "ET1"})
        db (mem-db/create-db [c1 c2])]

    (cc/entry-title-unique-constraint
      db c1)
    )

  (dc/find-latest-concepts
    (mem-db/create-db [(make-coll-concept "PROV1" "C1-PROV1" 1 {:entry-title "ET1"})
                       (make-coll-concept "PROV1" "C2-PROV1" 1 {:entry-title "ET1"})])
    {:entry-title "ET1"
     :concept-type :collection
     :provider-id "PROV1"})

  )

CMR-1195: Refactor and finish unit tests for entry-title constraint


(ns replete.core
  (:require-macros [cljs.env.macros :refer [ensure with-compiler-env]]
                   [cljs.analyzer.macros :refer [no-warn]])
  (:require [cljs.js :as cljs]
            [cljs.pprint :refer [pprint]]
            [cljs.tagged-literals :as tags]
            [cljs.tools.reader :as r]
            [cljs.tools.reader.reader-types :refer [string-push-back-reader]]
            [cljs.analyzer :as ana]
            [cljs.compiler :as c]
            [cljs.env :as env]
            [cljs.repl :as repl]
            [clojure.string :as s]))

(def DEBUG false)

(defonce st (cljs/empty-state))

(defn ^:export setup-cljs-user []
  (js/eval "goog.provide('cljs.user')")
  (js/eval "goog.require('cljs.core')"))

(def app-env (atom nil))

(defn map-keys [f m]
  (reduce-kv (fn [r k v] (assoc r (f k) v)) {} m))

(defn ^:export init-app-env [app-env]
  (reset! replete.core/app-env (map-keys keyword (cljs.core/js->clj app-env))))

(defn user-interface-idiom-ipad?
  "Returns true iff the interface idiom is iPad."
  []
  (= "iPad" (:user-interface-idiom @app-env)))

(defn repl-read-string [line]
  (r/read-string {:read-cond :allow :features #{:cljs}} line))

(defn ^:export is-readable? [line]
  (binding [r/*data-readers* tags/*cljs-data-readers*]
    (with-compiler-env st
      (try
        (repl-read-string line)
        true
        (catch :default _
          false)))))

(def current-ns (atom 'cljs.user))

(defn ns-form? [form]
  (and (seq? form) (= 'ns (first form))))

(def repl-specials '#{in-ns require require-macros doc})

(defn repl-special? [form]
  (and (seq? form) (repl-specials (first form))))

(def repl-special-doc-map
  '{in-ns          {:arglists ([name])
                    :doc      "Sets *cljs-ns* to the namespace named by the symbol, creating it if needed."}
    require        {:arglists ([& args])
                    :doc      "Loads libs, skipping any that are already loaded."}
    require-macros {:arglists ([& args])
                    :doc      "Similar to the require REPL special function but
                    only for macros."}
    doc            {:arglists ([name])
                    :doc      "Prints documentation for a var or special form given its name"}})

(defn- repl-special-doc [name-symbol]
  (assoc (repl-special-doc-map name-symbol)
    :name name-symbol
    :repl-special-function true))

(defn reflow [text]
  (and text
    (-> text
     (s/replace #" \n  " "")
     (s/replace #"\n  " " "))))

;; Copied from cljs.analyzer.api (which hasn't yet been converted to cljc)
(defn resolve
  "Given an analysis environment resolve a var. Analogous to
   clojure.core/resolve"
  [env sym]
  {:pre [(map? env) (symbol? sym)]}
  (try
    (ana/resolve-var env sym
      (ana/confirm-var-exists-throw))
    (catch :default _
      (ana/resolve-macro-var env sym))))

(defn extension->lang [extension]
  (if (= ".js" extension)
    :js
    :clj))

(defn load-and-callback! [path extension cb]
  (when-let [source (js/REPLETE_LOAD (str path extension))]
    (cb {:lang   (extension->lang extension)
         :source source})
    :loaded))

(defn load [{:keys [name macros path] :as full} cb]
  #_(prn full)
  (loop [extensions (if macros
                      [".clj" ".cljc"]
                      [".cljs" ".cljc" ".js"])]
    (if extensions
      (when-not (load-and-callback! path (first extensions) cb)
        (recur (next extensions)))
      (cb nil))))

(defn require [macros-ns? sym reload]
  (cljs.js/require
    {:*compiler*     st
     :*data-readers* tags/*cljs-data-readers*
     :*load-fn*      load
     :*eval-fn*      cljs/js-eval}
    sym
    reload
    {:macros-ns macros-ns?
     :verbose   (:verbose @app-env)}
    (fn [res]
      #_(println "require result:" res))))

(defn require-destructure [macros-ns? args]
  (let [[[_ sym] reload] args]
    (require macros-ns? sym reload)))

(defn print-error [error]
  (let [cause (.-cause error)]
    (println (.-message cause))
    (println (.-stack cause))))

(defn ^:export read-eval-print
  ([source]
   (read-eval-print source true))
  ([source expression?]
   (binding [ana/*cljs-ns* @current-ns
             *ns* (create-ns @current-ns)
             r/*data-readers* tags/*cljs-data-readers*]
     (let [expression-form (and expression? (repl-read-string source))]
       (if (repl-special? expression-form)
         (let [env (assoc (ana/empty-env) :context :expr
                                          :ns {:name @current-ns})]
           (case (first expression-form)
             in-ns (reset! current-ns (second (second expression-form)))
             require (require-destructure false (rest expression-form))
             require-macros (require-destructure true (rest expression-form))
             doc (if (repl-specials (second expression-form))
                   (repl/print-doc (repl-special-doc (second expression-form)))
                   (repl/print-doc
                     (let [sym (second expression-form)
                           var (with-compiler-env st
                                 (resolve env sym))]
                       (update (:meta var)
                         :doc (if (user-interface-idiom-ipad?)
                                identity
                                reflow))))))
           (prn nil))
         (try
           (cljs/eval-str
             st
             source
             (if expression? source "File")
             (merge
               {:ns         @current-ns
                :load       load
                :eval       cljs/js-eval
                :source-map false
                :verbose    (:verbose @app-env)}
               (when expression?
                 {:context       :expr
                  :def-emits-var true}))
             (fn [{:keys [ns value error] :as ret}]
               (if expression?
                 (if-not error
                   (do
                     (prn value)
                     (when-not
                       (or ('#{*1 *2 *3 *e} expression-form)
                         (ns-form? expression-form))
                       (set! *3 *2)
                       (set! *2 *1)
                       (set! *1 value))
                     (reset! current-ns ns)
                     nil)
                   (do
                     (set! *e error))))
               (when error
                 (print-error error))))
           (catch :default e
             (print-error e))))))))

#_(defn read-eval-print-form [form env]
  (let [_ (when DEBUG (prn "form:" form))]
    (if (repl-special? form)
      (case (first form)
        in-ns (reset! current-ns (second (second form)))
        doc (if (repl-specials (second form))
              (repl/print-doc (repl-special-doc (second form)))
              (repl/print-doc
               (let [sym (second form)
                     var (resolve env sym)]
                 (update (:meta var)
                         :doc (if (user-interface-idiom-ipad?)
                                identity
                                reflow))))))
      (let [_ (when DEBUG (prn "form:" form))
            ast (ana/analyze env form)
            _ (when DEBUG (prn "ast:" ast))
            js (with-out-str
                 (ensure
                  (c/emit ast)))
            _ (when DEBUG (prn "js:" js))]
        (try (prn (let [ret (js/eval js)]
                    (when-not
                        (or ('#{*1 *2 *3 *e} form)
                            (ns-form? form))
                      (set! *3 *2)
                      (set! *2 *1)
                      (set! *1 ret))
                    (reset! current-ns ana/*cljs-ns*)
                    ret))
             (catch js/Error e
               (set! *e e)
               (print (.-message e) "\n" (first (s/split (.-stack e) #"eval code")))))))))

#_(defn ^:export read-eval-print-orig [lines]
  (binding [ana/*cljs-ns* @current-ns
            *ns* (create-ns @current-ns)
            r/*data-readers* tags/*cljs-data-readers*]
    (with-compiler-env cenv
      (let [env (assoc (ana/empty-env) :context :expr
                                       :ns {:name @current-ns}
                                       :def-emits-var true)]
        (try
          (let [infile (string-push-back-reader lines)
                eof (js-obj)]
            (loop []
              (let [form (r/read {:eof eof :read-cond :allow :features #{:cljs}} infile)]
                (when-not (identical? eof form)
                  (read-eval-print-form form env)
                  (recur)))))
          (catch js/Error e
            (println (.-message e))))))))

Remove commented code

Remove commented code


(ns overtone.sc.machinery.ugen.metadata.extras.bat
  (:use [overtone.sc.machinery.ugen common check]))

(def specs
  [
   {:name "Coyote"
    :summary "an amplitude tracking based onset detector"
    :args [{:name "in"
            :default 0
            :doc "The input signal"}

           {:name "track-fall"
            :default 0.2
            :doc "60dB convergence time for the initial amplitude
                  tracker." }

           {:name "slow-lag"
            :default 0.2
            :doc "Lag time for the slow smoother. "}

           {:name "fast-lag"
            :default 0.01
            :doc "Lag time for the fast smoother."}

           {:name "fast-mul"
            :default 0.5
            :doc "Multiplier for the fast smoother. At the instant of
                  onsets, fast smoother output will exceed the slow
                  smoother and trigger an onset report. If you want to
                  tweak the sensitivity of the tracking, you should try
                  tweaking this value first. Higher values(approaching
                  to 1) makes the tracking more sensitive." }

           {:name "thresh"
            :default 0.05
            :doc "The minimum threshold for the input to begin tracking
                  onsets. "}

           {:name "min-dur"
            :default 0.1
            :doc "Minimum duration between events."}]

    :rates #{:kr}
    :doc "Coyote is an onset detector which tries to find onset attacks
          in a signal without using FFT processing. It tracks the
          amplitude changes in the incoming signal and sends a trigger
          when an onset is found. To get the best tracking for a
          particular signal by tweaking the arguments, one needs to
          understand how the onset detection works inside the UGen:

          Coyote compares three different analysis results in parallel
          and tries to report an onset event in the signal. The first
          phase is amplitude tracking. The trackFall argument is the
          60dB convergence time of the decaying signal(the attack time
          is constant: 0.001, the process is the same with the Amplitude
          UGen, trackFall is the releaseTime). The output of this
          tracking is divided to 3 inputs inside. The first two are
          smoothers(lowpass filters) with different lag times. slowLag
          is the lag time of the slow smoother, and the fastLag is the
          lag time of the fast one. The fast smoother is multiplied by a
          value(fastMul argument) which should be between 0 and 0.9 so
          its output is always below the slow smoother, except in
          onsets. So when an onset occurs, the fast smoother output
          rises quicker than the slow smoother, and when the fast one
          exceeds the slower at an instant(occurs only at onsets), a
          trigger is sent to the output from the UGen. For the next
          trigger to happen, a specified time should pass which is
          defined by the minDur parameter. So minDur defines the minimum
          time between events/triggers.

          This approach is extremely fast in response(compared to FFT
          based detectors) when detecting onsets and works well on most
          contexts(guitar, percussion, etc...). But it has a drawback
          when there are sustaining sounds present from the same
          instrument at the moment of an onset, so there is a third unit
          inside that averages the input beginning from the last trigger
          whose output is also smoothed by a smoother(lag time is also
          set to slowLag) and it too is compared with the output of fast
          smoother to make the tracking work better when there are
          sustaining sounds present at the moment of an onset.

          The default values are a good starting point and works well on
          many contexts." }

   {:name "TrigAvg"
    :summary "triggered signal averager"
    :args [{:name "in"
            :default 0
            :doc "The input signal"}

           {:name "trig"
            :default 0
            :doc "When triggered, TrigAvg forgets the past average and
                  starts averaging from zero." }]
    :rates #{:kr}
    :doc "Averages the absolute values of its input between triggers."}

   {:name "WAmp"
    :summary "windowed amplitude follower"
    :args [{:name "in"
            :default 0
            :doc "The input signal"}

           {:name "win-size"
            :default 0.1
            :doc "The window size in seconds. Not modulatable."}]
    :rates #{:kr}
    :doc "Averages and outputs the absolute value of incoming signals
          received between now and (now - winSize) seconds."}

   {:name "MarkovSynth"
    :summary "First order Markov Chain implementation for audio signals"
    :args [{:name "in"
            :default 0
            :doc "The input signal"}

           {:name "is-recording"
            :default 1
            :doc "if non-zero, MarkovSynth populates the internal table with its signal input."}

           {:name "wait-time"
            :default 2
            :doc "Defines the wait time of the UGen to start synthesizing the table, in seconds."}

           {:name "table-size"
            :default 10
            :doc "The probability table size for each sample. High values are memory hungry!"}]
    :rates #{:ar}
    :doc "MarkovSynth populates a sample to sample transition
          probability table with its signal input. Each possible sample
          value in an 16bit signal has its own transition probability
          table whose size is defined by the table-size argument at
          creation time. It waits and populates the table for wait-time
          seconds and then starts synthesizing audio by continuously
          outputting a random value selected from the probability table
          of the last synthesized sample. Once the end of table is
          reached for a single sample, its index wraps back to zero and
          populating continues in this fashion as long as is-recording
          argument is non-zero. The character of the input is mainly
          defined by the way its input signal changes. So input signals
          showing little difference in amplitude and periodicity has a
          similar quality in output. The output becomes less dynamic.

          If the tableSize is 1, the output is usually a reflection of
          the input. tableSize of 2 makes some funny blips and
          blops. When tableSize goes higher, older and older transition
          values are taken into account and the output changes
          accordingly. You should be careful with the table-size as it
          allocates all the memory for the tables beforehand so it may
          cause troubles.

          You may want to use leak-dc on its output as the output is
          offset agnostic, it just selects a past-recorded transition
          value at random."}

   {:name "FrameCompare"
    :summary "calculates spectral MSE distance of two fft chains"
    :args [{:name "buffer1"
            :doc "FFT chain 1"}

           {:name "buffer2"
            :doc "FFT chain 2"}

           {:name "w-amount"
            :default 0.5
            :doc "Influence of the weight matrix (should be between 0
                  and 1). Weight matrix helps to minimize errors on
                  regions with more energy. "}]
    :rates #{:kr}
    :doc "Given two FFT chains, this UGen calculates the MSE between the
          magnitudes of these two inputs and provides a continuous
          analytic similarity rating (lower the value, more similar the
          inputs). In it's current state, only hanning window should be
          used (wintype: 1)."}

   {:name "NeedleRect"
    :summary ""
    :args [{:name "rate"
            :default 1
            :doc ""}

           {:name "img-width"
            :default 100
            :doc ""}

           {:name "img-height"
            :default 100
            :doc ""}

           {:name "rect-x"
            :default 0
            :doc ""}

           {:name "rect-y"
            :default 0
            :doc ""}

           {:name "rect-w"
            :default 100
            :doc ""}

           {:name "rect-h"
            :default 100
            :doc ""}]
    :rates #{:ar}
    :doc ""}

   {:name "SkipNeedle"
    :summary ""
    :args [{:name "range"
            :default 44100
            :doc ""}

           {:name "rate"
            :default 10
            :doc ""}

           {:name "offset"
            :default 0
            :doc ""}]
    :rates #{:ar}
    :doc ""}])

(ns overtone.sc.machinery.ugen.metadata.extras.bat
  (:use [overtone.sc.machinery.ugen common check]))

(def specs
  [
   {:name "Coyote"
    :summary "an amplitude tracking based onset detector"
    :args [{:name "in"
            :default 0
            :doc "The input signal"}

           {:name "track-fall"
            :default 0.2
            :doc "60dB convergence time for the initial amplitude
                  tracker." }

           {:name "slow-lag"
            :default 0.2
            :doc "Lag time for the slow smoother. "}

           {:name "fast-lag"
            :default 0.01
            :doc "Lag time for the fast smoother."}

           {:name "fast-mul"
            :default 0.5
            :doc "Multiplier for the fast smoother. At the instant of
                  onsets, fast smoother output will exceed the slow
                  smoother and trigger an onset report. If you want to
                  tweak the sensitivity of the tracking, you should try
                  tweaking this value first. Higher values (approaching
                  to 1) makes the tracking more sensitive." }

           {:name "thresh"
            :default 0.05
            :doc "The minimum threshold for the input to begin tracking
                  onsets. "}

           {:name "min-dur"
            :default 0.1
            :doc "Minimum duration between events."}]

    :rates #{:kr}
    :doc "Coyote is an onset detector which tries to find onset attacks
          in a signal without using FFT processing. It tracks the
          amplitude changes in the incoming signal and sends a trigger
          when an onset is found. To get the best tracking for a
          particular signal by tweaking the arguments, one needs to
          understand how the onset detection works inside the UGen:

          Coyote compares three different analysis results in parallel
          and tries to report an onset event in the signal. The first
          phase is amplitude tracking. The track-fall argument is the
          60dB convergence time of the decaying signal(the attack time
          is constant: 0.001, the process is the same with the amplitude
          UGen, track-fall is the release-time). The output of this
          tracking is divided to 3 inputs inside. The first two are
          smoothers (lowpass filters) with different lag times. slow-lag
          is the lag time of the slow smoother, and the fast-lag is the
          lag time of the fast one. The fast smoother is multiplied by a
          value(fast-mul argument) which should be between 0 and 0.9 so
          its output is always below the slow smoother, except in
          onsets. So when an onset occurs, the fast smoother output
          rises quicker than the slow smoother, and when the fast one
          exceeds the slower at an instant (occurs only at onsets), a
          trigger is sent to the output from the UGen. For the next
          trigger to happen, a specified time should pass which is
          defined by the minDur parameter. So min-dur defines the minimum
          time between events/triggers.

          This approach is extremely fast in response (compared to FFT
          based detectors) when detecting onsets and works well on most
          contexts (guitar, percussion, etc...). But it has a drawback
          when there are sustaining sounds present from the same
          instrument at the moment of an onset, so there is a third unit
          inside that averages the input beginning from the last trigger
          whose output is also smoothed by a smoother (lag time is also
          set to slowLag) and it too is compared with the output of fast
          smoother to make the tracking work better when there are
          sustaining sounds present at the moment of an onset.

          The default values are a good starting point and works well on
          many contexts." }

   {:name "TrigAvg"
    :summary "triggered signal averager"
    :args [{:name "in"
            :default 0
            :doc "The input signal"}

           {:name "trig"
            :default 0
            :doc "When triggered, TrigAvg forgets the past average and
                  starts averaging from zero." }]
    :rates #{:kr}
    :doc "Averages the absolute values of its input between triggers."}

   {:name "WAmp"
    :summary "windowed amplitude follower"
    :args [{:name "in"
            :default 0
            :doc "The input signal"}

           {:name "win-size"
            :default 0.1
            :doc "The window size in seconds. Not modulatable."}]
    :rates #{:kr}
    :doc "Averages and outputs the absolute value of incoming signals
          received between now and (now - winSize) seconds."}

   {:name "MarkovSynth"
    :summary "First order Markov Chain implementation for audio signals"
    :args [{:name "in"
            :default 0
            :doc "The input signal"}

           {:name "is-recording"
            :default 1
            :doc "if non-zero, MarkovSynth populates the internal table with its signal input."}

           {:name "wait-time"
            :default 2
            :doc "Defines the wait time of the UGen to start synthesizing the table, in seconds."}

           {:name "table-size"
            :default 10
            :doc "The probability table size for each sample. High values are memory hungry!"}]
    :rates #{:ar}
    :doc "MarkovSynth populates a sample to sample transition
          probability table with its signal input. Each possible sample
          value in an 16bit signal has its own transition probability
          table whose size is defined by the table-size argument at
          creation time. It waits and populates the table for wait-time
          seconds and then starts synthesizing audio by continuously
          outputting a random value selected from the probability table
          of the last synthesized sample. Once the end of table is
          reached for a single sample, its index wraps back to zero and
          populating continues in this fashion as long as is-recording
          argument is non-zero. The character of the input is mainly
          defined by the way its input signal changes. So input signals
          showing little difference in amplitude and periodicity has a
          similar quality in output. The output becomes less dynamic.

          If the tableSize is 1, the output is usually a reflection of
          the input. tableSize of 2 makes some funny blips and
          blops. When tableSize goes higher, older and older transition
          values are taken into account and the output changes
          accordingly. You should be careful with the table-size as it
          allocates all the memory for the tables beforehand so it may
          cause troubles.

          You may want to use leak-dc on its output as the output is
          offset agnostic, it just selects a past-recorded transition
          value at random."}

   {:name "FrameCompare"
    :summary "calculates spectral MSE distance of two fft chains"
    :args [{:name "buffer1"
            :doc "FFT chain 1"}

           {:name "buffer2"
            :doc "FFT chain 2"}

           {:name "w-amount"
            :default 0.5
            :doc "Influence of the weight matrix (should be between 0
                  and 1). Weight matrix helps to minimize errors on
                  regions with more energy. "}]
    :rates #{:kr}
    :doc "Given two FFT chains, this UGen calculates the MSE between the
          magnitudes of these two inputs and provides a continuous
          analytic similarity rating (lower the value, more similar the
          inputs). In it's current state, only hanning window should be
          used (wintype: 1)."}

   {:name "NeedleRect"
    :summary ""
    :args [{:name "rate"
            :default 1
            :doc ""}

           {:name "img-width"
            :default 100
            :doc ""}

           {:name "img-height"
            :default 100
            :doc ""}

           {:name "rect-x"
            :default 0
            :doc ""}

           {:name "rect-y"
            :default 0
            :doc ""}

           {:name "rect-w"
            :default 100
            :doc ""}

           {:name "rect-h"
            :default 100
            :doc ""}]
    :rates #{:ar}
    :doc ""}

   {:name "SkipNeedle"
    :summary ""
    :args [{:name "range"
            :default 44100
            :doc ""}

           {:name "rate"
            :default 10
            :doc ""}

           {:name "offset"
            :default 0
            :doc ""}]
    :rates #{:ar}
    :doc ""}])

use clojure-style names in Coyote docstring.


(def projects
  "A map of the other development projects to their versions"
  {:cmr-access-control-app "0.1.0-SNAPSHOT"
   :cmr-acl-lib "0.1.0-SNAPSHOT"
   :cmr-bootstrap-app "0.1.0-SNAPSHOT"
   :cmr-collection-renderer-lib "0.1.0-SNAPSHOT"
   :cmr-common-app-lib "0.1.0-SNAPSHOT"
   :cmr-common-lib "0.1.1-SNAPSHOT"
   :cmr-cubby-app "0.1.0-SNAPSHOT"
   :cmr-elastic-utils-lib "0.1.0-SNAPSHOT"
   :cmr-es-spatial-plugin "0.1.0-SNAPSHOT"
   :cmr-index-set-app "0.1.0-SNAPSHOT"
   :cmr-indexer-app "0.1.0-SNAPSHOT"
   :cmr-ingest-app "0.1.0-SNAPSHOT"
   :cmr-message-queue-lib "0.1.0-SNAPSHOT"
   :cmr-metadata-db-app "0.1.0-SNAPSHOT"
   :cmr-mock-echo-app "0.1.0-SNAPSHOT"
   :cmr-oracle-lib "0.1.0-SNAPSHOT"
   :cmr-orbits-lib "0.1.0-SNAPSHOT"
   :cmr-schema-validation-lib "0.1.0-SNAPSHOT"
   :cmr-search-app "0.1.0-SNAPSHOT"
   :cmr-search-relevancy-test "0.1.0-SNAPSHOT"
   :cmr-spatial-lib "0.1.0-SNAPSHOT"
   :cmr-system-int-test "0.1.0-SNAPSHOT"
   :cmr-transmit-lib "0.1.0-SNAPSHOT"
   :cmr-umm-lib "0.1.0-SNAPSHOT"
   :cmr-umm-spec-lib "0.1.0-SNAPSHOT"
   :cmr-virtual-product-app "0.1.0-SNAPSHOT"})

(def project-dependencies
  "A list of other projects as maven dependencies"
  (doall (map (fn [[project-name version]]
                (let [maven-name (symbol "nasa-cmr" (name project-name))]
                  [maven-name version]))
              projects)))

(def create-checkouts-commands
  (vec
    (apply concat ["do"
                   "shell" "mkdir" "checkouts,"]
           (map (fn [project-name]
                  ["shell" "ln" "-s" (str "../../" (subs (name project-name) 4)) "checkouts/,"])
                (keys projects)))))

;; The version number here is for the sprint number. It will be incremented each sprint. The second
;; number is for which delivery of the version was given to ECHO for use.
(defproject nasa-cmr/cmr-dev-system "0.1.0-SNAPSHOT"
  :description "Dev System combines together the separate microservices of the CMR into a single
               application to make it simpler to develop."
  :url "https://github.com/nasa/Common-Metadata-Repository/tree/master/dev-system"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :exclusions [[commons-codec/commons-codec]
               [org.clojure/clojure]
               [ring/ring-codec]]
  :dependencies ~(concat '[[commons-codec/commons-codec "1.11"]
                           [org.clojure/clojure "1.10.0"]
                           ;; Add groovy to support groovy scripting in elastic
                           [org.codehaus.groovy/groovy-all "2.4.0"]
                           [ring/ring-codec "1.1.1"]]
                  project-dependencies)
  :plugins [[lein-environ "1.1.0"]
            [lein-shell "0.5.0"]
            [test2junit "1.3.3"]]
  :repl-options {:init-ns user
                 :timeout 300000
                 :welcome (do
                           (println (slurp "resources/text/banner.txt"))
                           (println (slurp "resources/text/loading.txt")))}
  :jvm-opts ["-XX:-OmitStackTraceInFastThrow"
             "-Dclojure.compiler.direct-linking=true"
             ;; Avoid race conditions when creating jruby context.
             "-Dorg.jruby.embed.localcontext.scope=concurrent"]
             ;; Uncomment to enable logging in jetty.
             ; "-Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StrErrLog"
             ; "-Dorg.eclipse.jetty.LEVEL=INFO"
             ; "-Dorg.eclipse.jetty.websocket.LEVEL=INFO"]
  :profiles {:security {:plugins [[com.livingsocial/lein-dependency-check "1.1.1"]]
                        :dependency-check {:output-format [:all]
                                           :suppression-file "resources/security/suppression.xml"
                                           :properties-file "resources/security/dependencycheck.properties"}}
             :dev-dependencies {:exclusions [[org.clojure/tools.nrepl]]
                                :dependencies [[criterium "0.4.4"]
                                               [debugger "0.2.0"]
                                               [drift "1.5.3"]
                                               [org.clojars.gjahad/debug-repl "0.3.3"]
                                               [org.clojure/tools.namespace "0.2.11"]
                                               [org.clojure/tools.nrepl "0.2.13"]
                                               [pjstadig/humane-test-output "0.9.0"]
                                               [proto-repl "0.3.1"]
                                               [proto-repl-charts "0.3.2"]
                                               [proto-repl-sayid "0.1.3"]
                                               [ring-mock "0.1.5"]]
                                ;; XXX Note that profiling can be kept in a profile,
                                ;;     with no need to comment/uncomment.
                                ;; Use the following to enable JMX profiling with visualvm
                                ;:jvm-opts ^:replace ["-server"
                                ;                     "-Dcom.sun.management.jmxremote"
                                ;                     "-Dcom.sun.management.jmxremote.ssl=false"
                                ;                     "-Dcom.sun.management.jmxremote.authenticate=false"
                                ;                     "-Dcom.sun.management.jmxremote.port=1098"]
                                :source-paths ["src" "dev" "test"]
                                :injections [(require 'pjstadig.humane-test-output)
                                             (pjstadig.humane-test-output/activate!)]}
              ;; This is to separate the dependencies from the dev-config specified in profiles.clj
             :dev [:dev-dependencies :dev-config]
             ;; The following run-* profiles are used in conjunction with other lein
             ;; profiles to set the default CMR run mode and may be used in the
             ;; following manner:
             ;;
             ;;   $ lein with-profile +run-external repl
             ;;
             ;; which will use dev and the other default profiles in addition to
             ;; run-external (or whichever run mode profile is given).
             :run-in-memory {:jvm-opts ["-Dcmr.runmode=in-memory"]}
             :run-external {:jvm-opts ["-Dcmr.runmode=external"]}
             :uberjar {:main cmr.dev-system.runner
             ;; See http://stephen.genoprime.com/2013/11/14/uberjar-with-titan-dependency.html
                       :uberjar-merge-with {#"org\.apache\.lucene\.codecs\.*" [slurp str spit]}
                       :aot :all}
             :static {}
             ;; This profile is used for linting and static analysis. To run for this
             ;; project, use `lein lint` from inside the project directory. To run for
             ;; all projects at the same time, use the same command but from the top-
             ;; level directory.
             :lint {:source-paths ^:replace ["src"]
                    :test-paths ^:replace []
                    :plugins [[jonase/eastwood "0.2.5"]
                              [lein-ancient "0.6.15"]
                              [lein-bikeshed "0.5.0"]
                              [lein-kibit "0.1.6"]
                              [venantius/yagni "0.1.4"]]}
             ;; The following profile is overriden on the build server or in the user's
             ;; ~/.lein/profiles.clj file.
             :internal-repos {}}
  :aliases {
            ;; Creates the checkouts directory to the local projects
            "create-checkouts" ~create-checkouts-commands
            ;; Alias to test2junit for consistency with lein-test-out
            "test-out"
            ["test2junit"]
            ;; Installs the Elasticsearch Marvel plugin locally.
            ;; Visit http://localhost:9210/_plugin/marvel/sense/index.html
            "install-marvel"
            ["shell" "cmr" "install" "local" "marvel"]
            ;; Linting aliases
            "kibit"
            ["do"
              ["shell" "echo" "== Kibit =="]
              ["with-profile" "lint" "kibit"]]
            "eastwood"
            ["with-profile" "lint" "eastwood" "{:namespaces [:source-paths]}"]
            "bikeshed"
            ["with-profile" "lint" "bikeshed" "--max-line-length=100"]
            "yagni"
            ["with-profile" "lint" "yagni"]
            "check-deps"
            ["with-profile" "lint" "ancient" ":all"]
            "check-sec"
            ["with-profile" "security" "dependency-check"]
            "lint"
            ["do"
              ["check"] ["kibit"] ["eastwood"]]
            ;; Placeholder for future docs and enabler of top-level alias
            "generate-static"
            ["with-profile" "static"
             "shell" "echo"]
            ;; Run a local copy of SQS/SNS
            "start-sqs-sns"
            ["shell" "cmr" "start" "local" "sqs-sns"]
            "stop-sqs-sns"
            ["shell" "cmr" "stop" "local" "sqs-sns"]
            "restart-sqs-sns"
            ["do"
              ["stop-sqs-sns"]
              ["start-sqs-sns"]]})

(def projects
  "A map of the other development projects to their versions"
  {:cmr-access-control-app "0.1.0-SNAPSHOT"
   :cmr-acl-lib "0.1.0-SNAPSHOT"
   :cmr-bootstrap-app "0.1.0-SNAPSHOT"
   :cmr-collection-renderer-lib "0.1.0-SNAPSHOT"
   :cmr-common-app-lib "0.1.0-SNAPSHOT"
   :cmr-common-lib "0.1.1-SNAPSHOT"
   :cmr-cubby-app "0.1.0-SNAPSHOT"
   :cmr-elastic-utils-lib "0.1.0-SNAPSHOT"
   :cmr-es-spatial-plugin "0.1.0-SNAPSHOT"
   :cmr-index-set-app "0.1.0-SNAPSHOT"
   :cmr-indexer-app "0.1.0-SNAPSHOT"
   :cmr-ingest-app "0.1.0-SNAPSHOT"
   :cmr-message-queue-lib "0.1.0-SNAPSHOT"
   :cmr-metadata-db-app "0.1.0-SNAPSHOT"
   :cmr-mock-echo-app "0.1.0-SNAPSHOT"
   :cmr-oracle-lib "0.1.0-SNAPSHOT"
   :cmr-orbits-lib "0.1.0-SNAPSHOT"
   :cmr-schema-validation-lib "0.1.0-SNAPSHOT"
   :cmr-search-app "0.1.0-SNAPSHOT"
   :cmr-search-relevancy-test "0.1.0-SNAPSHOT"
   :cmr-spatial-lib "0.1.0-SNAPSHOT"
   :cmr-system-int-test "0.1.0-SNAPSHOT"
   :cmr-transmit-lib "0.1.0-SNAPSHOT"
   :cmr-umm-lib "0.1.0-SNAPSHOT"
   :cmr-umm-spec-lib "0.1.0-SNAPSHOT"
   :cmr-virtual-product-app "0.1.0-SNAPSHOT"})

(def project-dependencies
  "A list of other projects as maven dependencies"
  (doall (map (fn [[project-name version]]
                (let [maven-name (symbol "nasa-cmr" (name project-name))]
                  [maven-name version]))
              projects)))

(def create-checkouts-commands
  (vec
    (apply concat ["do"
                   "shell" "mkdir" "checkouts,"]
           (map (fn [project-name]
                  ["shell" "ln" "-s" (str "../../" (subs (name project-name) 4)) "checkouts/,"])
                (keys projects)))))

;; The version number here is for the sprint number. It will be incremented each sprint. The second
;; number is for which delivery of the version was given to ECHO for use.
(defproject nasa-cmr/cmr-dev-system "0.1.0-SNAPSHOT"
  :description "Dev System combines together the separate microservices of the CMR into a single
               application to make it simpler to develop."
  :url "https://github.com/nasa/Common-Metadata-Repository/tree/master/dev-system"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :exclusions [[commons-codec/commons-codec]
               [org.clojure/clojure]
               [ring/ring-codec]]
  :dependencies ~(concat '[[commons-codec/commons-codec "1.11"]
                           [org.clojure/clojure "1.10.0"]
                           ;; Add groovy to support groovy scripting in elastic
                           [org.codehaus.groovy/groovy-all "2.4.0"]
                           [ring/ring-codec "1.1.1"]]
                  project-dependencies)
  :plugins [[lein-environ "1.1.0"]
            [lein-shell "0.5.0"]
            [test2junit "1.3.3"]]
  :repl-options {:init-ns user
                 :timeout 300000
                 :welcome (do
                           (println (slurp "resources/text/banner.txt"))
                           (println (slurp "resources/text/loading.txt")))}
  :jvm-opts ["-XX:-OmitStackTraceInFastThrow"
             "-Dclojure.compiler.direct-linking=true"
             "-Dorg.jruby.embed.localcontext.scope=singlethread"]
             ;; Uncomment to enable logging in jetty.
             ; "-Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StrErrLog"
             ; "-Dorg.eclipse.jetty.LEVEL=INFO"
             ; "-Dorg.eclipse.jetty.websocket.LEVEL=INFO"]
  :profiles {:security {:plugins [[com.livingsocial/lein-dependency-check "1.1.1"]]
                        :dependency-check {:output-format [:all]
                                           :suppression-file "resources/security/suppression.xml"
                                           :properties-file "resources/security/dependencycheck.properties"}}
             :dev-dependencies {:exclusions [[org.clojure/tools.nrepl]]
                                :dependencies [[criterium "0.4.4"]
                                               [debugger "0.2.0"]
                                               [drift "1.5.3"]
                                               [org.clojars.gjahad/debug-repl "0.3.3"]
                                               [org.clojure/tools.namespace "0.2.11"]
                                               [org.clojure/tools.nrepl "0.2.13"]
                                               [pjstadig/humane-test-output "0.9.0"]
                                               [proto-repl "0.3.1"]
                                               [proto-repl-charts "0.3.2"]
                                               [proto-repl-sayid "0.1.3"]
                                               [ring-mock "0.1.5"]]
                                ;; XXX Note that profiling can be kept in a profile,
                                ;;     with no need to comment/uncomment.
                                ;; Use the following to enable JMX profiling with visualvm
                                ;:jvm-opts ^:replace ["-server"
                                ;                     "-Dcom.sun.management.jmxremote"
                                ;                     "-Dcom.sun.management.jmxremote.ssl=false"
                                ;                     "-Dcom.sun.management.jmxremote.authenticate=false"
                                ;                     "-Dcom.sun.management.jmxremote.port=1098"]
                                :source-paths ["src" "dev" "test"]
                                :injections [(require 'pjstadig.humane-test-output)
                                             (pjstadig.humane-test-output/activate!)]}
              ;; This is to separate the dependencies from the dev-config specified in profiles.clj
             :dev [:dev-dependencies :dev-config]
             ;; The following run-* profiles are used in conjunction with other lein
             ;; profiles to set the default CMR run mode and may be used in the
             ;; following manner:
             ;;
             ;;   $ lein with-profile +run-external repl
             ;;
             ;; which will use dev and the other default profiles in addition to
             ;; run-external (or whichever run mode profile is given).
             :run-in-memory {:jvm-opts ["-Dcmr.runmode=in-memory"]}
             :run-external {:jvm-opts ["-Dcmr.runmode=external"]}
             :uberjar {:main cmr.dev-system.runner
             ;; See http://stephen.genoprime.com/2013/11/14/uberjar-with-titan-dependency.html
                       :uberjar-merge-with {#"org\.apache\.lucene\.codecs\.*" [slurp str spit]}
                       :aot :all}
             :static {}
             ;; This profile is used for linting and static analysis. To run for this
             ;; project, use `lein lint` from inside the project directory. To run for
             ;; all projects at the same time, use the same command but from the top-
             ;; level directory.
             :lint {:source-paths ^:replace ["src"]
                    :test-paths ^:replace []
                    :plugins [[jonase/eastwood "0.2.5"]
                              [lein-ancient "0.6.15"]
                              [lein-bikeshed "0.5.0"]
                              [lein-kibit "0.1.6"]
                              [venantius/yagni "0.1.4"]]}
             ;; The following profile is overriden on the build server or in the user's
             ;; ~/.lein/profiles.clj file.
             :internal-repos {}}
  :aliases {
            ;; Creates the checkouts directory to the local projects
            "create-checkouts" ~create-checkouts-commands
            ;; Alias to test2junit for consistency with lein-test-out
            "test-out"
            ["test2junit"]
            ;; Installs the Elasticsearch Marvel plugin locally.
            ;; Visit http://localhost:9210/_plugin/marvel/sense/index.html
            "install-marvel"
            ["shell" "cmr" "install" "local" "marvel"]
            ;; Linting aliases
            "kibit"
            ["do"
              ["shell" "echo" "== Kibit =="]
              ["with-profile" "lint" "kibit"]]
            "eastwood"
            ["with-profile" "lint" "eastwood" "{:namespaces [:source-paths]}"]
            "bikeshed"
            ["with-profile" "lint" "bikeshed" "--max-line-length=100"]
            "yagni"
            ["with-profile" "lint" "yagni"]
            "check-deps"
            ["with-profile" "lint" "ancient" ":all"]
            "check-sec"
            ["with-profile" "security" "dependency-check"]
            "lint"
            ["do"
              ["check"] ["kibit"] ["eastwood"]]
            ;; Placeholder for future docs and enabler of top-level alias
            "generate-static"
            ["with-profile" "static"
             "shell" "echo"]
            ;; Run a local copy of SQS/SNS
            "start-sqs-sns"
            ["shell" "cmr" "start" "local" "sqs-sns"]
            "stop-sqs-sns"
            ["shell" "cmr" "stop" "local" "sqs-sns"]
            "restart-sqs-sns"
            ["do"
              ["stop-sqs-sns"]
              ["start-sqs-sns"]]})

Change localcontext to singlethread.

CMR-5677: Change localcontext to singlethread.