This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-05-03
Channels
- # aleph (6)
- # announcements (4)
- # babashka (73)
- # beginners (117)
- # calva (25)
- # chlorine-clover (59)
- # cider (21)
- # clara (3)
- # cljdoc (8)
- # cljs-dev (54)
- # cljsrn (15)
- # clojure (65)
- # clojure-france (5)
- # clojure-spec (3)
- # clojure-uk (13)
- # clojurescript (79)
- # conf-proposals (1)
- # conjure (17)
- # core-logic (11)
- # datomic (21)
- # fulcro (82)
- # graalvm (11)
- # helix (7)
- # jobs-discuss (11)
- # joker (2)
- # juxt (3)
- # local-first-clojure (1)
- # luminus (5)
- # nrepl (61)
- # off-topic (12)
- # pathom (70)
- # re-frame (3)
- # reitit (3)
- # rum (1)
- # shadow-cljs (58)
- # sql (1)
- # tools-deps (26)
- # xtdb (3)
Ok so I got this working. Very cool! I see it calling a nested resolver multiple times but I don't understand why. Is there docs on how to read the graph shown by pathom-viz? The use case is debugging a batch query issue where is is calling an individual resolver multiple times when I think it should not.
Does anyone have a good reference set up for a pathom.core/parser for a node js environment (pathom mvn/version “2.2.31”)? I’ve switched the pathom.connect/reader2 to async-reader2 and I started getting async channels returned from my parser, but I’m wondering if there’s more to it. Any descriptions or links to a repo are appreciated!
When writing derived/computed attribute resolver, does Pathom know to deeply merge the result?
hello @matthew_lefebvre, did you changed the parser to async-parser
?
also, when switching to it, the parser itself will start returning a core async channel, reading on it will give you the full result
Ok so I got this working. Very cool! I see it calling a nested resolver multiple times but I don't understand why. Is there docs on how to read the graph shown by pathom-viz? The use case is debugging a batch query issue where is is calling an individual resolver multiple times when I think it should not.
Every single index that has a horizontal bar in this screenshot is an item without a :name
attribute & Pathom attempting to call an individual item resolver to find it.
I think my specific problem has to do with a resolver returning a list of items. Some of those items have a :name
attribute and some don't. For the ones that don't, Pathom will call a resolver that works on the individual item (perhaps hoping it could resolve the :name
). That resolver will also not return a :name
because it simply does not exist on certain items. Is there a way to tell pathom that an attribute will not become available no matter how many other resolvers it calls?
In general, how do people handle optional attributes? Do you not include them in the ::pc/output vector?
Every single index that has a horizontal bar in this screenshot is an item without a :name
attribute & Pathom attempting to call an individual item resolver to find it.
Ah, I have found the bug/missing thing that I thought worked and does not. Here's a repro:
(pc/defresolver videos-resolver
[_ _]
{::pc/input #{:user/id}
::pc/output [{:user/videos [:video/id :video/name :video/info]}]}
{:user/videos [{:video/id 1
:video/name "v1"
:video/info {:a "a"}}]})
(pc/defresolver video-extra-info
[_ _]
{::pc/input #{:video/id}
::pc/output [{:video/info [:b]}]}
{:video/info {:b "b"}})
(parser
{}
[{[:user/id 1] [{:user/videos [:video/id
{:video/info [:b]}]}]}])
=> {[:user/id 1] #:user{:videos [#:video{:id 1, :info {}}]}}
Even though my query is asking for {:video/info [:b]}
and a resolver is capable of resolving that key, it is not included in the output.
Is there a way around this?I could get hacky and do this:
(pc/defresolver video-extra-info
[env _]
{::pc/input #{:video/id}
::pc/output [:video/duration
{:video/info [:b]}]}
(swap! (::p/entity env) assoc-in [:video/info :b] "b")
{:video/duration 1
:video/info {:b "b"}})
(parser
{}
[{[:user/id 1] [{:user/videos [:video/id
:video/duration
{:video/info [:b]}]}]}])
(parser
{}
[{[:user/id 1] [{:user/videos [:video/id
:video/duration
{:video/info [:b]}]}]}])
As long as I have an additional key that would cause Pathom to hit the video-extra-info resolver, that works.To be clear, the problem is that Pathom is not letting me update a nested map that is already partially resolved.
i think the issue may be both resolvers have ::pc/output
containing :video/info
, this is working for me:
(pc/defresolver videos-resolver
[_ _]
{::pc/input #{:user/id}
::pc/output [{:user/videos [:video/id :video/name]}]}
{:user/videos [{:video/id 1 :video/name "v1"}]})
(pc/defresolver video-extra-info
[_ _]
{::pc/input #{:video/id}
::pc/output [{:video/info [:b]}]}
{:video/info {:b "b"}})
(myparser
{}
[{[:user/id 1] [{:user/videos [:video/id
:video/name
{:video/info [:b]}]}]}])
=> {[:user/id 1] #:user{:videos [#:video{:id 1, :name "v1", :info {:b "b"}}]}}
@danvingo But I want it the way I wrote it -- a resolver will compute some new attributes on a nested entity.
(pc/defresolver task-resolver [env {:keys [task/id]}]
{::pc/input #{:task/id}
::pc/output [:task/description]}
{:task/description "task Description"})
(pc/defresolver habit-resolver [env {:keys [habit/id]}]
{::pc/input #{:habit/id}
::pc/output [:habit/description {:habit/task-id [:task/id]}]}
{:habit/description "description" :habit/task-id {:task/id "task-id"}})
(myparser {}
[{[:habit/id :habit] [:habit/id :habit/description {:habit/task-id [:task/description]}]}])
;; =>
{[:habit/id :habit] #:habit{:id :habit, :description "description", :task-id #:task{:description "task Description"}}}
I think this is what you're looking for - you have to wrap the property you want to join in a map. I just ran into this as well.yep, it surprised me too, but not a big deal, i just add an alias:
(def habit-task-alias-res (pc/alias-resolver :habit/task-id :task/id))
@kenny the thign is that once a property is resolved, pathom wont try to resolve again, so you can't have a "merge" like you are thinking
would put a new whole dimension of processing, it goes against some basic heuristics of how the library works, the same attribute appearing multiple times is expected to be just different paths for the same data, not an accumulator thing
otherwise the engine would have to always process every path, that could get a out of control quick
when it sees :video/info
present, it wont try to get more of it
but, if you had something more nested, it would work, like this:
(pc/defresolver videos-resolver
[_ _]
{::pc/input #{:user/id}
::pc/output [{:user/videos [:video/id :video/name]}]}
{:user/videos [{:video/id 1 :video/name "v1"}]})
(pc/defresolver video-extra-info
[_ _]
{::pc/input #{:video/id}
::pc/output [::video/info-b]}
{:video/info-b "b"})
Hi there, if I'm using Datomic, how do I make sure that my resolver is always getting the latest value of the DB? Would I need pass down the DB connection through the context and get the value in each resolver? Or is there another way?
if you mean ‘latest’ in terms of perhaps during a mutation join, or something, you’d need to either pass in the conn and grab the db in your resolver, or write a plugin that say updates it in the env after a mutation.
as opposed to simply using the one that you pass in when you invoke the parser? such that if anything changes while the resolution is happening, you have the latest one? You’d still need either approach. my plugin experiment put the conn and the db in an atom, and refreshed the db after mutations. You could do the same to have it happen after anything. It gets a little weird because on the one hand, part of datomic’s value in many cases is having a ‘stable’ view of the database, which is really nice when you’re doing pathom/graphql/etc, but there are obvious cases, like mutation joins where you really need an update
I guess where I'm confused is that if I just pass (d/db conn)
to the parser like so:
(def parser
(p/parser
{::p/env {::p/reader [p/map-reader
pc/reader2
pc/open-ident-reader
p/env-placeholder-reader]
::p/placeholder-prefixes #{">"}
:db (d/db conn)}
::p/mutate pc/mutate
::p/plugins [(pc/connect-plugin {::pc/register my-resolvers})
p/error-handler-plugin
p/trace-plugin]}))
You can use the env-wrap-plugin:
(defn augment-env-request
[env]
(assoc env
:db db
:current-user (user/get-current-user env)
:config config))
;; add to plugins vector:
(p/env-wrap-plugin augment-env-request)
as opposed to when you create it. Or, you could do what you’re doing there, but pass in the conn. and have a plugin assoc in the db or something. but the simplest way to get going is pass it in when you invoke it
yep, the value of values, higher order funcs, and all that jazz 🙂 It’s a function that’s closed over all your setup, and acts accordingly when you invoke it
its a wildly more complex example of something like
(def add2 (partial + 2))
(add2 2) ; => 4
forgot about the env-wrap-plugin that @danvingo mentioned. Same principle. Easier way to dynamically muck with the env
that particular case is a little tricker. I’ll have to find the code, but you can basically write your own plugin and say ‘refresh’ the db value after a mutation takes place. Mine was pretty straightforward, but you then get into stuff like ‘well was that one that i cared about’ etc. There may be ways (pathom packs a ton of metadata into the env), to further optimize it, like say determining if what i’m about to resolve, is linked to a mutation that happened. It can get a little weird because you can end up with other issues, in my more simplistic attempt you could end up calling a resolver with a new db value even though what was being resolved had nothing to do with the change that was made, and then you lose the ‘stable db’ view even when it’s not necessary
I see. So would you recommend passing the connection and the latest db value (when the parser is first called) to the parser env?
Okay, cool. Thank you 🙂 I was a bit lost on how the env was actually constructed and where
Are there any example projects on GH with Pathom usage? I know it's typically used in Fulcro
yeah the default fulcro setup is probably the best way to get started as it shows how to wire in requests from the client, etc.
np and the pathom setup is there: https://github.com/fulcrologic/fulcro-template/blob/master/src/main/app/server_components/pathom.clj