diff --git a/Makefile b/Makefile index c713b57..6ee21be 100644 --- a/Makefile +++ b/Makefile @@ -3,5 +3,4 @@ run: lein run dev server: - lein cljsbuild auto & redis-server & diff --git a/project.clj b/project.clj index 4425f98..0b07866 100644 --- a/project.clj +++ b/project.clj @@ -4,7 +4,7 @@ [hiccup "1.0.0"] [cheshire "5.3.1"] [ring/ring-core "1.1.0"] - [clj-redis "0.0.12"] + [com.taoensso/carmine "2.4.4"] [noir "1.3.0-beta1"]] :jvm-opts ["-Dfile.encoding=utf-8"] :main NoteHub.server) diff --git a/settings b/settings index f2b0c79..e55fd78 100644 --- a/settings +++ b/settings @@ -1,2 +1,3 @@ max-title-length = 40 domain = http://notehub.org +db-url = http://localhost:6379 diff --git a/src/NoteHub/api.clj b/src/NoteHub/api.clj index 1f729d2..9915c79 100644 --- a/src/NoteHub/api.clj +++ b/src/NoteHub/api.clj @@ -9,7 +9,7 @@ [ring.util.codec] [NoteHub.storage :as storage])) -(def version "1.0") +(def version "1.1") (def domain (get-setting :domain)) @@ -45,8 +45,7 @@ (if description (str domain "/" (storage/get-short-url noteID)) (let [[year month day title] (split noteID #" ")] - (apply str (interpose "/" - [domain year month day (ring.util.codec/url-encode title)]))))) + (apply str (interpose "/" [domain year month day (ring.util.codec/url-encode title)]))))) (let [md5Instance (java.security.MessageDigest/getInstance "MD5")] (defn get-signature diff --git a/src/NoteHub/storage.clj b/src/NoteHub/storage.clj index 65f2166..f0c7cbb 100644 --- a/src/NoteHub/storage.clj +++ b/src/NoteHub/storage.clj @@ -3,18 +3,18 @@ [clojure.string :only (blank?)] [noir.util.crypt :only [encrypt]] [noir.options :only [dev-mode?]]) - (:require [clj-redis.client :as redis])) + (:require [taoensso.carmine :as car :refer (wcar)])) -; Initialize the data base -(def db - (redis/init - (when-not (dev-mode?) - {:url (get-setting :db-url)}))) +(def conn {:pool {} :spec {:uri (get-setting :db-url)}}) +(defmacro redis [cmd & body] + `(car/wcar conn + (~(symbol "car" (name cmd)) + ~@body))) (defn get-current-date [] (str (java.util.Date.))) -; DB hierarchy levels +; hierarchy levels (def note "note") (def published "published") (def edited "edited") @@ -26,107 +26,90 @@ (def publisher-key "publisher-key") (defn valid-publisher? [pid] - (redis/hexists db publisher-key pid)) + (= 1 (redis :hexists publisher-key pid))) (defn register-publisher [pid] "Returns nil if given PID exists or a PSK otherwise" (when (not (valid-publisher? pid)) - (let [psk (encrypt (str (rand-int Integer/MAX_VALUE) pid)) - _ (redis/hset db publisher-key pid psk)] + (let [psk (encrypt (str (rand-int Integer/MAX_VALUE) pid))] + (redis :hset publisher-key pid psk) psk))) (defn revoke-publisher [pid] - (redis/hdel db publisher-key pid)) + (redis :hdel publisher-key pid)) (defn get-psk [pid] - (redis/hget db publisher-key pid)) + (redis :hget publisher-key pid)) (defn create-session [] (let [token (encrypt (str (rand-int Integer/MAX_VALUE)))] - (do (redis/sadd db sessions token) - token))) + (do (redis :sadd sessions token) + token))) (defn invalidate-session [token] - (let [was-valid (redis/sismember db sessions token)] - (redis/srem db sessions token) - was-valid)) + (let [was-valid (redis :sismember sessions token)] + (redis :srem sessions token) + (= 1 was-valid))) -(defn edit-note - [noteID text] - (do - (redis/hset db edited noteID (get-current-date)) - (redis/hset db note noteID text))) +(defn edit-note [noteID text] + (redis :hset edited noteID (get-current-date)) + (redis :hset note noteID text)) (defn add-note ([noteID text pid] (add-note noteID text pid nil)) ([noteID text pid passwd] - (do - (redis/hset db note noteID text) - (redis/hset db published noteID (get-current-date)) - (redis/hset db publisher noteID pid) - (when (not (blank? passwd)) - (redis/hset db password noteID passwd))))) + (redis :hset note noteID text) + (redis :hset published noteID (get-current-date)) + (redis :hset publisher noteID pid) + (when (not (blank? passwd)) + (redis :hset password noteID passwd)))) (defn valid-password? [noteID passwd] - (let [stored (redis/hget db password noteID)] - (and stored (= stored passwd)))) - -(defn get-note - [noteID] - (let [text (redis/hget db note noteID)] - (when text - (do - (redis/hincrby db views noteID 1) - text)))) - -(defn get-note-views - [noteID] - (redis/hget db views noteID)) - -(defn get-publisher - [noteID] - (redis/hget db publisher noteID)) - -(defn get-note-statistics - "Return views, publishing and editing timestamp" - [noteID] + (let [stored (redis :hget password noteID)] + (and (not (= 0 stored)) (= stored passwd)))) + +(defn get-note-views [noteID] + (redis :hget views noteID)) + +(defn get-publisher [noteID] + (redis :hget publisher noteID)) + +(defn get-note-statistics [noteID] {:views (get-note-views noteID) - :published (redis/hget db published noteID) - :edited (redis/hget db edited noteID) + :published (redis :hget published noteID) + :edited (redis :hget edited noteID) :publisher (get-publisher noteID)}) -(defn note-exists? - [noteID] - (redis/hexists db note noteID)) +(defn note-exists? [noteID] + (= 1 (redis :hexists note noteID))) + +(defn get-note [noteID] + (when (note-exists? noteID) + (do + (redis :hincrby views noteID 1) + (redis :hget note noteID)))) -(defn delete-note - [noteID] +(defn delete-note [noteID] (doseq [kw [password views note published edited publisher]] ; TODO: delete short url by looking for the title - (redis/hdel db kw noteID))) + (redis :hdel kw noteID))) -(defn short-url-exists? - "Checks whether the provided short url is taken (for testing only)" - [url] - (redis/hexists db short-url url)) +(defn short-url-exists? [url] + (= 1 (redis :hexists short-url url))) (defn get-short-url [noteID] - (redis/hget db short-url noteID)) + (redis :hget short-url noteID)) -(defn resolve-url - "Resolves short url by providing all metadata of the request" - [url] - (let [value (redis/hget db short-url url)] +(defn resolve-url [url] + (let [value (redis :hget short-url url)] (when value (read-string value)))) -(defn delete-short-url - "Deletes a short url (for testing only)" - [noteID] - (let [value (redis/hget db short-url noteID)] +(defn delete-short-url [noteID] + (let [value (redis :hget short-url noteID)] (do - (redis/hdel db short-url noteID) - (redis/hdel db short-url value)))) + (redis :hdel short-url noteID) + (redis :hdel short-url value)))) (defn create-short-url "Creates a short url for the given request metadata or noteID or extracts @@ -134,7 +117,7 @@ [arg] (let [key (if (map? arg) (str (into (sorted-map) arg)) arg)] (if (short-url-exists? key) - (redis/hget db short-url key) + (redis :hget short-url key) (let [hash-stream (partition 5 (repeatedly #(rand-int 36))) hash-to-string (fn [hash] (apply str @@ -142,11 +125,11 @@ ; and the rest to chars (map #(char (+ (if (< 9 %) 87 48) %)) hash))) url (first - (remove short-url-exists? - (map hash-to-string hash-stream)))] + (remove short-url-exists? + (map hash-to-string hash-stream)))] (do ; we create two mappings: key params -> short url and back, ; s.t. we can later easily check whether a short url already exists - (redis/hset db short-url url key) - (redis/hset db short-url key url) - url))))) + (redis :hset short-url url key) + (redis :hset short-url key url) + url))))) \ No newline at end of file diff --git a/test/NoteHub/test/api.clj b/test/NoteHub/test/api.clj index 10aab4a..c9932db 100644 --- a/test/NoteHub/test/api.clj +++ b/test/NoteHub/test/api.clj @@ -1,7 +1,7 @@ (ns NoteHub.test.api (:require - [cheshire.core :refer :all] - [NoteHub.storage :as storage]) + [cheshire.core :refer :all] + [NoteHub.storage :as storage]) (:use [NoteHub.api] [noir.util.test] [clojure.test])) @@ -37,9 +37,9 @@ (isnt (storage/valid-publisher? pid2)))) (testing "note publishing & retrieval" (isnt (:success (:status (get-note "some note id")))) + (is (= "note is empty" (:message (:status (post-note "" pid (get-signature pid psk "")))))) (let [post-response (post-note note pid (get-signature pid psk note)) get-response (get-note (:noteID post-response))] - (is (= "note is empty" (:message (:status (post-note "" pid (get-signature pid psk "")))))) (is (:success (:status post-response))) (is (:success (:status get-response))) (is (= note (:note get-response))) @@ -100,7 +100,7 @@ (is (has-status response 200)) (is (get-in body ["status" "success"])) (is (= note ((parse-string - (:body (send-request [:get "/api/note"] {:version "1.0" :noteID noteID}))) "note"))) + (:body (send-request [:get "/api/note"] {:version "1.0" :noteID noteID}))) "note"))) (is (do (storage/delete-note noteID) (not (storage/note-exists? noteID))))))) @@ -120,7 +120,7 @@ (is (storage/note-exists? noteID)) (is (substring? "_test_ note" ((parse-string - (:body (send-request [:get "/api/note"] {:version "1.0" :noteID noteID}))) "note"))) + (:body (send-request [:get "/api/note"] {:version "1.0" :noteID noteID}))) "note"))) (let [response (send-request [:put "/api/note"] {:noteID noteID :note "WRONG pass" @@ -135,21 +135,21 @@ (isnt (get-in body ["statistics" "edited"])) (is (substring? "_test_ note" ((parse-string - (:body (send-request [:get "/api/note"] {:version "1.0" :noteID noteID}))) "note")))) + (:body (send-request [:get "/api/note"] {:version "1.0" :noteID noteID}))) "note")))) (is (get-in (parse-string - (:body (send-request [:put "/api/note"] - {:noteID noteID - :note "UPDATED CONTENT" - :pid pid - :signature (get-signature pid psk noteID "UPDATED CONTENT" "qwerty") - :password "qwerty" - :version "1.0"}))) ["status" "success"])) + (:body (send-request [:put "/api/note"] + {:noteID noteID + :note "UPDATED CONTENT" + :pid pid + :signature (get-signature pid psk noteID "UPDATED CONTENT" "qwerty") + :password "qwerty" + :version "1.0"}))) ["status" "success"])) (isnt (= nil (((parse-string - (:body (send-request [:get "/api/note"] {:version "1.0" :noteID noteID}))) + (:body (send-request [:get "/api/note"] {:version "1.0" :noteID noteID}))) "statistics") "edited"))) (is (substring? "UPDATED CONTENT" ((parse-string - (:body (send-request [:get "/api/note"] {:version "1.0" :noteID noteID}))) "note"))) + (:body (send-request [:get "/api/note"] {:version "1.0" :noteID noteID}))) "note"))) (is (do (storage/delete-note noteID) (not (storage/note-exists? noteID)))))))