-
Notifications
You must be signed in to change notification settings - Fork 0
/
errors.clj
117 lines (102 loc) · 4.49 KB
/
errors.clj
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
(ns libmisc-clj.errors
(:require [libmisc-clj.convert :as cnv]
[libmisc-clj.vars-http :as vh])
(:import (java.util NoSuchElementException)
(javax.xml.crypto NoSuchMechanismException)
(org.postgresql.util PSQLException)
(javax.naming LimitExceededException)
(clojure.lang ExceptionInfo)
(java.util.concurrent TimeoutException Future)))
(defmacro ^{:private true} pack-error->json
[msg T code]
`(cnv/json-response {:error ~msg
:trace (when ~T (.getLocalizedMessage ~T))}
~code))
(defn illegal-argument! [fmt & args]
(let [^String msg (apply format (str fmt) args)]
(throw (IllegalArgumentException. msg))))
(defn illegal-state! [fmt & args]
(let [^String msg (apply format (str fmt) args)]
(throw (IllegalStateException. msg))))
(defn access-denied! [fmt & args]
(let [^String msg (apply format (str fmt) args)]
(throw (IllegalMonitorStateException. msg))))
(defn not-authorized! [fmt & args]
(let [^String msg (apply format (str fmt) args)]
(throw (NoSuchMechanismException. msg))))
(defn wrap-exceptions
[handler]
(fn
([request]
(try (handler request)
(catch AssertionError ae
(pack-error->json "Invalid parameters" ae 400))
(catch IllegalArgumentException ia
(pack-error->json "Illegal parameters" ia 400))
(catch NoSuchElementException nfe
(pack-error->json "Not found" nfe 404))
(catch IllegalStateException ise
(pack-error->json "Illegal state" ise 409))
(catch IllegalMonitorStateException ims
(pack-error->json "Forbidden" ims 403))
(catch NoSuchMechanismException nsme
(pack-error->json "Not Authorized" nsme 401))
(catch PSQLException pge
(pack-error->json "Meta DB Error" pge 400))))))
(defmacro service-not-paid! [ctx]
`(throw (ex-info "Payment required" {:cause :not-paid :ctx ~ctx})))
(defmacro service-not-subscribed! [ctx]
`(throw (ex-info "Not subscribed" {:cause :not-subscribed :ctx ~ctx})))
(defmacro service-limits! [type ctx]
`(throw (ex-info "Limit reached" {:cause ~type :ctx ~ctx})))
(defn wrap-paid-access
[handler]
(fn
([request]
(try (handler request)
(catch ExceptionInfo ei
(let [ctx (some-> ei ex-data :ctx)]
(case (some-> ei ex-data :cause)
:not-paid (if ctx (vh/http-jerror-payment-required+ ctx)
vh/http-jerror-payment-required)
:not-subscribed (if ctx (vh/http-jerror-not-subscribed+ ctx)
vh/http-jerror-not-subscribed)
:over-limit (if ctx (vh/http-jerror-limit-exceeded+ ctx)
vh/http-jerror-limit-exceeded)
:over-quota vh/http-jerror-quota-reached
vh/http-jerror-locked)))
(catch UnsupportedOperationException _ vh/http-jerror-quota-reached)
(catch LimitExceededException _ vh/http-jerror-limit-exceeded)))))
(defmacro ignore-exceptions
"Simple macro which wraps the given expression in a try/catch block and ignores the exception if caught."
[& body]
`(try ~@body (catch Throwable ~'_)))
(defn do-with-auto-retries*
"Execute F, a function that takes no arguments, and return the results.
If F fails with an exception, retry F up to NUM-RETRIES times until it succeeds."
[num-retries f]
(if (<= num-retries 0)
(f)
(try (f)
(catch Throwable e
(do-with-auto-retries* (dec num-retries) f)))))
(defmacro auto-retry
"Execute BODY and return the results.
If BODY fails with an exception, retry execution up to NUM-RETRIES times until it succeeds."
[num-retries & body]
`(do-with-auto-retries* ~num-retries
(fn [] ~@body)))
(defn deref-with-timeout
"Call `deref` on a something derefable (e.g. a future or promise), and throw an exception if it takes more than
`timeout-ms`. If `ref` is a future it will attempt to cancel it as well."
[reff timeout-ms]
(let [result (deref reff timeout-ms ::timeout)]
(when (= result ::timeout)
(when (instance? Future reff)
(future-cancel reff))
(throw (TimeoutException. (format "Timed out after %d milliseconds." timeout-ms))))
result))
(defmacro with-timeout
"Run BODY in a `future` and throw an exception if it fails to complete after TIMEOUT-MS."
[timeout-ms & body]
`(deref-with-timeout (future ~@body) ~timeout-ms))