This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-04-19
Channels
- # announcements (37)
- # aws (6)
- # babashka (12)
- # babashka-sci-dev (16)
- # beginners (83)
- # biff (10)
- # cider (14)
- # cljdoc (26)
- # cljs-dev (20)
- # clojure (123)
- # clojure-czech (9)
- # clojure-europe (26)
- # clojure-nl (4)
- # clojure-norway (20)
- # clojure-spec (7)
- # clojure-uk (6)
- # clojured (14)
- # clojurescript (28)
- # cursive (5)
- # datalevin (8)
- # datomic (3)
- # duct (6)
- # emacs (26)
- # events (2)
- # fulcro (7)
- # gratitude (1)
- # holy-lambda (19)
- # integrant (1)
- # jobs (2)
- # leiningen (8)
- # lsp (7)
- # nyc (1)
- # pathom (70)
- # re-frame (8)
- # reagent (15)
- # releases (1)
- # sci (8)
- # shadow-cljs (117)
- # testing (5)
- # tools-deps (11)
- # vim (5)
Hi @wilkerlucio, if I have a query like this [{:user/tabs [{:tab/bookmarks [:bookmark/id]}]}]
and if the resolver that handles :bookmark/id
, or any resolver at any level in the query, throws an error, currently Pathom will handle this by utilising ::p/errors
for the failed attribute/field, and everything else will proceed as normal. In this case I don't want that, instead I want the entire query to fail and return some custom error like {:user/tabs {:error true :error-message "failed"}}
. There's a :fail-fast?
parser option but I can't seem to catch and process it. Previously I can achieve this behaviour by using mutations + ::p/process-error
, but since semantically it's wrong to use mutations to fetch data, I'm migrating everything to resolvers but currently stuck. What is the best way to achieve this? Thank you in advance.
I guess you are using lenient mode right? because on strict mode, any fail will fail everything
ah, yes
can you make a working example so we can talk over? with a full example, what happens, and what you would like to happen
I do have a working example, which is my current project but it’s bit bulky. Let me put up a small example with some resolvers.
Ok I’m not on my home PC atm so I can’t create a project fast enough. I do have some code to show you what I have.
Here is the first resolver of interest: https://github.com/aratare-jp/shinsetsu/blob/main/src/clj/shinsetsu/resolvers/tab.clj#L31
later on in my app flow, I need to fetch all the bookmarks within some tab, so I have another resolver for this: https://github.com/aratare-jp/shinsetsu/blob/main/src/clj/shinsetsu/resolvers/bookmark.clj#L29
for protected tab, I need to submit a password to fetch all the bookmarks within such tab
so if I submit the wrong password, you’d expect an error saying it’s the wrong password
but currently if I fetch a tab on some of its fields, and join with the bookmarks, when the password is incorrect only :tab/bookmarks
will have a reader-error
I want to have the all-or-nothing behaviour of P3 where if I can’t fetch the bookmarks due to wrong password, an error is returned instead
previously I could achieve this by using mutations, i.e. fetch-tabs
and fetch-bookmarks
.
if you have multiple tabs, you want to fail all the tabs, or just the tabs which the user is not allowed to access?
a way to solve this via query design is to change your query to allow for a layer controlling the access, like: [{:user/tabs [{:tab/allowed-tab [{:tab/bookmarks [:bookmark/id]}]}]}]
this way, you can fail at :tab/allowed-tab
, and them both bookmarks and all the siblings will not attempt to run
this is how I would do it in this case
you can make what you want by having a plugin wrapping around the entity process, but feels flacky, would require some very specific code (like checking for a presence of bookmarks on the result) and add unescessary overhead (since it would have to check every entity)
so I would go with :tab/allowed-tab
🙂
yeah, making everything fail given a specific circumstance in the result
but would be ugly, hehe
I’ll give allowed-tab a go. Also I tried :fail-fast?
and it did give me what I wanted, i.e. error thrown -> back track all the way to the top. But where can I catch the exception thrown?
I'm not sure if fail-fast will work as expected in this case
its intended to be used at the root, so errors are thrown immediatly (mostly useful for development environments, to see error earlier)
from the snippet I found on Pathom docs
(parser {::p/fail-fast? true}
[{:go [:key {:nest [:trigger-error :other]}
:trigger-error]}])
; => CompilerException clojure.lang.ExceptionInfo: Error triggered {:foo "bar"}, ...
I want that exception but even with a try-catch around the parser it just does the dont-mind-me-im-just-passing-by move on me didn’t get caught for some reason 😅
not sure if I get what you saying now, if you wrap on a try/catch you can't get this exception?
I think I replicated the issue, see if looks like what you see:
(ns test.demo-fail-sublings
(:require [com.wsscode.pathom.core :as p]
[com.wsscode.pathom.connect :as pc]))
(pc/defresolver user-tabs []
{:user/tabs
[{:tab/id 1
:tab/accessible? true}
{:tab/id 2
:tab/accessible? false}]})
(pc/defresolver bookmarks [{:tab/keys [id accessible?]}]
{:tab/bookmarks
(if accessible?
[{:bookmark/id id}]
(throw (ex-info "Fail here" {})))})
(pc/defresolver tab-stuff [{:tab/keys [id]}]
{:tab/name (str "Tab " id)})
(def registry
[user-tabs
bookmarks
tab-stuff])
(def parser
(p/parser
{::p/env {::p/reader [p/map-reader
pc/reader2
pc/open-ident-reader
p/env-placeholder-reader]
::p/placeholder-prefixes #{">"}}
::p/mutate pc/mutate
::p/plugins [(pc/connect-plugin {::pc/register registry})
p/error-handler-plugin
p/trace-plugin]}))
(comment
(parser {}
[{:user/tabs
[{:tab/bookmarks
[:bookmark/id]}
:tab/name]}])
; =>
; {:user/tabs [{:tab/bookmarks [{:bookmark/id 1}], :tab/name "Tab 1"}
; {:tab/bookmarks :com.wsscode.pathom.core/reader-error, :tab/name "Tab 2"}],
; :com.wsscode.pathom.core/errors {[:user/tabs 1 :tab/bookmarks] "class clojure.lang.ExceptionInfo: Fail here - {}"}}
)
solution using :tab/accessible
:
(ns test.demo-fail-sublings
(:require [com.wsscode.pathom.core :as p]
[com.wsscode.pathom.connect :as pc]))
(pc/defresolver user-tabs []
{:user/tabs
[{:tab/id 1
:tab/accessible? true}
{:tab/id 2
:tab/accessible? false}]})
(pc/defresolver bookmarks [{:tab/keys [id accessible?]}]
{:tab/bookmarks
(if accessible?
[{:bookmark/id id}]
(throw (ex-info "Fail here" {})))})
(pc/defresolver tab-accessible [env {:tab/keys [accessible?]}]
{::pc/input #{:tab/id :tab/accessible?}}
{:tab/accessible
(if accessible?
(p/entity env)
(throw (ex-info "Fail here" {})))})
(pc/defresolver tab-stuff [{:tab/keys [id]}]
{:tab/name (str "Tab " id)})
(def registry
[user-tabs
bookmarks
tab-stuff
tab-accessible])
(def parser
(p/parser
{::p/env {::p/reader [p/map-reader
pc/reader2
pc/open-ident-reader
p/env-placeholder-reader]
::p/placeholder-prefixes #{">"}}
::p/mutate pc/mutate
::p/plugins [(pc/connect-plugin {::pc/register registry})
p/error-handler-plugin
p/trace-plugin]}))
(comment
(parser {}
[{:user/tabs
[{:tab/accessible
[{:tab/bookmarks
[:bookmark/id]}
:tab/name]}]}])
; =>
; {:user/tabs [{:tab/accessible {:tab/bookmarks [{:bookmark/id 1}], :tab/name "Tab 1"}}
; {:tab/accessible :com.wsscode.pathom.core/reader-error}],
; :com.wsscode.pathom.core/errors {[:user/tabs 1 :tab/accessible] "class clojure.lang.ExceptionInfo: Fail here - {}"}}
)
OK will give it a go. May be i’ve been approaching this problem from a completely wrong angle all along.
this is all very novel stuff, we are all still figuring it out 😅
no books on information modelling though attributes out there, yet 😛
I don't think it would solve this one for you, because strict mode will break the whole query all the time, like fail-fast in this sense
no way to apply this strictness to a sub-part
then what about:
(parser {::p/fail-fast? true}
[{:user/tabs
[{:tab/bookmarks
[:bookmark/id]}
:tab/name]}])
Execution error (ExceptionInfo) at test.demo-fail-sublings/bookmarks (demo_fail_sublings.clj:16).
Fail here
what reader are you using?
so the fail-fast should work
what dont-mind-me-im-just-passing-by move on me
means?
(try
(parser {::p/fail-fast? true}
[{:user/tabs
[{:tab/bookmarks
[:bookmark/id]}
:tab/name]}])
(catch Throwable ex
(println "Gotcha" (ex-message ex))))
Gotcha Fail here
=> nil
Yep it does work as expected, but when I put the try-catch around it, the catch part didn’t get fired somehow. That’s what I meant.
full demo with catch:
(ns test.demo-fail-sublings
(:require [com.wsscode.pathom.core :as p]
[com.wsscode.pathom.connect :as pc]))
(pc/defresolver user-tabs []
{:user/tabs
[{:tab/id 1
:tab/accessible? true}
{:tab/id 2
:tab/accessible? false}]})
(pc/defresolver bookmarks [{:tab/keys [id accessible?]}]
{:tab/bookmarks
(if accessible?
[{:bookmark/id id}]
(throw (ex-info "Fail here" {})))})
(pc/defresolver tab-stuff [{:tab/keys [id]}]
{:tab/name (str "Tab " id)})
(def registry
[user-tabs
bookmarks
tab-stuff])
(def parser
(p/parser
{::p/env {::p/reader [p/map-reader
pc/reader2
pc/open-ident-reader
p/env-placeholder-reader]
::p/placeholder-prefixes #{">"}}
::p/mutate pc/mutate
::p/plugins [(pc/connect-plugin {::pc/register registry})
p/error-handler-plugin
p/trace-plugin
{::p/wrap-parser
(fn [parser]
(fn [env tx]
(tap> tx)
(parser env tx)))}]}))
(comment
(try
(parser {::p/fail-fast? true}
[{:user/tabs
[{:tab/bookmarks
[:bookmark/id]}
:tab/name]}])
(catch Throwable ex
(println "Gotcha" (ex-message ex))))
; Gotcha Fail here
; => nil
)
cause not being able to catch sounds strange
Thanks for the extensive help @wilkerlucio Will definitely try both of them 🙂
have fun 🙂