/
eql.cljc
147 lines (124 loc) · 4.87 KB
/
eql.cljc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
(ns com.wsscode.pathom3.interface.async.eql
(:require
[clojure.spec.alpha :as s]
[com.fulcrologic.guardrails.core :refer [<- => >def >defn >fdef ? |]]
[com.wsscode.pathom3.connect.foreign :as pcf]
[com.wsscode.pathom3.connect.indexes :as pci]
[com.wsscode.pathom3.connect.runner :as pcr]
[com.wsscode.pathom3.connect.runner.async :as pcra]
[com.wsscode.pathom3.entity-tree :as p.ent]
[com.wsscode.pathom3.format.eql :as pf.eql]
[com.wsscode.pathom3.interface.eql :as p.eql]
[com.wsscode.pathom3.plugin :as p.plugin]
[edn-query-language.core :as eql]
[promesa.core :as p]))
(defn process-ast* [env ast]
(p/let [ent-tree* (get env ::p.ent/entity-tree* (p.ent/create-entity {}))
result (pcra/run-graph! env ast ent-tree*)]
(as-> result <>
(pf.eql/map-select-ast (p.eql/select-ast-env env) <> ast))))
(>defn process-ast
[env ast]
[::pcra/env :edn-query-language.ast/node => p/promise?]
(p/let [env env]
(p.plugin/run-with-plugins env ::p.eql/wrap-process-ast
process-ast* env ast)))
(>defn process
"Evaluate EQL expression using async runner.
This interface allows you to request a specific data shape to Pathom and get
the response as a map with all data combined.
This is efficient for large queries, given Pathom can make a plan considering
the whole request at once (different from Smart Map, which always plans for one
attribute at a time).
At minimum you need to build an index to use this.
(p.eql/process (pci/register some-resolvers)
[:eql :request])
By default, processing will start with a blank entity tree. You can override this by
sending an entity tree as the second argument in the 3-arity version of this fn:
(p.eql/process (pci/register some-resolvers)
{:eql \"initial data\"}
[:eql :request])
For more options around processing check the docs on the connect runner."
([env tx]
[::pcra/env ::eql/query => p/promise?]
(p/let [env env]
(process-ast (assoc env ::pcr/root-query tx) (eql/query->ast tx))))
([env entity tx]
[::pcra/env map? ::eql/query => p/promise?]
(assert (map? entity) "Entity data must be a map.")
(p/let [env env]
(process-ast (-> env
(assoc ::pcr/root-query tx)
(p.ent/with-entity entity))
(eql/query->ast tx)))))
(>defn process-one
"Similar to process, but returns a single value instead of a map.
This is a convenience method to read a single attribute.
Simplest usage:
```clojure
(p.eql/process-one env :foo)
```
Same as process, you can send initial data:
```clojure
(p.eql/process-one env {:data \"here\"} :foo)
```
You can also use joins and param expressions:
```clojure
(p.eql/process-one env {:join [:sub-query]})
(p.eql/process-one env '(:param {:expr \"sion\"}))
```
"
([env attr]
[(s/keys)
(s/or :prop ::eql/property
:join ::eql/join
:param ::eql/param-expr)
=> any?]
(process-one env {} attr))
([env entity attr]
[(s/keys)
map?
(s/or :prop ::eql/property
:join ::eql/join
:param ::eql/param-expr)
=> any?]
(p/let [response (process env entity [attr])]
(some-> response first val))))
(>defn boundary-interface
"Returns a function that wraps the environment. When exposing Pathom to some external
system, this is the recommended way to do it. The format here makes your API compatible
with Pathom Foreign process, which allows the integration of distributed environments.
When calling the remote interface the user can send a query or a map containing the
query and the initial entity data. This map is open and you can use as a way to extend
the API.
Boundary interface:
([env-ext request])
([request])
Request is one of:
1. An EQL request
2. A map, supported keys:
:pathom/eql
:pathom/ast
:pathom/entity
:pathom/lenient-mode?
Env ext can be either a map to merge in the original env, or a function that transforms
the env."
[env]
[::pcra/env => fn?]
(let [env' (p/let [env env] (pci/register env pcf/foreign-indexes-resolver))]
(fn boundary-interface-internal
([env-extension input]
(p/let [{:pathom/keys [eql entity ast] :as request} (p.eql/normalize-input input)
; ensure if it's a promise it gets resolved
env' env'
env-extension env-extension
env' (-> env'
(p.eql/boundary-env input)
(p.eql/extend-env env-extension)
(assoc ::source-request request))
entity' (or entity {})]
(if ast
(process-ast (p.ent/with-entity env' entity') ast)
(process env' entity' (or eql (:pathom/tx request))))))
([input]
(boundary-interface-internal nil input)))))