merged with api branch
This commit is contained in:
@@ -8,3 +8,4 @@ pom.xml
|
|||||||
.lein-*
|
.lein-*
|
||||||
.crossover-cljs
|
.crossover-cljs
|
||||||
target/
|
target/
|
||||||
|
.nrepl-port
|
||||||
|
|||||||
@@ -1,20 +1,29 @@
|
|||||||
# NoteHub API
|
# NoteHub API
|
||||||
|
|
||||||
**Version 1.0, status: draft!**
|
**Version 1.0, status: released.**
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
The NoteHub API can only be used in combination with a __Publisher ID__ (PID) and __Publisher Secret Key__ (PSK), which can be issued [here](http://notehub.org/api/register). The PSK can be revoked at any moment in case of an API abuse.
|
The NoteHub API can only be used in combination with a __Publisher ID__ (PID) and __Publisher Secret Key__ (PSK), which can be requested [here](#registration). The PSK can be revoked at any moment in case of an API abuse.
|
||||||
|
|
||||||
A PID is a string chosen by the publisher and cannot be longer than 16 characters (e.g.: __notepadPlugin__). A PSK will be generated by the NoteHub API and can be a string of any length and content.
|
A PID is a string chosen by the publisher and cannot be longer than 16 characters (e.g.: __notepadPlugin__). A PSK will be generated by the NoteHub API and can be a string of any length and content.
|
||||||
|
|
||||||
All API requests must be issued with one special parameter `version` denoting the expected version of the API as a string, e.g. `1.0` (see examples below). You should always put the version of this document as a `version` parameter.
|
All API requests must be issued with one special parameter `version` denoting the expected version of the API as a string, e.g. `1.0` (see examples below). You should always put the version of this document as a `version` parameter.
|
||||||
|
|
||||||
|
## <a name="registration"></a>NoteHub API Access Request
|
||||||
|
To register as a publisher and gain access to NoteHub API, please [send](mailto:notehub@icloud.com?subject=NoteHub API Access Request&body=Please add [a] your contact information, [b] short usage explanation and [c] the URL of the resource or it's website.) an email with the following information about you: contact information, short description of what you want to do and an URL of the resource where the API will be used or its website.
|
||||||
## Note Retrieval
|
## Note Retrieval
|
||||||
|
|
||||||
A simple `GET` request to the following URL:
|
A simple `GET` request to the following URL:
|
||||||
|
|
||||||
http://notehub.org/api/note?version=1.0&title=<NOTE-ID>
|
http://notehub.org/api/note
|
||||||
|
|
||||||
|
with the following parameters:
|
||||||
|
|
||||||
|
Parameter | Explanation | Type
|
||||||
|
--- | --- | ---
|
||||||
|
`noteID` | Note-ID | **required**
|
||||||
|
`version` | Used API version | **required**
|
||||||
|
|
||||||
will return a JSON object containing following self explaining fields: `note`, `longURL`, `shortURL`, `statistics`, `status`.
|
will return a JSON object containing following self explaining fields: `note`, `longURL`, `shortURL`, `statistics`, `status`.
|
||||||
|
|
||||||
@@ -26,6 +35,7 @@ Example:
|
|||||||
shortURL: "http://notehub.org/0vrcp",
|
shortURL: "http://notehub.org/0vrcp",
|
||||||
statistics: {
|
statistics: {
|
||||||
published: "2014-1-3",
|
published: "2014-1-3",
|
||||||
|
edited: "2014-1-12",
|
||||||
views: 24
|
views: 24
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
@@ -36,7 +46,7 @@ Example:
|
|||||||
|
|
||||||
Hence, the status of the request can be evaluated by reading of the property `status.success`. The field `status.comment`might contain an error message, a warning or any other comments from the server.
|
Hence, the status of the request can be evaluated by reading of the property `status.success`. The field `status.comment`might contain an error message, a warning or any other comments from the server.
|
||||||
|
|
||||||
The note ID is a string, containing the date of publishing and a few first words of the note (usually the header), e.g.: `2014/1/3/lorem-ipsum`. This ID will be generated by NoteHub automatically.
|
The note ID is a string, containing the date of publishing and a few first words of the note (usually the title), e.g.: `"2014 1 3 lorem-ipsum"`. This ID will be generated by NoteHub automatically.
|
||||||
|
|
||||||
## Note Creation
|
## Note Creation
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
## News
|
## News
|
||||||
|
|
||||||
|
- January 2014: NoteHub API v1.0 [introduced](/api).
|
||||||
- January 2014: NoteHub 2.0 released: new theme, more performance, extended markdown
|
- January 2014: NoteHub 2.0 released: new theme, more performance, extended markdown
|
||||||
- September 2013: Solarized color theme [added](https://github.com/chmllr/NoteHub/pull/4) (thanks Brandon!) ([Demo](http://notehub.org/2012/6/16/how-notehub-is-built?theme=solarized))
|
- September 2013: Solarized color theme [added](https://github.com/chmllr/NoteHub/pull/4) (thanks Brandon!) ([Demo](http://notehub.org/2012/6/16/how-notehub-is-built?theme=solarized))
|
||||||
|
|
||||||
@@ -9,6 +10,7 @@
|
|||||||
Send your feedback and comments directly to [@gravitydenier](http://twitter.com/gravitydenier) or open an [issue](https://github.com/chmllr/NoteHub/issues) on GitHub.
|
Send your feedback and comments directly to [@gravitydenier](http://twitter.com/gravitydenier) or open an [issue](https://github.com/chmllr/NoteHub/issues) on GitHub.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
- **API**: Integrate the publishing functionality into your editor using the official [NoteHub API](/api).
|
||||||
- **Color Themes**: specify the color scheme by appending the corresponding parameter to the URL:
|
- **Color Themes**: specify the color scheme by appending the corresponding parameter to the URL:
|
||||||
- [Dark](http://notehub.org/2012/6/16/how-notehub-is-built?theme=dark)
|
- [Dark](http://notehub.org/2012/6/16/how-notehub-is-built?theme=dark)
|
||||||
- [Solarized-Dark](http://notehub.org/2012/6/16/how-notehub-is-built?theme=solarized-dark)
|
- [Solarized-Dark](http://notehub.org/2012/6/16/how-notehub-is-built?theme=solarized-dark)
|
||||||
|
|||||||
@@ -16,9 +16,11 @@ enter-passwd = Password
|
|||||||
publish = Publish
|
publish = Publish
|
||||||
update = Save
|
update = Save
|
||||||
published = Published
|
published = Published
|
||||||
|
edited = Edited
|
||||||
article-views = Article Views
|
article-views = Article Views
|
||||||
statistics = Statistics
|
statistics = Statistics
|
||||||
stats = statistics
|
stats = statistics
|
||||||
export = export
|
export = export
|
||||||
edit = edit
|
edit = edit
|
||||||
short-url = short url
|
short-url = short url
|
||||||
|
api-registration = NoteHub API Access Request
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
:description "A free and anonymous hosting for markdown pages."
|
:description "A free and anonymous hosting for markdown pages."
|
||||||
:dependencies [[org.clojure/clojure "1.5.1"]
|
:dependencies [[org.clojure/clojure "1.5.1"]
|
||||||
[hiccup "1.0.0"]
|
[hiccup "1.0.0"]
|
||||||
|
[cheshire "5.3.1"]
|
||||||
[ring/ring-core "1.1.0"]
|
[ring/ring-core "1.1.0"]
|
||||||
[clj-redis "0.0.12"]
|
[clj-redis "0.0.12"]
|
||||||
[noir "1.3.0-beta1"]]
|
[noir "1.3.0-beta1"]]
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ var timerDelay = iosDetected ? 800 : 400;
|
|||||||
var show = function(elem) { elem.style.display = "block" }
|
var show = function(elem) { elem.style.display = "block" }
|
||||||
var $draft, $action, $preview, $password, $plain_password, $input_elems, $dashed_line, updatePreview;
|
var $draft, $action, $preview, $password, $plain_password, $input_elems, $dashed_line, updatePreview;
|
||||||
|
|
||||||
|
function md2html(input){
|
||||||
|
return marked(input);
|
||||||
|
}
|
||||||
|
|
||||||
function loadPage() {
|
function loadPage() {
|
||||||
$draft = $("draft");
|
$draft = $("draft");
|
||||||
$action = $("action");
|
$action = $("action");
|
||||||
@@ -31,7 +35,7 @@ function loadPage() {
|
|||||||
timer = setTimeout(function(){
|
timer = setTimeout(function(){
|
||||||
show($dashed_line);
|
show($dashed_line);
|
||||||
show($input_elems);
|
show($input_elems);
|
||||||
$preview.innerHTML = marked(content);
|
$preview.innerHTML = md2html(content);
|
||||||
}, delay);
|
}, delay);
|
||||||
};
|
};
|
||||||
if($action){
|
if($action){
|
||||||
@@ -47,7 +51,7 @@ function loadPage() {
|
|||||||
|
|
||||||
var mdDocs = document.getElementsByClassName("markdown");
|
var mdDocs = document.getElementsByClassName("markdown");
|
||||||
for(var i = 0; i < mdDocs.length; i++){
|
for(var i = 0; i < mdDocs.length; i++){
|
||||||
mdDocs[i].innerHTML = marked(mdDocs[i].innerHTML);
|
mdDocs[i].innerHTML = md2html(mdDocs[i].innerHTML);
|
||||||
show(mdDocs[i]);
|
show(mdDocs[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
(ns NoteHub.api
|
||||||
|
(:import
|
||||||
|
[java.util Calendar])
|
||||||
|
(:use
|
||||||
|
[NoteHub.settings]
|
||||||
|
[clojure.string :rename {replace sreplace}
|
||||||
|
:only [replace blank? lower-case split-lines]])
|
||||||
|
(:require [NoteHub.storage :as storage]))
|
||||||
|
|
||||||
|
(def api-version "1.0")
|
||||||
|
|
||||||
|
(def domain "http://notehub.org/")
|
||||||
|
|
||||||
|
; 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))
|
||||||
|
|
||||||
|
(defn get-date
|
||||||
|
"Returns today's date"
|
||||||
|
[]
|
||||||
|
(map #(+ (second %) (.get (Calendar/getInstance) (first %)))
|
||||||
|
{Calendar/YEAR 0, Calendar/MONTH 1, Calendar/DAY_OF_MONTH 0}))
|
||||||
|
|
||||||
|
(defn- create-response
|
||||||
|
([success] { :success success })
|
||||||
|
([success message & params]
|
||||||
|
(assoc (create-response success) :message (apply format message params))))
|
||||||
|
|
||||||
|
(defn- getURL [noteID & description]
|
||||||
|
(if description
|
||||||
|
(str domain (storage/get-short-url noteID))
|
||||||
|
(str domain (sreplace noteID #" " "/"))))
|
||||||
|
|
||||||
|
(let [md5Instance (java.security.MessageDigest/getInstance "MD5")]
|
||||||
|
(defn get-signature
|
||||||
|
"Returns the MD5 hash for the concatenation of all passed parameters"
|
||||||
|
[& args]
|
||||||
|
(let [input (apply str args)]
|
||||||
|
(do (.reset md5Instance)
|
||||||
|
(.update md5Instance (.getBytes input))
|
||||||
|
(.toString (new java.math.BigInteger 1 (.digest md5Instance)) 16)))))
|
||||||
|
|
||||||
|
(defn get-note [noteID]
|
||||||
|
{:note (storage/get-note noteID)
|
||||||
|
:longURL (getURL noteID)
|
||||||
|
:shortURL (getURL noteID :short)
|
||||||
|
:statistics (storage/get-note-statistics noteID)
|
||||||
|
:status (if (storage/note-exists? noteID)
|
||||||
|
(create-response true)
|
||||||
|
(create-response false "noteID '%s' unknown" noteID))})
|
||||||
|
|
||||||
|
(defn post-note
|
||||||
|
([note pid signature] (post-note note pid signature nil))
|
||||||
|
([note pid signature password]
|
||||||
|
(let [errors (filter identity
|
||||||
|
[(when-not (storage/valid-publisher? pid) "pid invalid")
|
||||||
|
(when-not (= signature
|
||||||
|
(get-signature pid (storage/get-psk pid) note))
|
||||||
|
"signature invalid")
|
||||||
|
(when (blank? note) "note is empty")])]
|
||||||
|
(if (empty? errors)
|
||||||
|
(let [[year month day] (get-date)
|
||||||
|
untrimmed-line (filter #(or (= \- %) (Character/isLetterOrDigit %))
|
||||||
|
(-> note split-lines first (sreplace " " "-") lower-case))
|
||||||
|
trim (fn [s] (apply str (drop-while #(= \- %) s)))
|
||||||
|
title-uncut (-> untrimmed-line trim reverse trim reverse)
|
||||||
|
max-length (get-setting :max-title-length #(Integer/parseInt %) 80)
|
||||||
|
proposed-title (apply str (take max-length title-uncut))
|
||||||
|
date [year month day]
|
||||||
|
title (first (drop-while #(storage/note-exists? (build-key date %))
|
||||||
|
(cons proposed-title
|
||||||
|
(map #(str proposed-title "-" (+ 2 %)) (range)))))
|
||||||
|
noteID (build-key date title)]
|
||||||
|
(do
|
||||||
|
(storage/add-note noteID note password)
|
||||||
|
(storage/create-short-url noteID)
|
||||||
|
{
|
||||||
|
:noteID noteID
|
||||||
|
:longURL (getURL noteID)
|
||||||
|
:shortURL (getURL noteID :short)
|
||||||
|
:status (create-response true)
|
||||||
|
}))
|
||||||
|
{:status (create-response false (first errors))}))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn update-note [noteID note pid signature password]
|
||||||
|
(let [errors (filter identity
|
||||||
|
(seq
|
||||||
|
[(when-not (storage/valid-publisher? pid) "pid invalid")
|
||||||
|
(when-not (= signature
|
||||||
|
(get-signature pid (storage/get-psk pid) noteID note password))
|
||||||
|
"signature invalid")
|
||||||
|
(when (blank? note) "note is empty")
|
||||||
|
(when-not (storage/valid-password? noteID password) "password invalid")]))]
|
||||||
|
(if (empty? errors)
|
||||||
|
(do
|
||||||
|
(storage/edit-note noteID note)
|
||||||
|
{
|
||||||
|
:longURL (getURL noteID)
|
||||||
|
:shortURL (getURL noteID :short)
|
||||||
|
:status (create-response true)
|
||||||
|
})
|
||||||
|
{:status (create-response false (first errors))})))
|
||||||
+80
-50
@@ -11,83 +11,113 @@
|
|||||||
(when-not (dev-mode?)
|
(when-not (dev-mode?)
|
||||||
{:url (get-setting :db-url)})))
|
{:url (get-setting :db-url)})))
|
||||||
|
|
||||||
|
(defn get-current-date []
|
||||||
|
(str (java.util.Date.)))
|
||||||
|
|
||||||
; DB hierarchy levels
|
; DB hierarchy levels
|
||||||
(def note "note")
|
(def note "note")
|
||||||
|
(def published "published")
|
||||||
|
(def edited "edited")
|
||||||
(def views "views")
|
(def views "views")
|
||||||
(def password "password")
|
(def password "password")
|
||||||
(def sessions "sessions")
|
(def sessions "sessions")
|
||||||
(def short-url "short-url")
|
(def short-url "short-url")
|
||||||
|
(def publisher "publisher")
|
||||||
|
|
||||||
; Concatenates all fields to a string
|
(defn valid-publisher? [pid]
|
||||||
(defn build-key
|
(redis/hexists db publisher pid))
|
||||||
"Returns a storage-key for the given note coordinates"
|
|
||||||
[[year month day] title]
|
(defn register-publisher [pid]
|
||||||
(print-str year month day title))
|
"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 pid psk)]
|
||||||
|
psk)))
|
||||||
|
|
||||||
|
(defn revoke-publisher [pid]
|
||||||
|
(redis/hdel db publisher pid))
|
||||||
|
|
||||||
|
(defn get-psk [pid]
|
||||||
|
(redis/hget db publisher pid))
|
||||||
|
|
||||||
(defn create-session
|
(defn create-session
|
||||||
"Creates a random session token"
|
|
||||||
[]
|
[]
|
||||||
(let [token (encrypt (str (rand-int Integer/MAX_VALUE)))]
|
(let [token (encrypt (str (rand-int Integer/MAX_VALUE)))]
|
||||||
(do (redis/sadd db sessions token) token)))
|
(do (redis/sadd db sessions token)
|
||||||
|
token)))
|
||||||
|
|
||||||
(defn invalidate-session
|
(defn invalidate-session
|
||||||
"Invalidates given session"
|
|
||||||
[token]
|
[token]
|
||||||
; Jedis is buggy & returns an NPE for token == nil
|
; Jedis is buggy & returns an NPE for token == nil
|
||||||
(when token
|
(when token
|
||||||
(let [was-valid (redis/sismember db sessions token)]
|
(let [was-valid (redis/sismember db sessions token)]
|
||||||
(do (redis/srem db sessions token) was-valid))))
|
(do (redis/srem db sessions token)
|
||||||
|
was-valid))))
|
||||||
|
|
||||||
|
; TODO: deprecated
|
||||||
(defn update-note
|
(defn update-note
|
||||||
"Updates a note with the given store key if the specified password is correct"
|
[noteID text passwd]
|
||||||
[key text passwd]
|
(let [stored-password (redis/hget db password noteID)]
|
||||||
(let [stored-password (redis/hget db password key)]
|
|
||||||
(when (and stored-password (= passwd stored-password))
|
(when (and stored-password (= passwd stored-password))
|
||||||
(redis/hset db note key text))))
|
(redis/hset db edited noteID (get-current-date))
|
||||||
|
(redis/hset db note noteID text))))
|
||||||
|
|
||||||
(defn set-note
|
(defn edit-note
|
||||||
"Creates a note with the given title and text in the given date namespace"
|
[noteID text]
|
||||||
([date title text] (set-note date title text nil))
|
(do
|
||||||
([date title text passwd]
|
(redis/hset db edited noteID (get-current-date))
|
||||||
(let [key (build-key date title)]
|
(redis/hset db note noteID text)))
|
||||||
(do
|
|
||||||
(redis/hset db note key text)
|
(defn add-note
|
||||||
(when (not (blank? passwd))
|
([noteID text] (add-note noteID text nil))
|
||||||
(redis/hset db password key passwd))))))
|
([noteID text passwd]
|
||||||
|
(do
|
||||||
|
(redis/hset db note noteID text)
|
||||||
|
(redis/hset db published noteID (get-current-date))
|
||||||
|
(when (not (blank? passwd))
|
||||||
|
(redis/hset db password noteID passwd)))))
|
||||||
|
|
||||||
|
(defn valid-password? [noteID passwd]
|
||||||
|
(let [stored (redis/hget db password noteID)]
|
||||||
|
(and stored (= stored passwd))))
|
||||||
|
|
||||||
(defn get-note
|
(defn get-note
|
||||||
"Gets the note from the given date namespaces for the specified title"
|
[noteID]
|
||||||
[date title]
|
(let [text (redis/hget db note noteID)]
|
||||||
(let [key (build-key date title)
|
|
||||||
text (redis/hget db note key)]
|
|
||||||
(when text
|
(when text
|
||||||
(do
|
(do
|
||||||
(redis/hincrby db views key 1)
|
(redis/hincrby db views noteID 1)
|
||||||
text))))
|
text))))
|
||||||
|
|
||||||
(defn get-note-views
|
(defn get-note-views
|
||||||
"Returns the number of views for the specified date and note title"
|
[noteID]
|
||||||
[date title]
|
(redis/hget db views noteID))
|
||||||
(redis/hget db views (build-key date title)))
|
|
||||||
|
(defn get-note-statistics
|
||||||
|
"Return views, publishing and editing timestamp"
|
||||||
|
[noteID]
|
||||||
|
{ :views (redis/hget db views noteID)
|
||||||
|
:published (redis/hget db published noteID)
|
||||||
|
:edited (redis/hget db edited noteID) })
|
||||||
|
|
||||||
(defn note-exists?
|
(defn note-exists?
|
||||||
"Returns true if the note with the specified title and date exists"
|
[noteID]
|
||||||
[date title]
|
(redis/hexists db note noteID))
|
||||||
(redis/hexists db note (build-key date title)))
|
|
||||||
|
|
||||||
(defn delete-note
|
(defn delete-note
|
||||||
"Deletes the note with the specified coordinates"
|
[noteID]
|
||||||
[date title]
|
(doseq [kw [password views note published edited]]
|
||||||
(let [key (build-key date title)]
|
; TODO: delete short url by looking for the title
|
||||||
(doseq [kw [password views note]]
|
(redis/hdel db kw noteID)))
|
||||||
; TODO: delete short url by looking for the title
|
|
||||||
(redis/hdel db kw key))))
|
|
||||||
|
|
||||||
(defn short-url-exists?
|
(defn short-url-exists?
|
||||||
"Checks whether the provided short url is taken (for testing only)"
|
"Checks whether the provided short url is taken (for testing only)"
|
||||||
[url]
|
[url]
|
||||||
(redis/hexists db short-url url))
|
(redis/hexists db short-url url))
|
||||||
|
|
||||||
|
(defn get-short-url [noteID]
|
||||||
|
(redis/hget db short-url noteID))
|
||||||
|
|
||||||
(defn resolve-url
|
(defn resolve-url
|
||||||
"Resolves short url by providing all metadata of the request"
|
"Resolves short url by providing all metadata of the request"
|
||||||
[url]
|
[url]
|
||||||
@@ -97,19 +127,19 @@
|
|||||||
|
|
||||||
(defn delete-short-url
|
(defn delete-short-url
|
||||||
"Deletes a short url (for testing only)"
|
"Deletes a short url (for testing only)"
|
||||||
[key]
|
[noteID]
|
||||||
(let [value (redis/hget db short-url key)]
|
(let [value (redis/hget db short-url noteID)]
|
||||||
(do
|
(do
|
||||||
(redis/hdel db short-url key)
|
(redis/hdel db short-url noteID)
|
||||||
(redis/hdel db short-url value))))
|
(redis/hdel db short-url value))))
|
||||||
|
|
||||||
(defn create-short-url
|
(defn create-short-url
|
||||||
"Creates a short url for the given request metadata or extracts
|
"Creates a short url for the given request metadata or noteID or extracts
|
||||||
one if it was already created"
|
one if it was already created"
|
||||||
[metadata]
|
[arg]
|
||||||
(let [request (str (into (sorted-map) metadata))]
|
(let [key (if (map? arg) (str (into (sorted-map) arg)) arg)]
|
||||||
(if (short-url-exists? request)
|
(if (short-url-exists? key)
|
||||||
(redis/hget db short-url request)
|
(redis/hget db short-url key)
|
||||||
(let [hash-stream (partition 5 (repeatedly #(rand-int 36)))
|
(let [hash-stream (partition 5 (repeatedly #(rand-int 36)))
|
||||||
hash-to-string (fn [hash]
|
hash-to-string (fn [hash]
|
||||||
(apply str
|
(apply str
|
||||||
@@ -120,8 +150,8 @@
|
|||||||
(remove short-url-exists?
|
(remove short-url-exists?
|
||||||
(map hash-to-string hash-stream)))]
|
(map hash-to-string hash-stream)))]
|
||||||
(do
|
(do
|
||||||
; we create two mappings: request params -> short url and back,
|
; we create two mappings: key params -> short url and back,
|
||||||
; s.t. we can later easily check whether a short url already exists
|
; s.t. we can later easily check whether a short url already exists
|
||||||
(redis/hset db short-url url request)
|
(redis/hset db short-url url key)
|
||||||
(redis/hset db short-url request url)
|
(redis/hset db short-url key url)
|
||||||
url)))))
|
url)))))
|
||||||
|
|||||||
+33
-20
@@ -1,7 +1,9 @@
|
|||||||
(ns NoteHub.views.pages
|
(ns NoteHub.views.pages
|
||||||
(:require [hiccup.util :as util])
|
(:require [hiccup.util :as util]
|
||||||
|
[NoteHub.api :as api]
|
||||||
|
[cheshire.core :refer :all])
|
||||||
(:use
|
(:use
|
||||||
[NoteHub.storage]
|
[NoteHub.storage] ; TODO: delete this
|
||||||
[NoteHub.settings]
|
[NoteHub.settings]
|
||||||
[NoteHub.views.common]
|
[NoteHub.views.common]
|
||||||
[clojure.string :rename {replace sreplace}
|
[clojure.string :rename {replace sreplace}
|
||||||
@@ -12,9 +14,7 @@
|
|||||||
[hiccup.element]
|
[hiccup.element]
|
||||||
[noir.response :only [redirect status content-type]]
|
[noir.response :only [redirect status content-type]]
|
||||||
[noir.core :only [defpage defpartial]]
|
[noir.core :only [defpage defpartial]]
|
||||||
[noir.statuses])
|
[noir.statuses]))
|
||||||
(:import
|
|
||||||
[java.util Calendar]))
|
|
||||||
|
|
||||||
(defn get-hash
|
(defn get-hash
|
||||||
"A simple hash-function, which computes a hash from the text field
|
"A simple hash-function, which computes a hash from the text field
|
||||||
@@ -60,12 +60,6 @@
|
|||||||
links (interpose separator links)]
|
links (interpose separator links)]
|
||||||
[:div#panel (map identity links)]))))
|
[:div#panel (map identity links)]))))
|
||||||
|
|
||||||
(defn get-date
|
|
||||||
"Returns today's date"
|
|
||||||
[]
|
|
||||||
(map #(+ (second %) (.get (Calendar/getInstance) (first %)))
|
|
||||||
{Calendar/YEAR 0, Calendar/MONTH 1, Calendar/DAY_OF_MONTH 0}))
|
|
||||||
|
|
||||||
; Routes
|
; Routes
|
||||||
; ======
|
; ======
|
||||||
|
|
||||||
@@ -102,9 +96,10 @@
|
|||||||
|
|
||||||
; Update Note Page
|
; Update Note Page
|
||||||
(defpage "/:year/:month/:day/:title/edit" {:keys [year month day title]}
|
(defpage "/:year/:month/:day/:title/edit" {:keys [year month day title]}
|
||||||
(input-form "/update-note" :update
|
(let [noteID (api/build-key [year month day] title)]
|
||||||
(html (hidden-field :key (build-key [year month day] title)))
|
(input-form "/update-note" :update
|
||||||
(get-note [year month day] title) :enter-passwd))
|
(html (hidden-field :key noteID))
|
||||||
|
(get-note noteID) :enter-passwd)))
|
||||||
|
|
||||||
; New Note Page
|
; New Note Page
|
||||||
(defpage "/new" {}
|
(defpage "/new" {}
|
||||||
@@ -118,16 +113,16 @@
|
|||||||
(wrap
|
(wrap
|
||||||
(create-short-url params)
|
(create-short-url params)
|
||||||
(select-keys params [:title :theme :header-font :text-font])
|
(select-keys params [:title :theme :header-font :text-font])
|
||||||
(get-note [year month day] title)))
|
(get-note (api/build-key [year month day] title))))
|
||||||
|
|
||||||
; Provides Markdown of the specified note
|
; Provides Markdown of the specified note
|
||||||
(defpage "/:year/:month/:day/:title/export" {:keys [year month day title]}
|
(defpage "/:year/:month/:day/:title/export" {:keys [year month day title]}
|
||||||
(when-let [md-text (get-note [year month day] title)]
|
(when-let [md-text (get-note (api/build-key [year month day] title))]
|
||||||
(content-type "text/plain; charset=utf-8" md-text)))
|
(content-type "text/plain; charset=utf-8" md-text)))
|
||||||
|
|
||||||
; Provides the number of views of the specified note
|
; Provides the number of views of the specified note
|
||||||
(defpage "/:year/:month/:day/:title/stats" {:keys [year month day title]}
|
(defpage "/:year/:month/:day/:title/stats" {:keys [year month day title]}
|
||||||
(when-let [views (get-note-views [year month day] title)]
|
(when-let [views (get-note-views (api/build-key [year month day] title))]
|
||||||
(layout (get-message :statistics)
|
(layout (get-message :statistics)
|
||||||
[:table#stats.helvetica.central-element
|
[:table#stats.helvetica.central-element
|
||||||
[:tr
|
[:tr
|
||||||
@@ -158,7 +153,7 @@
|
|||||||
; if yes, we compute the current date, extract a title string from the text,
|
; if yes, we compute the current date, extract a title string from the text,
|
||||||
; which will be a part of the url and look whether this title is free today;
|
; which will be a part of the url and look whether this title is free today;
|
||||||
; if not, append "-n", where "n" is the next free number
|
; if not, append "-n", where "n" is the next free number
|
||||||
(let [[year month day] (get-date)
|
(let [[year month day] (api/get-date)
|
||||||
untrimmed-line (filter #(or (= \- %) (Character/isLetterOrDigit %))
|
untrimmed-line (filter #(or (= \- %) (Character/isLetterOrDigit %))
|
||||||
(-> draft split-lines first (sreplace " " "-") lower-case))
|
(-> draft split-lines first (sreplace " " "-") lower-case))
|
||||||
trim (fn [s] (apply str (drop-while #(= \- %) s)))
|
trim (fn [s] (apply str (drop-while #(= \- %) s)))
|
||||||
@@ -167,11 +162,11 @@
|
|||||||
; TODO: replace to ccs/take when it gets fixed
|
; TODO: replace to ccs/take when it gets fixed
|
||||||
proposed-title (apply str (take max-length title-uncut))
|
proposed-title (apply str (take max-length title-uncut))
|
||||||
date [year month day]
|
date [year month day]
|
||||||
title (first (drop-while #(note-exists? date %)
|
title (first (drop-while #(note-exists? (api/build-key date %))
|
||||||
(cons proposed-title
|
(cons proposed-title
|
||||||
(map #(str proposed-title "-" (+ 2 %)) (range)))))]
|
(map #(str proposed-title "-" (+ 2 %)) (range)))))]
|
||||||
(do
|
(do
|
||||||
(set-note date title draft password)
|
(add-note (api/build-key date title) draft password)
|
||||||
(redirect (url year month day title))))
|
(redirect (url year month day title))))
|
||||||
(response 400))))
|
(response 400))))
|
||||||
|
|
||||||
@@ -184,4 +179,22 @@
|
|||||||
long-url (if (empty? rest-params) core-url (util/url core-url rest-params))]
|
long-url (if (empty? rest-params) core-url (util/url core-url rest-params))]
|
||||||
(redirect long-url))))
|
(redirect long-url))))
|
||||||
|
|
||||||
|
; Here lives the API
|
||||||
|
|
||||||
|
(defpage "/api" args
|
||||||
|
(let [title (get-message :api-registration)]
|
||||||
|
(layout title [:article.markdown (slurp "API.md")])))
|
||||||
|
|
||||||
|
(defpage [:get "/api/note"] {:keys [version noteID]}
|
||||||
|
(generate-string (api/get-note noteID)))
|
||||||
|
|
||||||
|
(defpage [:post "/api/note"] {:keys [version note pid signature password]}
|
||||||
|
(generate-string (api/post-note note pid signature password)))
|
||||||
|
|
||||||
|
(defpage [:put "/api/note"] {:keys [version noteID note pid signature password]}
|
||||||
|
(generate-string (api/update-note noteID note pid signature password)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
(ns NoteHub.test.api
|
||||||
|
(:require
|
||||||
|
[cheshire.core :refer :all]
|
||||||
|
[NoteHub.storage :as storage])
|
||||||
|
(:use [NoteHub.api]
|
||||||
|
[noir.util.test]
|
||||||
|
[clojure.test]))
|
||||||
|
|
||||||
|
(def note "hello world! This is a _test_ note!")
|
||||||
|
(def pid "somePlugin")
|
||||||
|
(def pid2 "somePlugin2")
|
||||||
|
(def note-title (str (apply print-str (get-date)) " hello-world-this-is-a-test-note"))
|
||||||
|
(def note-url (str domain (apply str (interpose "/" (get-date))) "/hello-world-this-is-a-test-note"))
|
||||||
|
(defn substring? [a b] (not (= nil (re-matches (re-pattern (str "(?s).*" a ".*")) b))))
|
||||||
|
|
||||||
|
(defmacro isnt [arg] `(is (not ~arg)))
|
||||||
|
|
||||||
|
(defn register-publisher-fixture [f]
|
||||||
|
(def psk (storage/register-publisher pid))
|
||||||
|
(f)
|
||||||
|
(storage/revoke-publisher pid)
|
||||||
|
(storage/revoke-publisher pid2)
|
||||||
|
(storage/delete-note note-title))
|
||||||
|
|
||||||
|
(use-fixtures :each register-publisher-fixture)
|
||||||
|
|
||||||
|
(deftest api
|
||||||
|
(testing "API"
|
||||||
|
(testing "publisher registration"
|
||||||
|
(is (storage/valid-publisher? pid))
|
||||||
|
(isnt (storage/valid-publisher? pid2))
|
||||||
|
(let [psk2 (storage/register-publisher pid2)]
|
||||||
|
(is (= psk2 (storage/get-psk pid2)))
|
||||||
|
(is (storage/valid-publisher? pid2))
|
||||||
|
(is (storage/revoke-publisher pid2))
|
||||||
|
(isnt (storage/valid-publisher? "any_PID"))
|
||||||
|
(isnt (storage/valid-publisher? pid2))))
|
||||||
|
(testing "note publishing & retrieval"
|
||||||
|
(isnt (:success (:status (get-note "some note id"))))
|
||||||
|
(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)))
|
||||||
|
(is (= (:longURL post-response) (:longURL get-response) note-url))
|
||||||
|
(is (= (:shortURL post-response) (:shortURL get-response)))
|
||||||
|
(is (= "1" (get-in get-response [:statistics :views])))
|
||||||
|
(isnt (get-in get-response [:statistics :edited]))
|
||||||
|
(is (= "2" (get-in (get-note (:noteID post-response)) [:statistics :views])))))
|
||||||
|
(testing "creation with wrong signature"
|
||||||
|
(let [response (post-note note pid (get-signature pid2 psk note))]
|
||||||
|
(isnt (:success (:status response)))
|
||||||
|
(is (= "signature invalid" (:message (:status response)))))
|
||||||
|
(let [response (post-note note pid (get-signature pid2 psk "any note"))]
|
||||||
|
(isnt (:success (:status response)))
|
||||||
|
(is (= "signature invalid" (:message (:status response)))))
|
||||||
|
(isnt (:success (:status (post-note note pid (get-signature pid "random_psk" note)))))
|
||||||
|
(is (:success (:status (post-note note pid (get-signature pid psk note)))))
|
||||||
|
(let [randomPID "randomPID"
|
||||||
|
psk2 (storage/register-publisher randomPID)
|
||||||
|
_ (storage/revoke-publisher randomPID)
|
||||||
|
response (post-note note randomPID (get-signature randomPID psk2 note))]
|
||||||
|
(isnt (:success (:status response)))
|
||||||
|
(is (= (:message (:status response)) "pid invalid"))))
|
||||||
|
(testing "note update"
|
||||||
|
(let [post-response (post-note note pid (get-signature pid psk note) "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 (get-signature pid psk new-note) "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
|
||||||
|
(get-signature pid psk note-id new-note "passwd") "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
|
||||||
|
(get-signature pid psk note-id "aaa" "pass") "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))))))))
|
||||||
|
|
||||||
|
(deftest api-note-creation
|
||||||
|
(testing "Note creation"
|
||||||
|
(let [response (send-request [:post "/api/note"]
|
||||||
|
{:note note
|
||||||
|
:pid pid
|
||||||
|
:signature (get-signature pid psk note)
|
||||||
|
:version "1.0"})
|
||||||
|
body (parse-string (:body response))
|
||||||
|
noteID (body "noteID")]
|
||||||
|
(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")))
|
||||||
|
(is (do
|
||||||
|
(storage/delete-note noteID)
|
||||||
|
(not (storage/note-exists? noteID)))))))
|
||||||
|
|
||||||
|
(deftest note-update
|
||||||
|
(let [response (send-request [:post "/api/note"]
|
||||||
|
{:note note
|
||||||
|
:pid pid
|
||||||
|
:signature (get-signature pid psk note)
|
||||||
|
:version "1.0"
|
||||||
|
:password "qwerty"})
|
||||||
|
body (parse-string (:body response))
|
||||||
|
noteID (body "noteID")]
|
||||||
|
(testing "Note update"
|
||||||
|
(is (has-status response 200))
|
||||||
|
(is (get-in body ["status" "success"]))
|
||||||
|
(is (storage/note-exists? noteID))
|
||||||
|
(is (substring? "_test_ note"
|
||||||
|
((parse-string
|
||||||
|
(:body (send-request [:get "/api/note"] {:version "1.0" :noteID noteID}))) "note")))
|
||||||
|
(let [response (send-request [:put "/api/note"]
|
||||||
|
{:noteID noteID
|
||||||
|
:note "WRONG pass"
|
||||||
|
:pid pid
|
||||||
|
:signature (get-signature pid psk noteID "WRONG pass" "qwerty1")
|
||||||
|
:password "qwerty1"
|
||||||
|
:version "1.0"})
|
||||||
|
body (parse-string (:body response))]
|
||||||
|
(is (has-status response 200))
|
||||||
|
(isnt (get-in body ["status" "success"]))
|
||||||
|
(is (= "password invalid" (get-in body ["status" "message"])))
|
||||||
|
(isnt (get-in body ["statistics" "edited"]))
|
||||||
|
(is (substring? "_test_ note"
|
||||||
|
((parse-string
|
||||||
|
(: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"]))
|
||||||
|
(isnt (= nil (((parse-string
|
||||||
|
(: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")))
|
||||||
|
(is (do
|
||||||
|
(storage/delete-note noteID)
|
||||||
|
(not (storage/note-exists? noteID)))))))
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
(ns NoteHub.test.storage
|
(ns NoteHub.test.storage
|
||||||
(:use [NoteHub.storage] [clojure.test]))
|
(:use [NoteHub.storage]
|
||||||
|
[NoteHub.api :only [build-key]]
|
||||||
|
[NoteHub.views.pages]
|
||||||
|
[clojure.test]))
|
||||||
|
|
||||||
(def date [2012 06 03])
|
(def date [2012 06 03])
|
||||||
(def test-title "Some title.")
|
(def test-title "Some title.")
|
||||||
@@ -13,54 +16,55 @@
|
|||||||
|
|
||||||
|
|
||||||
(deftest storage
|
(deftest storage
|
||||||
(testing "Storage"
|
(testing "Storage"
|
||||||
(testing "of short-url mechanism"
|
(testing "of short-url mechanism"
|
||||||
(let [url (create-short-url metadata)
|
(let [url (create-short-url metadata)
|
||||||
url2 (create-short-url metadata)]
|
url2 (create-short-url metadata)]
|
||||||
(is (short-url-exists? url))
|
(is (short-url-exists? url))
|
||||||
(is (= url url2))
|
(is (= url url2))
|
||||||
(is (= metadata (resolve-url url)))
|
(is (= metadata (resolve-url url)))
|
||||||
(is (not (do
|
(is (not (do
|
||||||
(delete-short-url url)
|
(delete-short-url url)
|
||||||
(short-url-exists? url))))))
|
(short-url-exists? url))))))
|
||||||
(testing "of correct note creation"
|
(testing "of correct note creation"
|
||||||
(is (= (do
|
(is (= (do
|
||||||
(set-note date test-title test-note)
|
(add-note (build-key date test-title) test-note)
|
||||||
(get-note date test-title))
|
(get-note (build-key date test-title)))
|
||||||
test-note))
|
test-note))
|
||||||
(is (= "1" (get-note-views date test-title)))
|
(is (= "1" (get-note-views (build-key date test-title))))
|
||||||
(is (= (do
|
(is (= (do
|
||||||
(get-note date test-title)
|
(get-note (build-key date test-title))
|
||||||
(get-note-views date test-title))
|
(get-note-views (build-key date test-title)))
|
||||||
"2")))
|
"2")))
|
||||||
(testing "of note update"
|
(testing "of note update"
|
||||||
(is (= (do
|
(is (= (do
|
||||||
(set-note date test-title test-note "12345qwert")
|
(add-note (build-key date test-title) test-note "12345qwert")
|
||||||
(get-note date test-title))
|
(get-note (build-key date test-title)))
|
||||||
test-note))
|
test-note))
|
||||||
(is (= (do
|
(is (valid-password? (build-key date test-title) "12345qwert"))
|
||||||
(update-note (build-key date test-title) "update" "12345qwert")
|
(is (= (do
|
||||||
(get-note date test-title))
|
(update-note (build-key date test-title) "update" "12345qwert")
|
||||||
"update"))
|
(get-note (build-key date test-title)))
|
||||||
(is (= (do
|
"update"))
|
||||||
(update-note (build-key date test-title) "not authorized" "44444")
|
(is (= (do
|
||||||
(get-note date test-title))
|
(update-note (build-key date test-title) "not authorized" "44444")
|
||||||
"update")))
|
(get-note (build-key date test-title)))
|
||||||
(testing "of the note access"
|
"update")))
|
||||||
(is (not= (get-note date test-title) "any text")))
|
(testing "of the note access"
|
||||||
(testing "session management"
|
(is (not= (get-note (build-key date test-title)) "any text")))
|
||||||
(let [s1 (create-session)
|
(testing "session management"
|
||||||
s2 (create-session)
|
(let [s1 (create-session)
|
||||||
s3 (create-session)]
|
s2 (create-session)
|
||||||
(is (invalidate-session s1))
|
s3 (create-session)]
|
||||||
(is (not (invalidate-session (str s1 s2))))
|
(is (invalidate-session s1))
|
||||||
(is (invalidate-session s2))
|
(is (not (invalidate-session (str s1 s2))))
|
||||||
(is (not (invalidate-session "wrongtoken")))
|
(is (invalidate-session s2))
|
||||||
(is (invalidate-session s3))))
|
(is (not (invalidate-session "wrongtoken")))
|
||||||
(testing "of note existence"
|
(is (invalidate-session s3))))
|
||||||
(is (note-exists? date test-title))
|
(testing "of note existence"
|
||||||
(is (not (do
|
(is (note-exists? (build-key date test-title)))
|
||||||
(delete-note date test-title)
|
(is (not (do
|
||||||
(note-exists? date test-title))))
|
(delete-note (build-key date test-title))
|
||||||
(is (not (note-exists? [2013 06 03] test-title)))
|
(note-exists? (build-key date test-title)))))
|
||||||
(is (not (note-exists? date "some title"))))))
|
(is (not (note-exists? (build-key [2013 06 03] test-title))))
|
||||||
|
(is (not (note-exists? (build-key date "some title")))))))
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
(ns NoteHub.test.views.pages
|
(ns NoteHub.test.views.pages
|
||||||
(:use [NoteHub.views.pages]
|
(:use [NoteHub.views.pages]
|
||||||
|
[NoteHub.api :only [build-key get-date]]
|
||||||
[noir.util.test]
|
[noir.util.test]
|
||||||
[NoteHub.views.common :only [url]]
|
[NoteHub.views.common :only [url]]
|
||||||
[NoteHub.storage]
|
[NoteHub.storage]
|
||||||
[clojure.test]))
|
[clojure.test]))
|
||||||
|
|
||||||
(defn substring? [a b]
|
(defn substring? [a b] (not (= nil (re-matches (re-pattern (str "(?s).*" a ".*")) b))))
|
||||||
(not (= nil
|
|
||||||
(re-matches (re-pattern (str "(?s).*" a ".*")) b))))
|
|
||||||
(def date [2012 6 3])
|
(def date [2012 6 3])
|
||||||
(def test-title "some-title")
|
(def test-title "some-title")
|
||||||
(def test-note "# This is a test note.\nHello _world_. Motörhead, тест.")
|
(def test-note "# This is a test note.\nHello _world_. Motörhead, тест.")
|
||||||
|
|
||||||
(defn create-testnote-fixture [f]
|
(defn create-testnote-fixture [f]
|
||||||
(set-note date test-title test-note)
|
(add-note (build-key date test-title) test-note)
|
||||||
(f)
|
(f)
|
||||||
(delete-note date test-title))
|
(delete-note (build-key date test-title)))
|
||||||
|
|
||||||
(use-fixtures :each create-testnote-fixture)
|
(use-fixtures :each create-testnote-fixture)
|
||||||
|
|
||||||
@@ -23,8 +22,8 @@
|
|||||||
|
|
||||||
(deftest testing-fixture
|
(deftest testing-fixture
|
||||||
(testing "Was a not created?"
|
(testing "Was a not created?"
|
||||||
(is (= (get-note date test-title) test-note))
|
(is (= (get-note (build-key date test-title)) test-note))
|
||||||
(is (note-exists? date test-title))))
|
(is (note-exists? (build-key date test-title)))))
|
||||||
|
|
||||||
(deftest export-test
|
(deftest export-test
|
||||||
(testing "Markdown export"
|
(testing "Markdown export"
|
||||||
@@ -42,12 +41,12 @@
|
|||||||
{:session-key session-key
|
{:session-key session-key
|
||||||
:draft test-note
|
:draft test-note
|
||||||
:session-value (str (get-hash (str test-note session-key)))}) 302))
|
:session-value (str (get-hash (str test-note session-key)))}) 302))
|
||||||
(is (note-exists? date title))
|
(is (note-exists? (build-key date title)))
|
||||||
(is (substring? "Hello _world_"
|
(is (substring? "Hello _world_"
|
||||||
((send-request (url year month day title)) :body)))
|
((send-request (url year month day title)) :body)))
|
||||||
(is (do
|
(is (do
|
||||||
(delete-note date title)
|
(delete-note (build-key date title))
|
||||||
(not (note-exists? date title)))))))
|
(not (note-exists? (build-key date title))))))))
|
||||||
|
|
||||||
(deftest note-update
|
(deftest note-update
|
||||||
(let [session-key (create-session)
|
(let [session-key (create-session)
|
||||||
@@ -62,7 +61,7 @@
|
|||||||
:draft "test note"
|
:draft "test note"
|
||||||
:password "qwerty"
|
:password "qwerty"
|
||||||
:session-value (str (get-hash (str "test note" session-key)))}) 302))
|
:session-value (str (get-hash (str "test note" session-key)))}) 302))
|
||||||
(is (note-exists? date title))
|
(is (note-exists? (build-key date title)))
|
||||||
(is (substring? "test note"
|
(is (substring? "test note"
|
||||||
((send-request (url year month day title)) :body)))
|
((send-request (url year month day title)) :body)))
|
||||||
(is (has-status
|
(is (has-status
|
||||||
@@ -82,8 +81,8 @@
|
|||||||
(is (substring? "UPDATED CONTENT"
|
(is (substring? "UPDATED CONTENT"
|
||||||
((send-request (url year month day title)) :body)))
|
((send-request (url year month day title)) :body)))
|
||||||
(is (do
|
(is (do
|
||||||
(delete-note date title)
|
(delete-note (build-key date title))
|
||||||
(not (note-exists? date title)))))))
|
(not (note-exists? (build-key date title))))))))
|
||||||
|
|
||||||
(deftest requests
|
(deftest requests
|
||||||
(testing "HTTP Status"
|
(testing "HTTP Status"
|
||||||
|
|||||||
Reference in New Issue
Block a user