pathom

:pathom: https://github.com/wilkerlucio/pathom/ & https://pathom3.wsscode.com & https://roamresearch.com/#/app/wsscode
souenzzo 2021-02-03T16:48:44.150600Z

In pathom3, there is some function that given a an env and a query it returns which inputs are required to resolve this function? for example: - env: (pci/register [(single-attr-resolver :a :b inc)]) - query: [:b] - returns: [:a]

wilkerlucio 2021-02-03T17:09:34.150800Z

that's the planner

wilkerlucio 2021-02-03T17:13:48.151Z

it wont tell you directly that, but you can create a plan and infer this data from it

✔️ 1
2021-02-03T17:24:44.154200Z

@wilkerlucio i am testing the batch resolvers on pathom3 and found a problem. If the “parent” resolver of the batch resolver returns a seq you get a 1. Unhandled java.lang.ClassCastException this is a way to reproduce it:

(def db {:contracts {"contract-1" {::contract/id   1
                               ::contract/code "contract-1"
                               ::contract/name "the name"}}
         :positions {1 '({::position/units  10
                         ::instrument/fund {::fund/id 1}}
                        {::position/units  20
                         ::instrument/fund {::fund/id 2}})}
         :funds     {1 {::fund/code "fund 1"}
                     2 {::fund/code "fund 2"}}})

(pco/defresolver contract-resolver [{::contract/keys [code]}]
  {::pco/output [::contract/id ::contract/code ::contract/name]}
  (get-in db [:contracts code]))

(pco/defresolver position-resolver [{::contract/keys [id]}]
  {::pco/output [{::contract/positions [::position/units {::instrument/fund [::fund/id]}]} ]}
  {::contract/positions (get-in db [:positions id])})

(pco/defresolver fund-resolver [input]
  {::pco/input [::fund/id]
   ::pco/output [::fund/code]
   ::pco/batch? true}
  (clojure.pprint/pprint
   {:in input
    :res (map #(get-in db [:funds (::fund/id %)])
         input)})

  (map #(get-in db [:funds (::fund/id %)])
       input))

(p.eql/process
  (pci/register [contract-resolver position-resolver fund-resolver])
  [{[::contract/code "contract-1"] [::contract/name {::contract/positions [::position/units {::instrument/fund [::fund/code]}]}]}])
everything works perfectly if db is changed to
(def db {:contracts {"contract-1" {::contract/id   1
                               ::contract/code "contract-1"
                               ::contract/name "the name"}}
          ;;notice that positions are now a vec
         :positions {1 [{::position/units  10
                         ::instrument/fund {::fund/id 1}}
                        {::position/units  20
                         ::instrument/fund {::fund/id 2}}]}
         :funds     {1 {::fund/code "fund 1"}
                     2 {::fund/code "fund 2"}}})

wilkerlucio 2021-02-03T17:26:36.155400Z

@jmayaalv yes, that's a limitation, I think the right approach at this time is to give a better error message

wilkerlucio 2021-02-03T17:27:12.156200Z

