Browse Source

version manager; new API signatures; new noteID format

master
Christian Mueller 12 years ago
parent
commit
97529f489b
  1. 27
      src/notehub/api.clj
  2. 55
      src/notehub/handler.clj
  3. 52
      test/notehub/test/api.clj
  4. 3
      test/notehub/test/handler.clj
  5. 2
      test/notehub/test/storage.clj

27
src/notehub/api.clj

@ -11,7 +11,7 @@ @@ -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 @@ @@ -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,7 +46,7 @@ @@ -46,7 +46,7 @@
(map #(+ (second %) (.get (Calendar/getInstance) (first %)))
{Calendar/YEAR 0, Calendar/MONTH 1, Calendar/DAY_OF_MONTH 0}))
(defn- create-response
(defn create-response
([success] {:success success})
([success message & params]
(assoc (create-response success) :message (apply format message params))))
@ -54,12 +54,12 @@ @@ -54,12 +54,12 @@
(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 @@ @@ -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 @@ @@ -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 @@ @@ -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")

55
src/notehub/handler.clj

@ -90,25 +90,27 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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)))

52
test/notehub/test/api.clj

@ -9,7 +9,8 @@ @@ -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 @@ @@ -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 @@ @@ -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"

3
test/notehub/test/handler.clj

@ -1,11 +1,12 @@ @@ -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, тест.")

2
test/notehub/test/storage.clj

@ -1,9 +1,9 @@ @@ -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.")

Loading…
Cancel
Save