-
Notifications
You must be signed in to change notification settings - Fork 0
/
builder.cljs
112 lines (95 loc) · 4.05 KB
/
builder.cljs
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
(ns ^{:doc "Helper functions for easier construction of graphql queries. Most
importantly, provides the functions `field`, `query` and `mutation` (refer to
`active-graphql.examples` for usage)."}
active-graphql.builder
(:require [active-graphql.core :as g]))
(defn wrap-in-graphql-arg
"Takes a `value` in `#{int float boolean string map}` and wraps it in a
graphql value. When encountering a map, convert it into a Javascript object
and [[active-grpahl.core/stringify]] it."
[value]
(cond
;; FIXME: Nil is allow according to graphql-spec, isn't it? http://facebook.github.io/graphql/October2016/#sec-Null-Value
(nil? value) (throw (js/Error. "wrap-in-graphql-arg: expected valid value but got nil"))
(integer? value) (g/int-arg value)
(float? value) (g/float-arg value)
(boolean? value) (g/boolean-arg value)
(string? value) (g/string-arg value)
(map? value) (g/string-arg (g/stringify value))
(seq? value) (g/list-arg (map wrap-in-graphql-arg value))
:else (throw (js/Error. (str "wrap-in-graphql-arg: value of unsupported type (got " value ")")))))
(defn select
"Takes a map or seq containing pairs of keys (either keyword or string) and
values (in `#{int float boolean string}`) and returns a map prepared for
usage as a graphql map. The key is converted to a string, the val is wrapped
via `active-graphql.builder/wrap-in-graphql-arg`."
[arg-pairs]
(into {} (map (fn [[k v]]
[(name k) (wrap-in-graphql-arg v)]) arg-pairs)))
(defn project
"Takes a seq (list or vector) or `args` and converts them to
`active-graphql.core/field*`s. Each entry is either:
* a keyword or string
* a vector or `[alias field-name]` (i.e. `[:id :userId]`)
* a `active-graphql/field`
* a `active-graphql/inline-fragment`
Converts `fields` recursively."
[args]
(mapv (fn [key]
(cond
(vector? key) (let [[alias field] key]
(g/atomic-field (name alias) (name field) nil))
(g/field? key)
key
(g/inline-fragment? key)
key
:else (g/atomic-field nil (name key) nil)))
args))
(defn compile
"Takes a graphql method contructor function (either `query` or `mutation`)
and returns a grapqhl document with the fields `qs` that can be printed via
`print-document`."
[constr qs]
(g/graphql (constr "" qs)))
(defn query
"Takes an arbitrary number of `field` and wraps them in a query document."
[& qs]
(compile g/query qs))
(defn mutation
"Takes an arbitrary number of `field` and wraps them in a mutation document."
[& qs]
(compile g/mutation qs))
(defn subscription
"Takes a single `field` and wraps it in a subscription document."
[q]
(compile g/subscription q))
(defn field
"Takes the name of a graphql query or mutation `the-name` and a number of
args. `args` can be elements of type keyword, string, inline-fragments or `field`s themselves.
If `the-name` is a vector, it's first element is the alias for the query, the
second one the 'real' name."
[the-name & args]
(let [[sel args] (if (and (not (g/field? (first args)))
(not (g/inline-fragment? (first args)))
(map? (first args)))
[(first args) (rest args)]
[nil args])
[alias nom] (if (vector? the-name)
[(name (first the-name)) (name (second the-name))]
[nil (name the-name) ])]
(g/field* alias nom (select sel) (project args))))
(defn inline-fragment
[type & selections]
(g/inline-fragment* type (project selections)))
(defn request
"`request` takes a graphql `document` and returns a stringified version ready
for sending to an endpoint.
Examples:
```
(request (query (field :foo :bar)))
;; => \"\\\"query\\\": \\\"query { foo { bar }}\\\"\"
(request (query (field :foo {:obj (stringify {:name \"wilhelm\"})} :bar)))
;; => \"\\\"query\\\": \\\"query { foo(obj: \\\\\\\"foo\\\\\\\":\\\\\\\"bar\\\\\\\"}\\\\\\\") { bar }\\\"
```"
[document]
(g/create-request document))