| Okay - actual code is good :)) But I can't replicate the behavior. Here is the code: https://github.com/kxygk/ednless/blob/master/pathom-prectest... I didn't know what jdbc was so I just plugged in dummy values - but I think I'm matching your logic one2one Let me know if you can tweak it to have the weird results trigger At a high level I'd say a couple of things stand out. - Your resolvers take in an id and return an id.. That sets off a bunch of alarm bells for me. I wouldn't ever do that. There is no good reason to have that, even if the values are identical (if the values change then that's even more dangerous). I can't point to exactly what will go wrong, but you're exacerbating the problem of having multiple resolvers providing an input. Worse yet, in this example you are guaranteed that they are all running. So where is it going to take the ID from..? I don't even know - The :fat-key isn't doing anything here. If you wanted to do the nested request, it'd be a solid way to guarantee the fat branch is always taken: (pco/defresolver get-user-with-customer
[{:users/keys [id]}]
{::pco/output [{:fat-pack [:users/id
:users/email
:users/customer_id
:customers/id
:customers/billing_number
:customers/phone_number]}]}
(println "get-user-with-customer FAT triggered")
{:fat-pack {:users/id 66
:users/email "66@66.com"
:users/customer_id id
:customers/id id
:customers/billing_number 666
:customers/phone_number 6666}})
(def env
(pci/register [get-user
get-customer
get-user-with-customer
user-customer-bridge]))
(p.eql/process env
{:users/id 1000}
[{:fat-pack [:customers/phone_number
:users/email]}])
;;{:fat-pack {:customers/phone_number 6666, :users/email "66@66.com"}}
I think you can see you get the same results, but there is no way for it to go wrong. Admittedly here I have the EQL request "unpacking" it, but it can also be unpacked transparently from a different resolver using nested inputs. (pco/defresolver fat-eater
[{:keys [fat-pack]}]
{::pco/input[{:fat-pack [:users/id
:users/email
:users/customer_id
:customers/id
:customers/billing_number
:customers/phone_number]}]
::pco/output [:response]}
(println "fat-eater triggered")
{:response (str "yum, just ate: "
(:users/id fat-pack))})
(def env
(pci/register [get-user
get-customer
get-user-with-customer
user-customer-bridge
fat-eater]))
(p.eql/process env
{:users/id 1000}
[:response])
;; {:response "yum, just ate: 66"}
This is very explicit and about equivalent to your original thought of "why not just have explicit imperative function calls". The consumer (the `fat-eater` or the user making the EQL request) knows a priori that he wants the fat call.EDIT: At the bottom of the .clj I added a bunch of other tests for the pipeline branching (the A->B->C thing) - Adding an XXX resolver to override step B worked when "forced" with an extra input key. - Adding an YYY resolver to override step B by outputting an extra key ends up always overriding B (whether you request the new dummy output key or not!) I'm guessing here.. but it's probably b/c YYY has the same input requirements as B, but YYY outputs two keys instead of one - so it's preferred. In a way both seem useful. The first lets you use a flag to select when you take a branch. The second make you always override the branch |
You mean don't return :users/id when that's what was passed in? That makes sense, but I don't think I ran into issues with it.
> The :fat-key isn't doing anything here
For me it is. The absence of it means only one of the resolvers is run regardless of data order.
> This is very explicit and about equivalent to your original thought of "why not just have explicit imperative function calls"
I've been playing with this more today. My big thing is I want some good guidelines for writing resolvers to begin with so query time is not an exercise in auditing the whole codebase. Here are some guidelines I played around with, let me know what you think.
- When a resolver gets an entity and related data (like `get-user-with-customer`), try to pack related entities into their own nested collections. I.E. don't return `[:users/email :customer/name]`, return [:users/email {:users/customer [name]}`. This makes it less ambiguous, as putting the customer information flat with the other user data can complicate queries later on. Like if you select a user and their last order, what does `:customer/name` refer to? The customer of the user or the customer of the order?
- If a resolver is really expecting to operate on a users customer id, make that explicit in the input with `{:users/customer [id]}`.
- If you want a resolver to stay "open" and not specify where its inputs come from (just takes `:customer/id`), you can make bridges between that resolver and the entity resolvers that translate to the right keys (A bridge that turns `{:users/customer_id}` into `:get-billing-method/customer_id)
- Careful seeding multiple entities. If you feed your query the params `{:users/id 16230 :admins/id 9}`, it's not clear what the requested data is applying to (is `:roles-and-permissions` applying to the admin or the regular user?)