aboutsummaryrefslogtreecommitdiffstats
path: root/src/gallifrey/handler.clj
blob: 52d59c72128f510da719300da78fef4be5982512 (plain)
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
(ns gallifrey.handler
  (:require [gallifrey.store :as store]
            [utilis.map :refer [map-vals compact]]
            [liberator.core :refer [defresource]]
            [liberator.representation :refer [as-response ring-response]]
            [compojure.core :refer [GET PUT PATCH ANY defroutes]]
            [compojure.route :refer [resources]]
            [ring.util.response :refer [resource-response content-type]]
            [ring.middleware.defaults :refer [wrap-defaults api-defaults]]
            [ring.middleware.anti-forgery :refer [wrap-anti-forgery]]
            [ring.middleware.session :refer [wrap-session]]
            [cheshire.core :as json]
            [clj-time.format :as tf]
            [integrant.core :as ig]))

(declare handler)

(defonce ^:private the-store (atom nil))

(defmethod ig/init-key :gallifrey/handler  [_ {:keys [store]}]
  (reset! the-store store)
  handler)

(defmethod ig/halt-key! :gallifrey/handler  [_ _]
  (reset! the-store nil))

(declare serialize de-serialize)

(defn entity-existed?
  [id & {:keys [t-k]}]
  (store/entity-existed? @the-store id :t-k t-k))

(defresource entity-endpoint [id]
  :allowed-methods [:get :put :delete]
  :available-media-types ["application/json"]
  :malformed? (fn [ctx]
                (when (#{:put :delete} (-> ctx :request :request-method))
                  (-> ctx :request :params :actor empty?)))
  :exists? (fn [ctx]
             (if-let [resource (->> (when-let [t-k (-> ctx :request :params :t-k)]
                                      (tf/parse t-k))
                                    (store/get-entity @the-store id :t-k)
                                    serialize)]
               {::resource resource}
               (when (and (= :put (-> ctx :request :request-method))
                          (-> ctx :request :params :create not-empty))
                 true)))
  :existed? (fn [ctx]
              (entity-existed? id :t-k (when-let [t-k (-> ctx :request :params :t-k)]
                                         (tf/parse t-k))))
  :handle-ok ::resource
  :can-put-to-missing? false
  :handle-not-implemented (fn [{{m :request-method} :request :as ctx}]
                            (when (= :put m)
                              (-> (as-response "Resource not found" ctx)
                                  (assoc :status (if (entity-existed? id) 410 404))
                                  (ring-response))))
  :put! (fn [ctx]
          (let [body (json/parse-string (slurp (get-in ctx [:request :body])))
                actor (-> ctx :request :params :actor)
                changes-only (when-let [c (-> ctx :request :params :changes-only)]
                               (boolean c))]
            {::created? (:created? (store/put-entity @the-store actor id body
                                                     :changes-only changes-only))}))
  :delete! (fn [ctx]
             (let [actor (-> ctx :request :params :actor)]
               (store/delete-entity @the-store actor id)))
  :new? ::created?)

(defresource entity-lifespan-endpoint [id]
  :allowed-methods [:get]
  :available-media-types ["application/json"]
  :exists? (fn [ctx]
             (when-let [resource (not-empty
                                  (store/entity-lifespan @the-store id))]
               {::resource (-> resource
                               (update :created str)
                               (update :updated (partial map str))
                               (update :deleted str)
                               compact)}))
  :handle-ok ::resource)

(defroutes app-routes
  (ANY "/entity/:id" [id] (entity-endpoint id))
  (GET "/entity/:id/lifespan" [id] (entity-lifespan-endpoint id))
  (resources "/"))

(def handler
  (-> app-routes
      (wrap-defaults api-defaults)))


;;; Implementation

(defn- serialize
  [e]
  (compact
   (update e :_meta #(map-vals
                      (fn [m]
                        (map-vals str m)) %))))

(defn- de-serialize
  [e]
  e)