the issue is that batch needs to be able to navigate, it would be possible to make lists work, but at some processing cost (because I can't update-in a list, but I can in a vector)

wilkerlucio 2021-02-03T17:27:30.156400Z

can you open an issue for it?

2021-02-03T17:27:46.156600Z

got it, yes will do. Thanks a lot!

markaddleman 2021-02-03T18:59:35.158200Z

I'm struggling to surface resolver exceptions in Pathom3. What attr should I provide to pcrs/get-attribute-error to get the internal error exception?

(pco/defresolver throws [{:a/keys [input]}]
  (throw (Exception. "Internal error"))
  {:a/output (str "echoing " input)})

(-> (p.eql/process (-> [throws] (pci/register))
                   [{[:a/input "hello"] [:a/output]}])
    (meta)
    ::pcr/run-stats
    (psm/smart-run-stats)
    (pcrs/get-attribute-error :a/output))   

markaddleman 2021-02-03T19:00:50.159Z

Under the latest Pathom3 commit, get-attribute-error itself returns

{:com.wsscode.pathom3.connect.runner.stats/node-error-type :com.wsscode.pathom3.connect.runner.stats/node-error-type-unreachable
    :com.wsscode.pathom3.connect.runner/node-error #reveal/error{:via [{:type clojure.lang.ExceptionInfo
                                                                        :message "Can't find a path for :a/output"
                                                                        :data {:com.wsscode.pathom3.attribute/attribute :a/output}
                                                                        :at [com.wsscode.pathom3.connect.runner.stats$attribute_error__30270 invokeStatic "stats.cljc" 73]}]
                                                                 :trace [[com.wsscode.pathom3.connect.runner.stats$attribute_error__30270 invokeStatic "stats.cljc" 73]
                                                                         [com.wsscode.pathom3.connect.runner.stats$attribute_error__30270 invoke "stats.cljc" 51]...}

wilkerlucio 2021-02-03T19:12:08.160Z

hello @markaddleman, the issue is that you are looking for the error in the wrong map

wilkerlucio 2021-02-03T19:12:21.160300Z

errors and stats are per entity (each map in the response gets its own)

wilkerlucio 2021-02-03T19:12:38.160500Z

this should work:

(-> (p.eql/process (-> [throws] (pci/register))
      [{[:a/input "hello"] [:a/output]}])
    (get [:a/input "hello"])
    (meta)
    ::pcr/run-stats
    (psm/smart-run-stats)
    (pcrs/get-attribute-error :a/output))

markaddleman 2021-02-03T19:12:59.160800Z

ah ha

markaddleman 2021-02-03T19:14:29.161100Z

Thx!

markaddleman 2021-02-03T19:15:07.161800Z

One more thing: Is there a convenient & performant way of checking if any attributes have errors?

wilkerlucio 2021-02-03T19:15:32.162800Z

yes, in the run stats, there is a key that says all the nodes with errors

markaddleman 2021-02-03T19:15:38.163100Z

My desired exception handling policy is to throw an exception if any resolver throws so I'm writing my own query function to handle this

markaddleman 2021-02-03T19:15:47.163500Z

perfect

wilkerlucio 2021-02-03T19:16:02.163700Z

I think you can learn a lot about checking the sources for the attribute-errors-plugin: https://github.com/wilkerlucio/pathom3/blob/master/src/main/com/wsscode/pathom3/connect/built_in/plugins.cljc#L10-L40

markaddleman 2021-02-03T19:16:32.164Z

perfect! Thank you

wilkerlucio 2021-02-03T20:13:42.164100Z

one thing to consider is that its possible to have a failed node, but still a fully successful response, this can happen if there are multiple paths for something (a OR node), some path fails, other path succeed

wilkerlucio 2021-02-03T20:13:51.164300Z

in this case, you will have an error, but still a complete response

markaddleman 2021-02-03T20:36:35.164500Z

Understood. In my case, there is no OR but I'll keep this is mind

markaddleman 2021-02-03T23:24:31.166600Z

Wondering what you think about resolver input maps being smart-maps? My app gets a client query, processed by pathom as EQL and many times it is convenient to pass the input around to different functions that might need to compute things for which resolvers already exist.

eoliphant 2021-02-09T19:57:44.213500Z

i’ve been struggling with that as well lol.

markaddleman 2021-02-09T20:05:14.213900Z

Here is my latest thought: Because Cursive does a much better job of navigating among function defs and function calls, my sanity prefers function calls over smart-map resolvers and keys. It's just much easier to reverse engineer what's going on. Perhaps when I become more familiar with Pathom Viz and other debugging tools, I'll change my mind.

markaddleman 2021-02-09T20:05:49.214100Z

That's not to say I don't think smart-maps & resolvers have their place... They definitely do. It's just that when I don't have a compelling reason one way or the other, I'll stick to fns