Skip to content

Instantly share code, notes, and snippets.

@seryh
Last active November 18, 2016 06:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seryh/c0fa5e6743726efc1b8d47c163987f7d to your computer and use it in GitHub Desktop.
Save seryh/c0fa5e6743726efc1b8d47c163987f7d to your computer and use it in GitHub Desktop.
(ns skooma.rpc
(:require-macros [cljs.core.async.macros :refer [go go-loop]])
(:require [cljs.core.async :refer [<! >! chan pub sub put!]]
[cljs.core :refer [random-uuid]]
[clojure.string :as string]
[cljs-http.client :as http]
[cljs-http.util :as http-util]))
(defrecord context [rpc-url def-params])
(def ^:private error-chan (chan))
(def ^:private our-error-pub (pub error-chan :msg-type))
(def ^:private rpc-template {:jsonrpc "2.0"
:method nil
:params nil
:id nil})
(def default-context (atom (map->context {:rpc-url "/rpc"
:def-params {:userID 1863
:currency "RUB"
:lang "ru"}})))
(defn uuid-string
[] (str (.-uuid (random-uuid))))
(defn http-error? [http-response]
(not (boolean (http/unexceptional-status? (:status http-response)))))
(defn rpc-error? [http-response]
(boolean (-> http-response :body :error)))
(defn error-response? [http-response]
(or (http-error? http-response)
(rpc-error? http-response)))
(defn when-result [http-response])
(defn sub-from-error
"Подписаться на события об RPC ошибках"
[f]
(let [c (chan)]
(sub our-error-pub :error c)
(go-loop []
(let [{:keys [msg-type http-response]} (<! c)]
(f http-response)
(recur))) c))
(defmulti post
"return channel with body http response"
(fn ([context method params]
[(type context) (type method) (type params)])))
(defmethod post [js/String PersistentArrayMap nil] [method params _]
(post @default-context method params))
(defmethod post [context js/String PersistentArrayMap] [context method params]
(let [params (merge (:def-params context) params)
req (merge rpc-template {:id (uuid-string)
:method method
:params params})
resp-chan (chan)
proxy-chan (http/post (:rpc-url context) {:json-params req})]
(go (let [http-response (<! proxy-chan)]
(when (error-response? http-response)
(>! error-chan {:msg-type :error :http-response (-> http-response :body)}))
(>! resp-chan (-> http-response :body))))
resp-chan))
(defmulti posts
"return channel with hashmap http responses"
(fn ([first & body]
(if (= context (type first))
context
:default))))
(defmethod posts :default [& body]
(apply posts (cons @default-context body)))
(defmethod posts context [context & body]
(let [resp-chan (chan)
req-map (apply hash-map body)
reqs-maker (fn [coll key item]
(let [id (uuid-string)
params (merge (:def-params context) (:params item))
req (merge rpc-template {:id id
:method (:method item)
:params params})]
(merge coll {:req-list (conj (:req-list coll) req)
:keys (merge (:keys coll) {id key})})))
data (reduce-kv reqs-maker {:keys {} :req-list []} req-map)
proxy-chan (http/post (:rpc-url context) {:json-params (:req-list data)})]
(go (let [http-response (<! proxy-chan)
resp-maker (fn [coll item]
(when (boolean (:error item))
(>! error-chan {:msg-type :error :http-response item}))
(let [id (:id item)
id-keys (:keys data)
key (id-keys id)]
(merge coll {key item})))]
(if (http-error? http-response)
(>! error-chan {:msg-type :error :http-response (-> http-response :body)})
(let [response (reduce resp-maker {} (-> http-response :body))]
(>! resp-chan response))))) resp-chan))
(comment
(let [res-chan (post "AutoFind" {:term "новосибирск"})]
(go (println (<! res-chan))))
(let [res-chan (posts
:res-1 {:method "AutoFind" :params {:term "sdfgsdgf"}}
:res-2 {:method "AutoFind" :params {:term "sdfgsdfgsd"}})]
(go (println (<! res-chan))))
;; => {:res-1 {:jsonrpc 2.0, :result {:ItemCount 0, :Result [[]]}, :id 253d1114-b1f6-48c2-974d-30012c5ccee4}, :res-2 {:jsonrpc 2.0, :result {:ItemCount 0, :Result [[]]}, :id a081cc78-607f-4db2-958d-a4af2ae68cbc}}
)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment