This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-09-22
Channels
- # alda (1)
- # aws-lambda (23)
- # beginners (27)
- # boot (156)
- # business (2)
- # carry (4)
- # cider (1)
- # cljsjs (2)
- # cljsrn (29)
- # clojure (170)
- # clojure-austin (35)
- # clojure-czech (8)
- # clojure-dusseldorf (5)
- # clojure-italy (4)
- # clojure-nl (1)
- # clojure-quebec (2)
- # clojure-russia (45)
- # clojure-spec (49)
- # clojure-uk (12)
- # clojurescript (81)
- # component (5)
- # datomic (24)
- # devcards (26)
- # emacs (4)
- # hoplon (4)
- # jobs (1)
- # juxt (5)
- # leiningen (6)
- # luminus (14)
- # mount (26)
- # om (27)
- # om-next (2)
- # onyx (22)
- # pedestal (2)
- # planck (3)
- # proton (5)
- # re-frame (19)
- # reagent (2)
- # ring-swagger (60)
- # spacemacs (12)
- # specter (8)
- # untangled (119)
- # vim (61)
- # yada (36)
What’s the idiomatic way of adding a custom error handler to all resources? Just wrapping yada/resource
in a function that adds :responses {500 (fn [ctx] …}
?
Like this?
(defn- resource [m]
(-> (assoc m :responses {500 {:produces "text/plain"
:response (fn [ctx]
(pr-str ctx))}})
yada/resource))
not sure if this is specifically a yada issue, but I'm running into a problem when I try to use the result of a function that queries for datomic data as a response in a yada resource. almost anything I return gives...
com.fasterxml.jackson.core.JsonGenerationException: Cannot JSON encode object of class: class java.lang.NullPointerException: java.lang.NullPointerException\n\tat cheshire.generate$generate.invokeStatic(generate.clj:154)\n\tat cheshire.generate$generate.invoke(generate.clj:116)\n\tat cheshire.generate$generate.invokeStatic(generate.clj:124)\n\tat cheshire.generate$generate.invoke(generate.clj:116)\n\tat cheshire.core$generate_string.invokeStatic(core.clj:73)\n\tat cheshire.core$generate_string.invoke(core.clj:48)\n\tat yada.body$fn__19868.invokeStatic(body.clj:166)\n\tat yada.body$fn__19868.invoke(body.clj:163)\n\tat clojure.lang.MultiFn.invoke(MultiFn.java:233)\n\tat yada.body$fn__19856.invokeStatic(body.clj:89)\n\tat yada.body$fn__19856.invoke(body.clj:57)\n\tat yada.body$fn__19755$G__19748__19762.invoke(body.clj:34)\n\tat yada.handler$standard_error.invokeStatic(handler.clj:73)\n\tat yada.handler$standard_error.invoke(handler.clj:72)\n\tat yada.handler$handle_request_with_maybe_subresources$fn__21738.invoke(handler.clj:150)\n\tat manifold.deferred$catch_SINGLEQUOTE_$fn__1531.invoke(deferred.clj:962)\n\tat manifold.deferred.Listener.onError(deferred.clj:220)\n\tat manifold.deferred.Deferred$fn__1311$fn__1312.invoke(deferred.clj:400)\n\tat clojure.lang.AFn.run(AFn.java:22)\n\tat io.aleph.dirigiste.Executor$Worker$1.run(Executor.java:62)\n\tat manifold.executor$thread_factory$reify__812$f__813.invoke(executor.clj:36)\n\tat clojure.lang.AFn.run(AFn.java:22)\n\tat java.lang.Thread.run(Thread.java:745)\n
where the NullPointer is happening is beyond me, since put the result of that function is just a string
@chromaticgliss never seen that before, could you paste your resource map?
(defn get-sales-resource
[sales]
(yada/resource
{:methods
{:post
{:consumes "application/edn"
:produces "application/edn"
:parameters {:body {:store/pks clojure.lang.PersistentVector}}
;; (schema/optional-key :discount/name) String
;; (schema/optional-key :product/pk) String
;; (schema/optional-key :discount/date) java.util.Date}}
:response #(get-sales sales %)}}}))
when I was returning a string my :produces
was text/plain
similar issues though
the only way I could get a valid response was when my string was "Hello"
posted the wrong resource, initally, woops
if i call my (get-sales ...)
directly I get a sensible value returned though. it's currently reduced to a statically defined datomic query
user> (def ctx {:parameters {:body {:store/pks [50420]}}})
#'user/ctx
user> (def sales (:sales-api system))
#'user/sales
user> (s/get-sales sales ctx)
#{[285873023224729]}
user>
My function as it currently stands is just this:
(defn get-sales
"Endpoint to get sales."
[sales ctx]
(d/q '[:find ?e :where [?e :discount/name _] [?e :store/store [:store/pk 50420]]] (d/db (-> sales :stores-db :conn))))
Strange that cheshire is involved, given you're return edn. Lots of Clojure things, like Datomic entites, will need help to encode to json.
Try returning a map not a string. E.g. {:message result}
progress maybe! it yields a slightly more useful json exception
but yeah I'm not sure why cheshire is involved at all
'com.fasterxml.jackson.core.JsonGenerationException: Cannot JSON encode object of class: class java.lang.Exception: java.lang.Exception: processing rule: (q__12392 ?version-id ?tx), message: processing clause: [?publish :publish/tx ?tx], message: :db.error/not-an-entity Unable to resolve entity: :publish/tx\n\tat cheshire.generate$generate.invokeStatic(generate.clj:154)\n\tat cheshire.generate$generate.invoke(generate.clj:116)\n\tat cheshire.generate$generate.invokeStatic(generate.clj:124)\n\tat cheshire.generate$generate.invoke(generate.clj:116)\n\tat cheshire.core$generate_string.invokeStatic(core.clj:73)\n\tat cheshire.core$generate_string.invoke(core.clj:48)\n\tat yada.body$fn__19868.invokeStatic(body.clj:166)\n\tat yada.body$fn__19868.invoke(body.clj:163)\n\tat clojure.lang.MultiFn.invoke(MultiFn.java:233)\n\tat yada.body$fn__19856.invokeStatic(body.clj:89)\n\tat yada.body$fn__19856.invoke(body.clj:57)\n\tat yada.body$fn__19755$G__19748__19762.invoke(body.clj:34)\n\tat yada.handler$standard_error.invokeStatic(handler.clj:73)\n\tat yada.handler$standard_error.invoke(handler.clj:72)\n\tat yada.handler$handle_request_with_maybe_subresources$fn__21738.invoke(handler.clj:150)\n\tat manifold.deferred$catch_SINGLEQUOTE_$fn__1531.invoke(deferred.clj:962)\n\tat manifold.deferred.Listener.onError(deferred.clj:220)\n\tat manifold.deferred.Deferred$fn__1311$fn__1312.invoke(deferred.clj:400)\n\tat clojure.lang.AFn.run(AFn.java:22)\n\tat io.aleph.dirigiste.Executor$Worker$1.run(Executor.java:62)\n\tat manifold.executor$thread_factory$reify__812$f__813.invoke(executor.clj:36)\n\tat clojure.lang.AFn.run(AFn.java:22)\n\tat java.lang.Thread.run(Thread.java:745)\n'
when calling directly the function yields
user> (s/get-sales sales ctx)
{:message #{[285873023224729]}}
Do you declare you produce application/json anywhere?
not that I know of... we've got a couple other endpoints but they're all doing things in edn
something is hitting yada.body line 166, and that's a defmethod for "application/json"
ah, I see, there's an error that's thrown in the Datomic query - then yada tries to provide the error in application/json
(def error-representations (ys/representation-seq (ys/representation-set-coercer [{:media-type #{"application/json" "application/json;pretty=true;q=0.96" "text/plain;q=0.9" "text/html;q=0.8" "application/edn;q=0.6" "application/edn;pretty=true;q=0.5"} :charset charset/platform-charsets}])))
relevant code is line 61 onwards in yada.handler
then the error itself can't be converted to json
so the thing to do is wrap your query in a try/catch
ah... have to figure out what Datomic is throwing
at least in your catch block you can return some map that says 'datomic error', perhaps pr-str that datomic error so it marshalls in json ok
yada should have a mechanism to override the error types that are produces, but for now you can workaround this by ensuring all datomic access is properly wrapped in try/catch, that the errors are logged, and that a simplified error is passed back to the user-agent
respond with a 500 (if it's reached the datomic level, it's bypassed all request defences, so it's a 500 I think)
but if you log the datomic error on the server you'll be able to debug
thanks for chatting this on slack, it's revealed a potential improvement in the way yada does errors
excellent, will do. thanks so much!