-
Notifications
You must be signed in to change notification settings - Fork 12
/
fetch.cljs
132 lines (109 loc) · 4.75 KB
/
fetch.cljs
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
(ns lambdaisland.fetch
(:refer-clojure :exclude [get])
(:require [kitchen-async.promise :as p]
[clojure.core :as c]
[clojure.string :as str]
[clojure.set :as set]
[lambdaisland.uri :as uri]
[lambdaisland.uri.normalize :as uri-normalize]
[applied-science.js-interop :as j]
[cognitect.transit :as transit]))
;; fetch(url, {
;; method: 'POST', // *GET, POST, PUT, DELETE, etc.
;; mode: 'cors', // no-cors, *cors, same-origin
;; cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
;; credentials: 'same-origin', // include, *same-origin, omit
;; headers: {
;; 'Content-Type': 'application/json'
;; // 'Content-Type': 'application/x-www-form-urlencoded',
;; },
;; redirect: 'follow', // manual, *follow, error
;; referrerPolicy: 'no-referrer', // no-referrer, *client
;; body: JSON.stringify(data) // body data type must match "Content-Type" header
;; });
(def content-types
{:transit-json "application/transit+json"
:json "application/json"
:form-encoded "application/x-www-form-urlencoded"
:text "text/plain"
:html "text/html"
:edn "application/edn"})
(def transit-json-writer
(delay (transit/writer :json)))
(def transit-json-reader
(delay (transit/reader :json)))
(defmulti encode-body (fn [content-type body opts] content-type))
(defmethod encode-body :default [_ body opts]
body)
(defmethod encode-body :transit-json [_ body opts]
(transit/write (:transit-json-writer opts @transit-json-writer) body))
(defmethod encode-body :json [_ body opts]
(js/JSON.stringify (clj->js body)))
(defmulti decode-body (fn [content-type bodyp opts] content-type))
(defmethod decode-body :default [_ bodyp opts]
(p/let [body bodyp]
(j/call bodyp :text)))
(defmethod decode-body :transit-json [_ bodyp opts]
(p/let [text (j/call bodyp :text)]
(let [decoded (transit/read (:transit-json-reader opts @transit-json-reader) text)]
(if (satisfies? IWithMeta decoded)
(vary-meta decoded assoc ::raw text)
decoded))))
(defmethod decode-body :json [_ bodyp opts]
(p/let [body bodyp]
(j/call bodyp :json)))
(defn fetch-opts [{:keys [method accept content-type]
:or {method :get
accept :transit-json
content-type :transit-json}}]
#js {:method (str/upper-case (name method))
:headers #js {"Accept" (c/get content-types accept)
"Content-Type" (c/get content-types content-type)}
:redirect "follow"})
(defn request [url & [{:keys [method accept content-type query-params body]
:as opts
:or {accept :transit-json
content-type :transit-json}}]]
(let [url (-> url
uri/uri
(assoc :query (uri/map->query-string query-params))
str)
request (cond-> (fetch-opts opts)
body
(j/assoc! :body (encode-body content-type body opts)))]
(p/let [response (js/fetch url request)]
(p/try
(let [headers (j/get response :headers)
header-map (into {} (map vec) (es6-iterator-seq (j/call headers :entries)))
content-type-header (j/call headers :get "Content-Type")
content-type (when content-type-header
(c/get (set/map-invert content-types)
(str/replace content-type-header #";.*" "")))]
(p/let [body (decode-body content-type response opts)]
^{::request (j/assoc! request :url url)
::response response}
{:status (j/get response :status)
:headers header-map
:body body}))
(p/catch :default e
^{::request (j/assoc! request :url url)
::response response}
{:error e})))))
(def get request)
(defn post [url & [opts]]
(request url (assoc opts :method :post)))
(defn put [url & [opts]]
(request url (assoc opts :method :put)))
(defn delete [url & [opts]]
(request url (assoc opts :method :delete)))
(defn head [url & [opts]]
(request url (assoc opts :method :head)))
(comment
(p/let [result (get "/as400/paginated/VSBSTAMDTA.STOVKP"
{:query-params {:page 1
:page-size 20}})]
(def xxx result))
(p/let [body (:body xxx)]
(def body body))
(p/let [res (head "/as400/paginated/VSBSTAMDTA.STOVKP")]
(def xxx res)))