From 97529f489b9f0f08494e32ef473aaffb4fe9c587 Mon Sep 17 00:00:00 2001 From: Christian Mueller Date: Sat, 1 Feb 2014 21:02:34 +0100 Subject: [PATCH] version manager; new API signatures; new noteID format --- src/notehub/api.clj | 29 +++++++++--------- src/notehub/handler.clj | 55 +++++++++++++++++++---------------- test/notehub/test/api.clj | 52 ++++++++++++++++++--------------- test/notehub/test/handler.clj | 3 +- test/notehub/test/storage.clj | 2 +- 5 files changed, 74 insertions(+), 67 deletions(-) diff --git a/src/notehub/api.clj b/src/notehub/api.clj index 89e2e14..7ee38af 100644 --- a/src/notehub/api.clj +++ b/src/notehub/api.clj @@ -11,7 +11,7 @@ [hiccup.util :as util] [notehub.storage :as storage])) -(def version "1.1") +(def version "1.2") (def domain (get-setting @@ -33,8 +33,8 @@ ; Concatenates all fields to a string (defn build-key "Returns a storage-key for the given note coordinates" - [[year month day] title] - (print-str year month day title)) + [year month day title] + (apply str (interpose "/" [year month day title]))) (defn derive-title [md-text] (sreplace (first (split-lines md-text)) @@ -46,20 +46,20 @@ (map #(+ (second %) (.get (Calendar/getInstance) (first %))) {Calendar/YEAR 0, Calendar/MONTH 1, Calendar/DAY_OF_MONTH 0})) -(defn- create-response - ([success] { :success success }) +(defn create-response + ([success] {:success success}) ([success message & params] (assoc (create-response success) :message (apply format message params)))) (defn- get-path [token & [description]] (if (= :url description) (str domain "/" token) - (let [[year month day title] (split token #" ")] + (let [[year month day title] (split token #"/")] (if description (str domain "/" (storage/create-short-url token {:year year :month month :day day :title title})) (str domain (url year month day title)))))) -(defn get-note [noteID] +(defn get-note [{:keys [noteID]}] (if (storage/note-exists? noteID) (let [note (storage/get-note noteID)] {:note note @@ -72,8 +72,7 @@ (create-response false "noteID '%s' unknown" noteID))) (defn post-note - ([note pid signature] (post-note note pid signature {})) - ([note pid signature opts] + [{:keys [note pid signature password] :as params}] ;(log "post-note: %s" {:pid pid :signature signature :password password :note note}) (let [errors (filter identity [(when-not (storage/valid-publisher? pid) "pid invalid") @@ -82,17 +81,15 @@ (when (blank? note) "note is empty")])] (if (empty? errors) (let [[year month day] (map str (get-date)) - password (opts :password) - params (opts :params {}) + params (dissoc params :note :pid :signature :password) raw-title (filter #(or (= \- %) (Character/isLetterOrDigit %)) (-> note derive-title trim (sreplace " " "-") lower-case)) max-length (get-setting :max-title-length #(Integer/parseInt %) 80) proposed-title (apply str (take max-length raw-title)) - date [year month day] - title (first (drop-while #(storage/note-exists? (build-key date %)) + title (first (drop-while #(storage/note-exists? (build-key year month day %)) (cons proposed-title (map #(str proposed-title "-" (+ 2 %)) (range))))) - noteID (build-key date title) + noteID (build-key year month day title) new-params (assoc params :year year :month month :day day :title title) short-url (get-path (storage/create-short-url noteID new-params) :url) long-url (get-path noteID)] @@ -102,10 +99,10 @@ :longURL (if (empty? params) long-url (str (util/url long-url params))) :shortURL short-url :status (create-response true)})) - {:status (create-response false (first errors))})))) + {:status (create-response false (first errors))}))) -(defn update-note [noteID note pid signature password] +(defn update-note [{:keys [noteID note pid signature password]}] ;(log "update-note: %s" {:pid pid :noteID noteID :signature signature :password password :note note}) (let [errors (filter identity [(when-not (storage/valid-publisher? pid) "pid invalid") diff --git a/src/notehub/handler.clj b/src/notehub/handler.clj index 28aec34..16af70e 100644 --- a/src/notehub/handler.clj +++ b/src/notehub/handler.clj @@ -90,25 +90,27 @@ {:headers {"Content-Type" ctype} :body content}) +(defn version-manager [f params] + (generate-string + (if-let [version (:version params)] + (f (if (and (:noteID params) (< (Float/parseFloat version) 1.3)) + (assoc params :noteID (sreplace (params :noteID) #" " "/")) + params)) + (api/create-response false "API version expected")))) + (defroutes api-routes (GET "/" [] (layout (get-message :api-title) (md-node :article (slurp "API.md")))) - (GET "/note" [version noteID] - (generate-string (api/get-note noteID))) + (GET "/note" {params :params} + (version-manager api/get-note params)) (POST "/note" {params :params} - (generate-string - (api/post-note - (:note params) - (:pid params) - (:signature params) - {:params (dissoc params :version :note :pid :signature :password) - :password (:password params)}))) + (version-manager api/post-note params)) - (PUT "/note" [version noteID note pid signature password] - (generate-string (api/update-note noteID note pid signature password)))) + (PUT "/note" {params :params} + (version-manager api/update-note params))) (defroutes app-routes (context "/api" [] api-routes) @@ -127,11 +129,11 @@ (md-node :div.centered.helvetica (get-message :footer)))) (GET "/:year/:month/:day/:title/export" [year month day title] - (when-let [md-text (:note (api/get-note (api/build-key [year month day] title)))] + (when-let [md-text (:note (api/get-note {:noteID (api/build-key year month day title)}))] (return-content-type "text/plain; charset=utf-8" md-text))) (GET "/:year/:month/:day/:title/stats" [year month day title] - (when-let [stats (:statistics (api/get-note (api/build-key [year month day] title)))] + (when-let [stats (:statistics (api/get-note {:noteID (api/build-key year month day title)}))] (layout (get-message :statistics) [:table#stats.helvetica.central-element (map @@ -140,10 +142,10 @@ [:published :edited :publisher :views])]))) (GET "/:year/:month/:day/:title/edit" [year month day title] - (let [noteID (api/build-key [year month day] title)] + (let [noteID (api/build-key year month day title)] (input-form "/update-note" :update (html (hidden-field :noteID noteID)) - (:note (api/get-note noteID)) :enter-passwd))) + (:note (api/get-note {:noteID noteID})) :enter-passwd))) (GET "/new" [] (input-form "/post-note" :publish @@ -154,9 +156,9 @@ (GET "/:year/:month/:day/:title" [year month day title :as params] (let [params (assoc (:query-params params) :year year :month month :day day :title title) - noteID (api/build-key [year month day] title)] + noteID (api/build-key year month day title)] (when (storage/note-exists? noteID) - (let [note (api/get-note noteID) + (let [note (api/get-note {:noteID noteID}) sanitized-note (sanitize (:note note))] (layout (:title note) (md-node :article.bottom-space sanitized-note) @@ -180,25 +182,28 @@ (redirect long-url)))) - (POST "/post-note" [session note signature password version] + (POST "/post-note" [session note signature password] (if (= signature (storage/sign session note)) (let [pid "NoteHub" - psk (storage/get-psk pid)] + psk (storage/get-psk pid) + params {:session session :note note :signature signature + :password password :pid pid}] (if (storage/valid-publisher? pid) - (let [resp (api/post-note note pid (storage/sign pid psk note) {:password password})] + (let [resp (api/post-note (assoc params :signature (storage/sign pid psk note)))] (if (get-in resp [:status :success]) (redirect (:longURL resp)) (response 400))) (response 500))) (response 400))) - (POST "/update-note" [noteID note password version] + (POST "/update-note" [noteID note password] (let [pid "NoteHub" - psk (storage/get-psk pid)] + psk (storage/get-psk pid) + params {:noteID noteID :note note :password password :pid pid}] (if (storage/valid-publisher? pid) - (let [resp (api/update-note noteID note pid - (storage/sign pid psk noteID note password) - password)] + (let [resp (api/update-note (assoc params + :signature + (storage/sign pid psk noteID note password)))] (if (get-in resp [:status :success]) (redirect (:longURL resp)) (response 403))) diff --git a/test/notehub/test/api.clj b/test/notehub/test/api.clj index 4585829..2003769 100644 --- a/test/notehub/test/api.clj +++ b/test/notehub/test/api.clj @@ -9,7 +9,8 @@ (def note "hello world!\nThis is a _test_ note!") (def pid "somePlugin") (def pid2 "somePlugin2") -(def note-title (str (apply print-str (get-date)) " hello-world")) +(def note-title (let [[y m d] (get-date)] + (apply str (interpose "/" [y m d "hello-world"])))) (def note-url (str (apply str domain "/" (interpose "/" (get-date))) "/hello-world")) (defn substring? [a b] (not (= nil (re-matches (re-pattern (str "(?s).*" a ".*")) b)))) @@ -45,10 +46,10 @@ (isnt (storage/valid-publisher? "any_PID")) (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 (storage/sign pid psk "")))))) - (let [post-response (post-note note pid (storage/sign pid psk note)) - get-response (get-note (:noteID post-response))] + (isnt (:success (:status (get-note {:noteID "some note id"})))) + (is (= "note is empty" (:message (:status (post-note {:note "" :pid pid :signature (storage/sign pid psk "")}))))) + (let [post-response (post-note {:note note :pid pid :signature (storage/sign pid psk note)}) + get-response (get-note post-response)] (is (:success (:status post-response))) (is (:success (:status get-response))) (is (= note (:note get-response))) @@ -65,44 +66,47 @@ (is (= (:title get-response) (derive-title note))) (is (= "1" (get-in get-response [:statistics :views]))) (isnt (get-in get-response [:statistics :edited])) - (is (= "3" (get-in (get-note (:noteID post-response)) [:statistics :views]))))) + (is (= "3" (get-in (get-note post-response) [:statistics :views]))))) (testing "creation with wrong signature" - (let [response (post-note note pid (storage/sign pid2 psk note))] + (let [response (post-note {:note note :pid pid :signature (storage/sign pid2 psk note)})] (isnt (:success (:status response))) (is (= "signature invalid" (:message (:status response))))) - (let [response (post-note note pid (storage/sign pid2 psk "any note"))] + (let [response (post-note {:note note :pid pid :signature (storage/sign pid2 psk "any note")})] (isnt (:success (:status response))) (is (= "signature invalid" (:message (:status response))))) - (isnt (:success (:status (post-note note pid (storage/sign pid "random_psk" note))))) - (is (:success (:status (post-note note pid (storage/sign pid psk note))))) + (isnt (:success (:status (post-note {:note note :pid pid :signature (storage/sign pid "random_psk" note)})))) + (is (:success (:status (post-note {:note note :pid pid :signature (storage/sign pid psk note)})))) (let [randomPID "randomPID" psk2 (storage/register-publisher randomPID) _ (storage/revoke-publisher randomPID) - response (post-note note randomPID (storage/sign randomPID psk2 note))] + response (post-note {:note note :pid randomPID :signature (storage/sign randomPID psk2 note)})] (isnt (:success (:status response))) (is (= (:message (:status response)) "pid invalid")))) (testing "note update" - (let [post-response (post-note note pid (storage/sign pid psk note) {:password "passwd"}) + (let [post-response (post-note {:note note :pid pid :signature (storage/sign pid psk note) :password "passwd"}) note-id (:noteID post-response) new-note "a new note!"] (is (:success (:status post-response))) - (is (:success (:status (get-note note-id)))) - (is (= note (:note (get-note note-id)))) - (let [update-response (update-note note-id new-note pid (storage/sign pid psk new-note) "passwd")] + (is (:success (:status (get-note {:noteID note-id})))) + (is (= note (:note (get-note {:noteID note-id})))) + (let [update-response (update-note {:noteID note-id :note new-note :pid pid + :signature (storage/sign pid psk new-note) :password "passwd"})] (isnt (:success (:status update-response))) (is (= "signature invalid" (:message (:status update-response))))) - (is (= note (:note (get-note note-id)))) - (let [update-response (update-note note-id new-note pid - (storage/sign pid psk note-id new-note "passwd") "passwd")] + (is (= note (:note (get-note {:noteID note-id})))) + (let [update-response (update-note {:noteID note-id :note new-note :pid pid + :signature (storage/sign pid psk note-id new-note "passwd") + :password "passwd"})] (is (= { :success true } (:status update-response))) - (isnt (= nil (get-in (get-note note-id) [:statistics :edited]))) - (is (= new-note (:note (get-note note-id))))) - (let [update-response (update-note note-id "aaa" pid - (storage/sign pid psk note-id "aaa" "pass") "pass")] + (isnt (= nil (get-in (get-note {:noteID note-id}) [:statistics :edited]))) + (is (= new-note (:note (get-note {:noteID note-id}))))) + (let [update-response (update-note {:noteID note-id :note "aaa" :pid pid + :signature (storage/sign pid psk note-id "aaa" "pass") + :password "pass"})] (isnt (:success (:status update-response))) (is (= "password invalid" (:message (:status update-response))))) - (is (= new-note (:note (get-note note-id)))) - (is (= new-note (:note (get-note note-id)))))))) + (is (= new-note (:note (get-note {:noteID note-id})))) + (is (= new-note (:note (get-note {:noteID note-id})))))))) (deftest api-note-creation (testing "Note creation" diff --git a/test/notehub/test/handler.clj b/test/notehub/test/handler.clj index ad67af7..2542818 100644 --- a/test/notehub/test/handler.clj +++ b/test/notehub/test/handler.clj @@ -1,11 +1,12 @@ (ns notehub.test.handler (:use clojure.test - [notehub.api :only [build-key get-date url]] + [notehub.api :only [get-date url]] notehub.storage ring.mock.request notehub.test.api notehub.handler)) +(defn build-key [[y m d] t] (notehub.api/build-key y m d t)) (def date [2012 6 3]) (def test-title "some-title") (def test-note "# This is a test note.\nHello _world_. Motörhead, тест.") diff --git a/test/notehub/test/storage.clj b/test/notehub/test/storage.clj index 139c10d..8b0a0ad 100644 --- a/test/notehub/test/storage.clj +++ b/test/notehub/test/storage.clj @@ -1,9 +1,9 @@ (ns notehub.test.storage (:use [notehub.storage] - [notehub.api :only [build-key]] [clojure.test]) (:require [taoensso.carmine :as car :refer (wcar)])) +(defn build-key [[y m d] t] (notehub.api/build-key y m d t)) (def date [2012 06 03]) (def test-title "Some title.") (def test-note "This is a test note.")