Skip to content

Commit 92c5ee8

Browse files
committed
Add otus-13 lesson
1 parent c62e042 commit 92c5ee8

File tree

4 files changed

+395
-0
lines changed

4 files changed

+395
-0
lines changed

otus-13/README.md

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
### Урок #13
2+
3+
4+
clj-http
5+
6+
https://github.com/dakrone/clj-http
7+
8+
9+
cheshire
10+
11+
https://github.com/dakrone/cheshire
12+
13+
14+
clj-http-fake
15+
16+
https://codeberg.org/valpackett/clj-http-fake
17+
18+
19+
Локальный HTTP сервер https://httpbin.org/#/
20+
21+
```bash
22+
docker run -p 80:80 kennethreitz/httpbin
23+
```

otus-13/project.clj

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
(defproject otus-13 "0.1.0-SNAPSHOT"
2+
:description "FIXME: write description"
3+
:url "http://example.com/FIXME"
4+
5+
:dependencies [[org.clojure/clojure "1.11.1"]
6+
[clj-http "3.12.3"]
7+
[cheshire "5.11.0"]
8+
[org.clojure/data.csv "1.0.1"]
9+
[clj-http-fake "1.0.4"]]
10+
11+
:repl-options {:init-ns otus-13.core})

otus-13/src/otus_13/core.clj

+316
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
(ns otus-13.core
2+
(:require [clj-http.client :as http]
3+
[clojure.java.io :as io]
4+
[cheshire.core :as cheshire]
5+
[clojure.data.csv :as csv])
6+
(:import [java.io InputStream]))
7+
8+
(def host "https://httpbin.org/")
9+
10+
(defn make-url
11+
([] host)
12+
([suffix] (str host suffix)))
13+
14+
(slurp "https://httpbin.org/json")
15+
16+
17+
;; GET, POST, PUT, DELETE
18+
19+
(http/get (make-url "json"))
20+
21+
22+
(http/get (make-url "json")
23+
{:as :json})
24+
25+
26+
(http/get (make-url "headers")
27+
{:headers {:x-otus-course "Clojure Developer"}
28+
:as :json})
29+
30+
31+
(http/get (make-url "headers")
32+
{:headers {:x-otus-course "Clojure Developer"}
33+
:accept "text/html"
34+
:as :json})
35+
36+
37+
;; All request options
38+
39+
;; :url
40+
;; :method
41+
;; :query-params
42+
;; :basic-auth
43+
;; :content-type
44+
;; :accept
45+
;; :accept-encoding
46+
;; :as
47+
;; :headers
48+
;; :body
49+
;; :connection-timeout
50+
;; :connection-request-timeout
51+
;; :connection-manager
52+
;; :cookie-store
53+
;; :cookie-policy
54+
;; :multipart
55+
;; :query-string
56+
;; :redirect-strategy
57+
;; :max-redirects
58+
;; :retry-handler
59+
;; :request-method
60+
;; :scheme
61+
;; :server-name
62+
;; :server-port
63+
;; :socket-timeout
64+
;; :uri
65+
;; :response-interceptor
66+
;; :proxy-host
67+
;; :proxy-port
68+
;; :http-client-context
69+
;; :http-request-config
70+
;; :http-client
71+
;; :proxy-ignore-hosts
72+
;; :proxy-user
73+
;; :proxy-pass
74+
;; :digest-auth
75+
;; :ntlm-auth
76+
;; :multipart-mode
77+
;; :multipart-charset
78+
79+
80+
(http/post (make-url "post")
81+
{:body "{\"foo\": \"bar\"}"
82+
:content-type :json
83+
:as :json})
84+
85+
86+
;; as a urlencoded body
87+
(http/post (make-url "post")
88+
{:form-params {:foo "bar"}
89+
:as :json})
90+
91+
92+
(http/post (make-url "post")
93+
{:form-params {:foo "bar"}
94+
:content-type :json
95+
:as :json})
96+
97+
98+
(http/put (make-url "put")
99+
{:as :json})
100+
101+
102+
(http/delete (make-url "delete")
103+
{:as :json})
104+
105+
106+
(http/request {:url (make-url "delete")
107+
:method :delete
108+
:as :json})
109+
110+
111+
112+
113+
114+
;; coercion
115+
116+
;; :byte-array, :json, :json-string-keys, :transit+json, :transit+msgpack, :clojure,
117+
;; :x-www-form-urlencoded, :stream, :reader
118+
119+
(def response
120+
(http/get (make-url "stream/10")
121+
{:as :stream}))
122+
123+
124+
(type (:body response))
125+
126+
(isa? (class (:body response)) InputStream)
127+
128+
(-> (io/reader (:body response))
129+
(line-seq)
130+
(first)
131+
(cheshire/parse-string true))
132+
133+
(->> (io/reader (:body response))
134+
(line-seq)
135+
(mapv #(cheshire/parse-string % true)))
136+
137+
138+
(let [response (http/get (make-url "stream/10") {:as :reader})]
139+
(with-open [reader (:body response)]
140+
(doall
141+
(for [line (line-seq reader)]
142+
(cheshire/parse-string line true)))))
143+
144+
145+
146+
147+
148+
;; custom CSV coercion
149+
(defmethod http/coerce-response-body :csv [_request {:keys [body] :as response}]
150+
(if (or (http/server-error? response)
151+
(http/client-error? response))
152+
response
153+
(let [v (-> (slurp body :encoding "Windows-1251")
154+
(csv/read-csv :separator \;))]
155+
(assoc response :body v))))
156+
157+
158+
(-> (http/get "https://iss.moex.com/iss/engines/stock/markets.csv"
159+
{:as :csv})
160+
:body)
161+
162+
163+
164+
;; exceptions, slingshot
165+
166+
(http/get (make-url "status/400"))
167+
168+
169+
(http/get (make-url "status/500"))
170+
171+
172+
(http/get (make-url "status/500")
173+
{:throw-entire-message? true})
174+
175+
176+
(http/get (make-url "status/500")
177+
{:throw-exceptions false})
178+
179+
180+
(http/server-error?
181+
(http/get (make-url "status/500")
182+
{:throw-exceptions false}))
183+
184+
185+
(http/get (make-url "status/250")
186+
{:unexceptional-status #(<= 200 % 249)})
187+
188+
189+
190+
191+
192+
(require '[slingshot.slingshot :refer [throw+ try+]])
193+
194+
195+
(try+
196+
(throw+ {:foo "bar"})
197+
(throw+ 152)
198+
(throw+ (ex-info "Error" {:foo "baz"})) ;; doesn't catch (wierd)!
199+
(throw+ (Exception. "Exception"))
200+
(throw+ (Throwable. "Error"))
201+
202+
(catch [:foo "bar"] thrown-map
203+
(println "caught a map")
204+
(println thrown-map))
205+
206+
(catch integer? i
207+
(println "caught a number")
208+
(println i))
209+
210+
(catch {:data {:foo "bar"}} thrown-map
211+
(println "caught a map")
212+
(println thrown-map))
213+
214+
(catch Exception ex
215+
(println "caught exception")
216+
(println ex))
217+
218+
(catch Throwable t
219+
(println "caught throwable")
220+
(println t))
221+
)
222+
223+
224+
225+
(try+
226+
(http/get (make-url "status/403"))
227+
(http/get (make-url "status/404"))
228+
(http/get (make-url "status/500"))
229+
230+
(catch [:status 403] {:keys [request-time headers body]}
231+
(println "403" request-time headers))
232+
233+
(catch [:status 404] {:keys [request-time headers body]}
234+
(println "NOT Found 404" request-time headers body))
235+
236+
(catch Object _
237+
(println "unexpected error" (:message &throw-context))))
238+
239+
240+
;; async request
241+
(http/get (make-url "delay/5")
242+
{:async? true :as :json}
243+
(fn [response] (println "response is:" (:body response)))
244+
(fn [exception] (println "exception message is: " (.getMessage exception))))
245+
246+
247+
248+
;; pagination
249+
(defn get-data-page [page]
250+
(println "page request" page)
251+
(-> (http/post (make-url "anything")
252+
{:as :json
253+
:content-type :json
254+
:form-params {:data (->> (range)
255+
(drop (* page 5))
256+
(take 5))
257+
:next (when (< page 10)
258+
(inc page))}})
259+
:body :data
260+
(cheshire/parse-string true)))
261+
262+
263+
(defn get-paginated-data [current-page]
264+
(lazy-seq
265+
(let [page (get-data-page current-page)
266+
page-data (:data page)
267+
next-page (:next page)]
268+
(cons page-data
269+
(when (some? next-page)
270+
(get-paginated-data next-page))))))
271+
272+
(def data
273+
(flatten (get-paginated-data 0)))
274+
275+
(type data)
276+
277+
(take 10 data)
278+
(take 20 data)
279+
280+
281+
(comment
282+
;; example of chinking
283+
(def xx (map #(do (prn %) %) (range 0 100)))
284+
(take 1 xx)
285+
)
286+
287+
288+
;; iteration is a seqable/reducible object (from Clojure 1.11)
289+
(def data-2
290+
(->> (iteration get-data-page
291+
:initk 0
292+
:kf :next
293+
:vf :data)
294+
(sequence cat)))
295+
296+
(take 10 data-2)
297+
298+
299+
;; lazy concat
300+
(defn lazy-concat [colls]
301+
(lazy-seq
302+
(when-first [c colls]
303+
(lazy-cat c (lazy-concat (rest colls))))))
304+
305+
306+
(def data-3
307+
(->> (iteration get-data-page
308+
:initk 0
309+
:kf :next
310+
:vf :data)
311+
(lazy-concat)))
312+
313+
314+
(->> (get-paginated-data 0)
315+
(lazy-concat)
316+
(take 10))

otus-13/test/otus_13/core_test.clj

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
(ns otus-13.core-test
2+
(:require
3+
[cheshire.core :as cheshire]
4+
[clojure.test :refer :all]
5+
[clj-http.client :as http]
6+
[clj-http.fake :refer [with-fake-routes]]))
7+
8+
9+
(defn get-slideshow []
10+
(-> (http/get "http://localhost:80/json"
11+
{:as :json})
12+
:body
13+
:slideshow))
14+
15+
16+
17+
(deftest fake-http-example-test
18+
19+
(with-fake-routes
20+
{"http://localhost:80/json"
21+
(fn [request]
22+
{:status 200
23+
:body (cheshire/generate-string
24+
{:slideshow {:author "Nikola Tesla"
25+
:title "Electric power transmission"}})})}
26+
27+
(is (= "Electric power transmission"
28+
(:title (get-slideshow)))))
29+
30+
31+
32+
33+
#_(with-fake-routes
34+
{;;#"http:.*/json"
35+
{:address "http://localhost:80/json" :query-params {:search "author"}}
36+
(fn [request]
37+
{:status 200
38+
:body (cheshire/generate-string
39+
{:slideshow {:author "Nikola Tesla"
40+
:title "Electric power transmission"}})})})
41+
42+
#_(with-fake-routes
43+
{"http://localhost:80/json"
44+
{:get (fn [request] {:status 200 :body "GET"})
45+
:post (fn [request] {:status 200 :body "POST"})}}))

0 commit comments

Comments
 (0)