-
Notifications
You must be signed in to change notification settings - Fork 1
/
app.clj
163 lines (151 loc) · 4.52 KB
/
app.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
(ns libekorma.app
(:require [compojure.core :refer [defroutes ANY]]
[liberator.core :refer [defresource]]
[korma.core :refer
[select insert values where delete update set-fields with join]]
[korma.db :refer [defdb]]
[libekorma.util :as util]
[libekorma.entities :refer [task tag tasktag]]))
(defdb dbconnection util/dbcon)
(defn tasks-request-malformed?
[{{method :request-method} :request :as ctx}]
(if (= :post method)
(let [task-data (util/parse-json-body ctx)]
(if (empty? (:title task-data))
[true {:message "Task title missing or empty"}]
[false {:task-data task-data}]))
false))
(defresource tasks-r
:available-media-types ["application/json"]
:allowed-methods [:get :post]
:malformed? tasks-request-malformed?
:post!
(fn [{task-data :task-data}]
(let [data (into task-data {:created_time (util/cur-time)})]
(insert task (values data))))
:handle-ok (fn [_]
(util/to-json
(select task (with tag)))))
(defn task-update-request-malformed?
[{{method :request-method} :request :as ctx}]
(if (= :put method)
(let [task-data (util/parse-json-body ctx)]
(cond
(empty? task-data)
[true {:message "No new values specififed"}]
(and (contains? task-data :title)
(empty? (:title task-data)))
[true {:message "Empty title is not allowed"}]
true
[false {:task-data task-data}]))
false))
(defn task-update-conflict? [{new-task :task-data old-task :task}]
(let [combined (into old-task new-task)]
(if (and (:is_done combined) (:is_cancelled combined))
[true {:message "Invalid state after update"}]
false)))
(defn update-task [{new-task :task-data old-task :task}]
(let [just-finished?
(or
(and (:is_done new-task)
(not (:is_done old-task)))
(and (:is_cancelled new-task)
(not (:is_cancelled old-task))))
finished-time-dict
(if just-finished?
{:finished_time (util/cur-time)}
{})
updated
(into finished-time-dict
(filter
(fn [[k _]] (#{:title :description :is_cancelled :is_done} k))
new-task))]
(update task
(set-fields updated)
(where {:task_id (:task_id old-task)}))))
(defresource one-task-r [task-id]
:available-media-types ["application/json"]
:allowed-methods [:get :delete :put]
:exists?
(fn [_]
(if-let [task
(first
(select task
(with tag)
(where {:task_id task-id})))]
[true {:task task}]
[false {:message "Task not found"}]))
:delete!
(fn [{{task-id :task_id} :task}]
(delete task
(where {:task_id task-id})))
:can-put-to-missing? false
:malformed? task-update-request-malformed?
:conflict? task-update-conflict?
:put! update-task
:handle-ok
(fn [{task :task}]
(util/to-json task)))
(defn post-task-tags [task-id tags]
(let [known-tags
(if (empty? tags)
[]
(select tag (where {:tag [in tags]})))
known (set (map :tag known-tags))
unknown (filter #(not (some #{%} known)) tags)]
(do
(delete tasktag
(where {:task_id task-id}))
(if-not (empty? unknown)
(insert tag
(values (map (fn [t] {:tag t}) unknown))))
(let [created-tags
(select tag (where {:tag [in unknown]}))
all-tags
(concat known-tags created-tags)]
(if-not (empty? all-tags)
(insert tasktag
(values
(map
(fn [{tag-id :tag_id}]
{:task_id task-id :tag_id tag-id})
all-tags))))))))
(defresource task-tags-r [task-id]
:available-media-types ["application/json"]
:allowed-methods [:post]
:malformed?
(fn [ctx]
[false {:tags (:tags (util/parse-json-body ctx))}])
:can-post-to-missing? false
:exists?
(fn [_]
(if-let [task (first (select task (where {:task_id task-id})))]
[true {:task task}]
[false {:message "Task not found"}]))
:post!
(fn [{tags :tags}]
(post-task-tags task-id tags)))
(defresource tag-tasks-r [tag-word]
:available-media-types ["application/json"]
:allowed-methods [:get]
:exists?
(fn [_]
(if-let [tag-item
(first (select tag
(where {:tag tag-word})))]
[true {:tag tag-item}]
[false {:message "Unknown tag"}]))
:handle-ok
(fn [{{tag-id :tag_id} :tag}]
(util/to-json
(select task
(with tag)
(join tasktag
{:task_id :tasktag.task_id})
(where {:tasktag.tag_id tag-id})))))
(defroutes app
(ANY "/" [] "Hello, I am a cool service!")
(ANY "/tasks" [] tasks-r)
(ANY "/task/:task-id" [task-id] (one-task-r (Integer/parseInt task-id)))
(ANY "/task/:task-id/tags" [task-id] (task-tags-r (Integer/parseInt task-id)))
(ANY "/tag/:tag" [tag] (tag-tasks-r tag)))