Skip to content

Instantly share code, notes, and snippets.

@igrishaev
Created October 2, 2018 13:18
Show Gist options
  • Save igrishaev/507c753e1acdc55119e9f0a60c73e2f3 to your computer and use it in GitHub Desktop.
Save igrishaev/507c753e1acdc55119e9f0a60c73e2f3 to your computer and use it in GitHub Desktop.
Single API endpoint
(ns project.api
(:require [project.spec :as spec]
[project.error :as e]
[project.handlers :as h]
[project.resp :as r]
[clojure.tools.logging :as log]))
(declare actions)
(defn kw->var
[kw]
(-> kw str (subs 1) symbol resolve))
(defn handler-unsafe
;; todo body
[request]
(let [{:keys [params user session]} request
spec :project.spec/api.base]
(if user
(if-let [params* (spec/conform spec params)]
(let [{:keys [action]} params*
rule (get actions action)]
(if rule
(let [{:keys [handler spec]} rule
handler (kw->var handler)
params* (spec/conform spec params)]
(if params*
(handler params* user session)
(r/err-spec spec params)))
(r/err-action action)))
(r/err-spec spec params))
(r/err-anon))))
(defn handler
[request]
(try
(handler-unsafe request)
(catch Throwable e
(log/error e) ;; TODO log better
(r/err-server e))))
(def actions
{:search-feeds {:handler :project.handlers/search-feeds
:spec :project.spec/api.search-feeds}
:update-subscription {:handler :project.handlers/update-subscription
:spec :project.spec/api.update-subscription}
:subscribe {:handler :project.handlers/subscribe
:spec :project.spec/api.subscribe}
:unsubscribe {:auth-required true
:handler :project.handlers/unsubscribe
:spec :project.spec/api.unsubscribe}
:subscriptions {:handler :project.handlers/subscriptions
:spec :project.spec/api.subscriptions}
:messages {:handler :project.handlers/messages
:spec :project.spec/api.messages}
:mark-read {:handler :project.handlers/mark-read
:spec :project.spec/api.mark-read}
:user-info {:handler :project.handlers/user-info
:spec :project.spec/api.user-info}
:logout {:handler :project.handlers/logout
:spec :project.spec/api.logout}
:import-opml {:handler :project.handlers/import-opml
:spec :project.spec/api.import-opml}
})
(ns project.app
(:require [project.api :as api]
[project.env :refer [env]]
[project.auth :as auth]
[project.views :as views]
[project.beat :as beat]
[compojure.core :refer [context defroutes GET POST]]
[compojure.route :as route]
[ring.middleware.resource :refer [wrap-resource]]
[ring.middleware.session :refer [wrap-session]]
[ring.middleware.session.cookie :refer [cookie-store]]
[ring.middleware.params :refer [wrap-params]]
[ring.middleware.keyword-params :refer [wrap-keyword-params]]
[ring.middleware.json :refer
[wrap-json-response wrap-json-params]]))
(defroutes app-naked
(GET "/dev" request (views/view-dev request))
(GET "/" request (views/view-index request))
(POST "/api" request (api/handler request))
(context
"/auth" []
(context
"/google" []
(GET "/init" request (auth/google-init request))
(GET "/back" request (auth/google-back request)))
(context
"/email" []
(POST "/init" request (auth/email-init request))
(GET "/back" request (auth/email-back request))))
(GET "/__cron/:api" request (beat/cron-handler request))
;; todo better 404
(route/not-found ""))
;;
;; Session & cookies
;;
(def opt-cookie
{:secure (:cookie-secure env)
:http-only (:cookie-http-only env)
:max-age (:cookie-max-age env)})
(def opt-session
{:store (cookie-store {:key (:cookie-session-key env)})
:cookie-attrs opt-cookie
:cookie-name (:cookie-session-name env)})
;;
;; Final app
;;
(def app
(-> app-naked
;; tpl/wrap-context
auth/wrap-user
(wrap-session opt-session)
wrap-keyword-params
wrap-params
wrap-json-params
wrap-json-response
;;views/wrap-exception
(wrap-resource "public")))
(ns project.handlers
"https://www.jsonrpc.org/specification"
(:require [project.models :as models
:refer (clean-feed clean-user)]
[project.db :as db]
[project.error :as e]
[project.sync :as sync]
[project.time :as t]
[project.env :refer (env)]
[project.search :as search]
[project.opml :as opml]
[project.resp :refer [ok] :as r]
[project.queue :as queue]
[clojure.tools.logging :as log]))
;;
;; API
;;
(defn subscribe
[params user & _]
(db/with-tx
(let [{:keys [feed_id title]} params
{user_id :id} user
limit 100] ;; todo config or the last number
(db/with-tx
(if-let [feed (models/get-feed-by-id feed_id)]
(do
;; create subscription
(models/upsert-sub {:feed_id feed_id
:user_id user_id
:title title})
;; subscribe to the latest N entries
(db/subscribe-user-to-the-last-feed-entries
{:user_id user_id
:feed_id feed_id
:limit limit})
;; TODO take the number from the last update
(db/sync-subs-counters
{:user_id user_id
:feed_id feed_id})
(let [feed (db/get-single-full-feed
{:feed_id feed_id :user_id user_id})]
(ok (clean-feed feed))))
(r/err-feed-404 feed_id))))))
(defn unsubscribe
[params user & _]
(let [{:keys [feed_id]} params
{user_id :id} user
where {:feed_id feed_id :user_id user_id}]
(db/with-tx
(db/unsub-del-sub where)
(db/unsub-del-messages where))
(ok {:feed_id feed_id})))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment