Skip to content

Commit f6ea72b

Browse files
committed
implement custom defaults support
1 parent d4e2129 commit f6ea72b

File tree

2 files changed

+92
-40
lines changed

2 files changed

+92
-40
lines changed

src/clj_http_hystrix/core.clj

Lines changed: 62 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
((http/wrap-exceptions (constantly resp)) req))
3535
resp))
3636

37-
(def ^:private ^:const hystrix-defaults
37+
(def ^:private ^:const hystrix-base-configuration
3838
{:hystrix/command-key :default
3939
:hystrix/fallback-fn default-fallback
4040
:hystrix/group-key :default
@@ -47,7 +47,10 @@
4747
:hystrix/bad-request-pred client-error?})
4848

4949
(def ^:private hystrix-keys
50-
(keys hystrix-defaults))
50+
(keys hystrix-base-configuration))
51+
52+
(defn ^:private hystrix-defaults [defaults]
53+
(merge hystrix-base-configuration (select-keys defaults hystrix-keys)))
5154

5255
(defn ^:private group-key [s]
5356
(HystrixCommandGroupKey$Factory/asKey (name s)))
@@ -96,42 +99,64 @@
9699
(fn [req resp]
97100
(contains? status-codes (:status resp)))))
98101

99-
(defn wrap-hystrix
100-
"Wrap a clj-http client request with hystrix features (but only if a
101-
command-key is present in the options map)."
102-
[f req]
103-
(if (not-empty (select-keys req hystrix-keys))
104-
(let [req (merge hystrix-defaults req)
105-
bad-request-pred (:hystrix/bad-request-pred req)
106-
fallback (:hystrix/fallback-fn req)
107-
wrap-bad-request (fn [resp] (if (bad-request-pred req resp)
108-
(throw (HystrixBadRequestException. "Ignored bad request"
109-
(wrap {:object resp
110-
:message "Bad request pred"
111-
:stack-trace (stack-trace)})))
112-
resp))
113-
wrap-exception-response (fn [resp] ((http/wrap-exceptions (constantly resp)) (assoc req :throw-exceptions true)))
114-
configurator (configurator req)
115-
logging-context (or (MDC/getCopyOfContextMap) {})
116-
command (proxy [HystrixCommand] [configurator]
117-
(getFallback []
118-
(MDC/setContextMap logging-context)
119-
(log-error (:hystrix/command-key req) this)
120-
(let [exception (.getFailedExecutionException ^HystrixCommand this)
121-
response (when exception (get-thrown-object exception))]
122-
(fallback req response)))
123-
(run []
124-
(MDC/setContextMap logging-context)
125-
(-> req
126-
(assoc :throw-exceptions false)
127-
f
128-
wrap-bad-request
129-
wrap-exception-response)))]
130-
(handle-exception #(.execute command) req))
131-
(f req)))
102+
(defn hystrix-wrapper
103+
"Create a function that wraps clj-http client requests with hystrix features
104+
(but only if a hystrix key is present in the options map).
105+
Accepts a possibly empty map of default hystrix values that will be used as
106+
fallback configuration for each hystrix request."
107+
[custom-defaults]
108+
(let [defaults (hystrix-defaults custom-defaults)]
109+
(fn [f req]
110+
(if (not-empty (select-keys req hystrix-keys))
111+
(let [req (merge defaults req)
112+
bad-request-pred (:hystrix/bad-request-pred req)
113+
fallback (:hystrix/fallback-fn req)
114+
wrap-bad-request (fn [resp] (if (bad-request-pred req resp)
115+
(throw
116+
(HystrixBadRequestException.
117+
"Ignored bad request"
118+
(wrap {:object resp
119+
:message "Bad request pred"
120+
:stack-trace (stack-trace)})))
121+
resp))
122+
wrap-exception-response (fn [resp]
123+
((http/wrap-exceptions (constantly resp))
124+
(assoc req :throw-exceptions true)))
125+
configurator (configurator req)
126+
logging-context (or (MDC/getCopyOfContextMap) {})
127+
command (proxy [HystrixCommand] [configurator]
128+
(getFallback []
129+
(MDC/setContextMap logging-context)
130+
(log-error (:hystrix/command-key req) this)
131+
(let [exception (.getFailedExecutionException ^HystrixCommand this)
132+
response (when exception (get-thrown-object exception))]
133+
(fallback req response)))
134+
(run []
135+
(MDC/setContextMap logging-context)
136+
(-> req
137+
(assoc :throw-exceptions false)
138+
f
139+
wrap-bad-request
140+
wrap-exception-response)))]
141+
(handle-exception #(.execute command) req))
142+
(f req)))))
132143

133144
(defn add-hook
134145
"Activate clj-http-hystrix to wrap all clj-http client requests as
135-
hystrix commands."
146+
hystrix commands.
147+
Provide custom hystrix defaults by providing an optional defaults map:
148+
149+
;; use clj-http-hystrix.core default configuration
150+
(add-hook)
151+
152+
;; 6 second timeout fallback, if not specified in request
153+
(add-hook {:hystrix/timeout-ms 6000})
154+
"
155+
([] (add-hook {}))
156+
([defaults]
157+
(hooke/add-hook #'http/request ::wrap-hystrix (hystrix-wrapper defaults))))
158+
159+
(defn remove-hook
160+
"Deactivate clj-http-hystrix."
136161
[]
137-
(hooke/add-hook #'http/request #'wrap-hystrix))
162+
(hooke/remove-hook #'http/request ::wrap-hystrix))

test/clj_http_hystrix/core_test.clj

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,35 @@
178178

179179
(fact "add-hook can be safely called more than once"
180180
(count (get-hooks)) => 1
181-
(get (get-hooks) #'wrap-hystrix) => #'wrap-hystrix
181+
(contains? (get-hooks) :clj-http-hystrix.core/wrap-hystrix) => true
182182
;call add-hook a few more times and ensure only one hook is present
183-
(add-hook), (add-hook), (add-hook)
183+
(add-hook), (add-hook)
184184
(count (get-hooks)) => 1
185-
(get (get-hooks) #'wrap-hystrix) => #'wrap-hystrix)
185+
(contains? (get-hooks) :clj-http-hystrix.core/wrap-hystrix) => true)
186+
187+
(fact "remove-hook removes clj-http-hystrix hook"
188+
(count (get-hooks)) => 1
189+
(contains? (get-hooks) :clj-http-hystrix.core/wrap-hystrix) => true
190+
(remove-hook)
191+
(get-hooks) => nil
192+
;can be called more than once
193+
(remove-hook), (remove-hook)
194+
(get-hooks) => nil
195+
;restore hook for additional testing
196+
(add-hook))
197+
198+
(fact "add-hook with user-defaults will override base configuration, but not call configuration"
199+
(rest-driven
200+
[{:method :GET
201+
:url "/"}
202+
{:status 500
203+
:times 3}]
204+
(make-hystrix-call {})
205+
=> (throws clojure.lang.ExceptionInfo "clj-http: status 500")
206+
;set custom default for fallback-fn
207+
(remove-hook)
208+
(add-hook {:hystrix/fallback-fn (constantly "bar")})
209+
(make-hystrix-call {}) => "bar"
210+
(make-hystrix-call {:hystrix/fallback-fn (constantly "baz")}) => "baz")
211+
(remove-hook)
212+
(add-hook))

0 commit comments

Comments
 (0)