Fork me on GitHub
#pathom
<
2020-03-18
>
ouvasam08:03:58

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}]}

ouvasam08:03:18

I am completly lost, trying lot of things but can't find a good way

ouvasam08:03:04

If someone has a tip for this ? Many Thanks

cjmurphy08:03:47

One quick thing - input (`::pc/input`) is usually a set.

ouvasam08:03:59

yes sorry i did try to reproduce an exmaple with fake code so it is a set in my code sorry

ouvasam08:03:39

My query looks like this

[:api-1/uri
 {:item
  [[:key1
    :key2
    :key3
    :key4]]}]

cjmurphy08:03:26

Did you put debug in each of them to see which ones are 'being fired'?

cjmurphy08:03:00

But just looking at api-1 your output does not match what is being returned. Same with api-2.

ouvasam08:03:49

Yes once :items is returned the other is not fired

ouvasam08:03:21

and api-2 should return api-2/uri made a typo in the fake code

ouvasam09:03:18

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}))

ouvasam09:03:50

If there is a better way to do that i'm interested Thanks,

ouvasam09:03:20

this is not the good way ...

pithyless09:03:14

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.

pithyless09:03:54

(although in your psuedocode, api-items-2 needs to also have the nested :items output)

ouvasam09:03:43

Many thanks @pithyless if i add items in the second one it is not called

pithyless10:03:00

with an eql query: [:api-1/uri :api-2/uri {:items [:key1 :key2 :key3 :key4]}] ?

ouvasam11:03:01

Yes it was the case With you’re recommandations I have a solution I think I’ll post it once sure it works

ouvasam12:03:05

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

ouvasam12:03:14

(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}))

ouvasam12:03:51

the strange thing is that i can't do a group-by inside the resolver api-all. I should do it in another function ???

ouvasam12:03:02

I use 2.3.0-alpha5

pithyless12:03:40

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]}]

pithyless12:03:22

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".

ouvasam12:03:08

these two keys should also return the url api to call

ouvasam12:03:52

When i do what you suggest, api-2 is not called

ouvasam13:03:10

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)

ouvasam13:03:15

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}]} 

ouvasam13:03:02

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]}))

pithyless14:03:27

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?

pithyless14:03:51

(Tested with pathom 2.2.30)

souenzzo14:03:02

@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

pithyless14:03:55

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. ¯\(ツ)

pithyless14:03:19

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]}]

ouvasam14:03:48

I did something like this also but once :items is return it does not go throught the other resolvers

pithyless14:03:18

^ I tested the above code in my repl.

ouvasam14:03:45

ah ok sorry i'll give it a try now

pithyless14:03:22

[{: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}]}

ouvasam14:03:02

My problem is that api-1 api-2 return both a list of [`{:key1 1 :key2 2}]` not just a single hash-map

ouvasam14:03:03

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

ouvasam14:03:23

many thanks for taking some time for this !

pithyless14:03:00

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]

ouvasam15:03:01

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

pithyless15:03:09

Flat namespaced keys for different apis is IMO a good way to approach this problem.

👍 4
ouvasam15:03:45

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

ouvasam15:03:31

I did use triple stores for other jobs and uri's were very powerful

ouvasam15:03:19

And thanks for your videos !

adamfeldman17:03:42

Please share links if you can 🙂 I only found “Domain Modeling with Datalog” https://www.youtube.com/watch?v=oo-7mN9WXTw&amp;t=9s

pithyless15:03:46

Thanks, always nice to hear someone is getting something out of them. 😊 Need to go afk for a bit, good luck with the modeling ;]

👍 4