Fork me on GitHub
Tom H.05:02:13

How can I write a resolver that puts a new key into a collection returned by another resolver? This is what I’m trying to get working:

(pco/defresolver new-key-resolver [{:keys [outside coll]}]
  {::pco/input [:outside {:coll [:inside]}]
   ::pco/output [{:coll [:new-key]}]}
  {:coll (map (fn [{:keys [inside]}]
                {:new-key (+ inside outside)})

Tom H.05:02:36

But when I try this:

(p.eql/process (pci/register [new-key-resolver])
    {:outside 2
     :coll [{:inside 1}
            {:inside 2}
            {:inside 3}]}
    [{:coll [:new-key]}])
I get
Execution error (ExceptionInfo) at com.wsscode.pathom3.connect.planner/verify-plan!* (planner.cljc:1688).
Pathom can't find a path for the following elements in the query: [:new-key] at path [:coll 0]

Tom H.05:02:09

But calling the resolver as a function works:

    {:outside 2
     :coll [{:inside 2}
            {:inside 3}
            {:inside 4}]})

; => {:coll ({:new-key 4} {:new-key 5} {:new-key 6})}


You need to feed :outside into the :coll the first time you get the coll


for example a fetch-coll-by-id resolver. Would fetch the coll and insert the :outside key into it right there

Tom H.05:02:02

but :outside is dependent on :coll , sorry forgot to add that

Tom H.05:02:41

:outside in the particular case I’m working on is a minimum value taken from the items in :coll

Tom H.05:02:06

I suppose I could do that math in the resolver that returns :coll in the first place?

Tom H.05:02:31

but they’re currently decoupled, the :coll resolver just returns ids


It’s a limitation in Pathom3. Nested resolvers can’t reach up or go backwards. You can only pass data down. This unfortunately ends up in coupling


I’m working on something to address this though.

Tom H.05:02:14

I suppose I could make :coll2 that does the work

Tom H.05:02:41

and leave the decoupled ones alone


It treats nested entities as references and tracks data with those references as a graph executes. Then you can do reverse lookups

💥 2

(deftest product-with-order-test
  (testing "Product has a relationship with the order it belongs to"
    (let [env (ordering/create-env)]
      (is (= {:com.example.order/products
              [{:com.example.product/title "Green Apple"
                {:com.example.order.price/discount 0.1
                 :com.example/id                   "order-1"}}]}
               {:com.example/id                   "order-1"
                :com.example.order.price/discount 0.1
                [{:com.example/id               "green-apple"
                  :com.example.product/title    "Green Apple"
                  :com.example.product/quantity 4
                  :com.example.product/price    5.0}]}


For example, this query starts at an order, nests into the products of the order and then nests further (with a reverse-lookup) to access discount


And the resolvers to make that work look like this:


(pco/defresolver reverse-lookup-for-products-is-an-order
  [_env input]
  {::pco/input  [{:com.example.order/_products [:com.example/id]}]
   ::pco/output [{:com.example/order [:com.example/id]}]}
  (let [{[{:com.example/keys [id]}] :com.example.order/_products} input]
    {:com.example/order {:com.example/id id}}))


I have to use a plugin and generate some resolvers to make this work

Tom H.05:02:36

wow nice, how does it know what to use as the id attribute?


All entities must have a com.example/id attribute in this example


For example, the entity we pass in originally is this:


{:com.example/id                   "order-1"
  :com.example.order.price/discount 0.1
  [{:com.example/id               "green-apple"
    :com.example.product/title    "Green Apple"
    :com.example.product/quantity 4
    :com.example.product/price    5.0}]}


This is the magic plugin that makes it work:


(p.plugin/defplugin transact-entity
   (fn [original]
     (fn [{:com.example.db/keys [schema] :as env} {:com.example/keys [id] :as out} k v]
       (let [env (update env :com.example/db
                   (fn [db]
                     (d/db-with (or db (d/empty-db schema))
                                [{:com.example/id id k v}])))]
         (original env out k v))))
   (fn [original]
     (fn [{:com.example.db/keys [schema] :as env} ast-or-graph entity-tree*]
       (let [env (update env :com.example/db
                   (fn [db]
                     (d/db-with (or db (d/empty-db schema))
         (original env ast-or-graph entity-tree*))))})


It manages state with datascript

clojure-spin 2

It’s very experimental atm.

Jakub Holý (HolyJak)11:02:20

Hi! Do you have any tips for authorization of mutations? I was thinking about using a :wrap-mutation plugin. Ideally I would be able to attach some kind of “metadata” to each mutation (e.g. :allowd-roles #{..}) and retrieve that info in the plugin to allow/deny it…


Take a look at regarding auth in pathom. You can add your own custom attributes to the config map for resolvers and mutations and access them in a variety of ways. As far as implementation goes, you could: 1. Use the feature which works for mutations too. 2. Use a plugin like ::pcr/wrap-mutate 3. Sanitise the EQL query before processing. 4. Filter the index and remove unauthorised resolvers before processing


You also mention “metadata” which I just wanted to clarify would most likely be attributes within the resolver/mutation config map, along-side ::pco/input & ::pco/output

💯 2
Jakub Holý (HolyJak)16:02:30

Thanks a lot, Caleb!


this may also be relevant. The person working on this is around here, and its not finished AFAIK but good reference maybe