This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-03-18
Channels
- # announcements (4)
- # babashka (2)
- # beginners (72)
- # calva (2)
- # cider (18)
- # clj-kondo (30)
- # cljs-dev (2)
- # clojure (106)
- # clojure-austin (2)
- # clojure-europe (17)
- # clojure-italy (6)
- # clojure-nl (4)
- # clojure-uk (109)
- # clojurescript (31)
- # cursive (6)
- # data-science (2)
- # datomic (30)
- # events (1)
- # fulcro (20)
- # graphql (4)
- # jobs (2)
- # joker (8)
- # kaocha (2)
- # meander (31)
- # off-topic (1)
- # pathom (53)
- # re-frame (22)
- # reitit (1)
- # shadow-cljs (26)
- # specter (2)
- # sql (20)
- # testing (2)
- # tools-deps (2)
- # tree-sitter (1)
- # xtdb (20)
- # yada (6)
Hi, i don't know how to create resolvers to do the following
(pc/defresolver api-1 [{:keys [root-query] :as env} params]
{::pc/output [:api/uri]}
(let []
{:api-1/uri "uri"}))
(pc/defresolver api-items-1 [{:keys [root-query] :as env} params]
{::pc/input #{:api-1/uri}
::pc/output [:items [[:key1
:key2]]]}
(let []
{:items [{:key1 1
:key2 2}]}))
(pc/defresolver api-2 [{:keys [root-query] :as env} params]
{::pc/output [:api-2/uri]}
(let []
{:api-1/uri "uri"}))
(pc/defresolver api-items-2 [{:keys [root-query] :as env} params]
{::pc/input #{:api-2/uri}
::pc/output [:items [[:key3
:key4]]]}
(let []
{:items [{:key3 3
:key4 4}]}))
;-> this is what i would like to obtain
{:api-1/uri "uri"
:api-2/uri "uri"
:items [{:key1 1
:key2 2
:key2 3
:key2 4}]}
yes sorry i did try to reproduce an exmaple with fake code so it is a set in my code sorry
But just looking at api-1 your output does not match what is being returned. Same with api-2.
Don't know if it is a good way but this now work :
(pc/defresolver api-1 [{:keys [root-query] :as env} params]
{::pc/output [:api/uri]}
(let []
{:api-1/uri "uri"}))
(pc/defresolver api-items-1 [{:keys [root-query] :as env} params]
{::pc/input #{:api-1/uri}
::pc/output [:items [[:key1
:key2]]]}
(let []
{:items [{:key1 1
:key2 2}]}))
(pc/defresolver api-2 [{:keys [root-query] :as env} params]
{::pc/output [:api-2/uri]}
(let []
{:api-1/uri "uri"}))
(pc/defresolver api-items-2 [{:keys [root-query] :as env} params]
{::pc/input #{:api-2/uri}
::pc/output [:key3 :key4]}
(let []
{:key3 3
:key4 4}))
That looks about right, if you want to achieve:
{:api-1/uri "uri"
:api-2/uri "uri"
:items [{:key1 1
:key2 2
:key2 3
:key2 4}]}
You can probably hide some of the boilerplate (if you wish) by dropping down to pathom.connect/resolver
and generating the resolvers dynamically, instead of using the macros.(although in your psuedocode, api-items-2
needs to also have the nested :items
output)
Many thanks @pithyless if i add items in the second one it is not called
Yes it was the case With you’re recommandations I have a solution I think I’ll post it once sure it works
i have something like that work i just have to add test on api-1 . api-2 to see if some corresponding keys are requested or not since tey are systematically called. But is ok
(pc/defresolver api-sales [{:keys [root-query] :as env} {:keys [api]}]
{::pc/output [:api-1/id
:api-2/id]}
(let [api-id :
[uri label ns-path] (first (acrux/api-details api-id))]
(println "sales-resolver")
{:api-1/id 1
:api-2/id 2}))
; (println "ennv" @(:com.wsscode.pathom.core/entity env))
(pc/defresolver api-1 [{:keys [group-by] :as env} params]
{::pc/input #{:api-1/id}
::pc/output #{:items1 [:key1 :key2 :store]}}
(let []
(println "api1" params)
{:items1 [{:store :b :key1 3 :key2 4} {:store :a :key1 3 :key2 4}]}))
(pc/defresolver api-2 [{:keys [group-by] :as env} params]
{::pc/input #{:api-2/id}
::pc/output #{:items2 [:key2 :key4 :store]}}
(let []
(println "api2" params)
{:items2 [{:store :a :key3 3 :key4 4} {:store :b :key3 3 :key4 4}]}))
(defn group-by-key
[items]
(group-by :store items))
(pc/defresolver api-all [{:keys [group-by] :as env} params]
{::pc/input #{:items1
:items2}
::pc/output #{:items}}
(let [items1 (group-by-key (:items1 params))
items2 (group-by-key (:items2 params))
result (map (fn [[k v]]
(apply merge v))
(merge-with into items1 items2))]
(println "api-all")
{:items result}))
the strange thing is that i can't do a group-by inside the resolver api-all. I should do it in another function ???
I don't see why you need api-all
at all. I think the problem is that Pathom is not recognizing the output join:
::pc/output #{:items1 [:key1 :key2 :store]}
should rather be:
::pc/output [{:items [:key1 :key2 :store]}]
Also, is :api-1/id
and :api-2/id
something you want to return to the client? Or is it just a temporary variable you're passing around to resolve the other keys? IIUC, you have two apis that use different identifiers for the same "thing", but return a different subset of information about the "thing".
yes that's it i have multiples api that return differents informations and i need to consolidate these data together. (e.g. on store key)
with this code
(pc/defresolver api-1 [{:keys [root-query] :as env} {:keys [api]}]
{::pc/output [:api-1/id
:api-1/uri]}
(let []
(println "api-1")
{:api-1/id 1
:api-2/uri "uri1"}))
(pc/defresolver api-2 [{:keys [root-query] :as env} {:keys [api]}]
{::pc/output [:api-2/id
:api-2/uri]}
(let []
(println "api-2")
{:api-2/id 1
:api-2/uri "uri2"}))
(pc/defresolver api-1-items [{:keys [group-by] :as env} params]
{::pc/input #{:api-1/id}
::pc/output [{:items [:key1 :key2 :store]}]}
(let []
(println "api-1-items" params)
{:items [{:store :b :key1 1 :key2 2}
{:store :a :key1 1 :key2 2}]}))
(pc/defresolver api-2-items [{:keys [group-by] :as env} params]
{::pc/input #{:api-2/id}
::pc/output [{:items [:key3 :key4 :store]}]}
(let []
(println "api-2-items" params)
{:items [{:store :a :key3 3 :key4 4}
{:store :b :key3 3 :key4 4}]}))
only api-sales and api-2 resolvers are called.
so if i have a query like this
[{:items [:key1 :store :key3]}]
the result is:
{:items [{:key1 1, :store : :key3 :com.wsscode.pathom.core/not-found} {:key1 1, :store :a, :key3 :com.wsscode.pathom.core/not-found}]}
here is the parser
(def parser
(p/parallel-parser
{::p/env {::p/reader [p/map-reader
pc/parallel-reader
pc/ident-reader
p/env-placeholder-reader]
::p/placeholder-prefixes #{">"}}
::p/mutate pc/mutate-async
::p/plugins [(pc/connect-plugin {::pc/register app-registry})
p/error-handler-plugin
p/trace-plugin]}))
OK, I've tried to come up with a minimal case:
(pc/defresolver api-1 [env input]
{::pc/output [:api-1/id]}
{:api-1/id 1})
(pc/defresolver api-1-items1 [env input]
{::pc/input #{:api-1/id}
::pc/output [{:items [:key1]}]}
{:items [{:key1 1}]})
(pc/defresolver api-1-items2 [env input]
{::pc/input #{:api-1/id}
::pc/output [{:items [:key2]}]}
{:items [{:key2 2}]})
This does not work as I would have expected:
[:api-1/id {:items [:key1 :key2]}]
=> ignores :key1 if all 3 resolvers are in parser; will return :key1 if api-1-items2 is not in parser
Never hit this "bug", because I never modeled split-joins this way. Maybe @wilkerlucio can weigh in?@pithyless in my mind model, it's expected
I think that app-items
should return [{:items [:id]}]
then you do :id -> :key1
and :id -> key2
That would work and probably how I would have done it myself; but still surprised it does not follow the depedency-graph, but instead probably stops on first resolver that returns something with :items
. I would have expected that joins would not short-circuit like that. ¯\(ツ)/¯
So, for clarity; the suggestion @ouvasam is to perhaps model it like this:
(pc/defresolver api-items [env input]
{::pc/output [{:items [:api-1/id :api-2/id]}]}
{:items [{:api-1/id 1
:api-2/id 2}]})
(pc/defresolver api-1 [env input]
{::pc/input #{:api-1/id}
::pc/output [:key1 :key2]}
{:key1 1
:key2 2})
(pc/defresolver api-2 [env input]
{::pc/input #{:api-2/id}
::pc/output [:key3 :key4]}
{:key3 3
:key4 4})
And then you query like this:
[{:items [:key1 :key2 :key3 :key4]}]
[{:items [:api-1/id :api-2/id :key1 :key2 :key3 :key4]}]
I did something like this also but once :items is return it does not go throught the other resolvers
[{:items [:api-1/id :api-2/id :key1 :key2 :key3 :key4]}]
=> {:items [{:api-1/id 1, :api-2/id 2, :key2 2, :key1 1, :key3 3, :key4 4}]}
My problem is that api-1 api-2 return both a list of [`{:key1 1 :key2 2}]` not just a single hash-map
the more i think about this, the more the solution with a global resolvers that receive items1 and items2 to merge the results should be the safier
Do you mean modifying it for batch requests?
(pc/defresolver api-1 [env input]
{::pc/input #{:api-1/id}
::pc/output [:key1 :key2]
::pc/transform pc/transform-batch-resolver}
(mapv #(fn [{:keys [:api-1/id]}]
{:key1 1
:key2 2})
input))
Or that for a single api-1/id
there will be multiple key1
's? That I would just model by returning it as a map of key1 => [values]
in my previous example, i d othe following api-1 return the uri from a db to get the items. api-1-items, use this uri to call an api and get the a list of keys (key1, key2). With this i do a single call for api1. A s there is lot of apis that return differents keys. if some keys from a different api are requested i'd like to do only one call by api. So api-2 work the same as api-1 but with a different uri and different keys. As i watched your video about pathom. I retain "FLAT" which is exactly what i need for the rest of the app. using namespaces keys is really interesting. Hope i am clear ? Another problem i have is that, all these apis don't return the same length of list. So i should be able to consolidate data in specific manners. That's why i think a global resolver should do the trick
Flat namespaced keys for different apis is IMO a good way to approach this problem.
That is what i should do to prove that to my customer. I can make it work with this global resolver but i am new to pathom so if there is better solution to do i am interested
Please share links if you can 🙂 I only found “Domain Modeling with Datalog” https://www.youtube.com/watch?v=oo-7mN9WXTw&t=9s