|
15 | 15 | (ns ^:no-doc com.walmartlabs.lacinia.introspection |
16 | 16 | "Uses the compiled schema to expose introspection." |
17 | 17 | (:require |
18 | | - [clojure.edn :as edn] |
19 | | - [clojure.java.io :as io] |
20 | | - [com.walmartlabs.lacinia.util :as util] |
21 | | - [com.walmartlabs.lacinia.internal-utils :refer [remove-keys is-internal-type-name? cond-let |
22 | | - get-nested keepv]] |
23 | | - [com.walmartlabs.lacinia.constants :as constants] |
24 | | - [clojure.string :as str] |
25 | | - [clojure.data.json :as json] |
26 | | - [clojure.spec.alpha :as s])) |
| 18 | + [clojure.edn :as edn] |
| 19 | + [clojure.java.io :as io] |
| 20 | + [com.walmartlabs.lacinia.util :as util] |
| 21 | + [com.walmartlabs.lacinia.internal-utils :refer [remove-keys is-internal-type-name? cond-let |
| 22 | + get-nested keepv]] |
| 23 | + [com.walmartlabs.lacinia.constants :as constants] |
| 24 | + [clojure.string :as str] |
| 25 | + [clojure.data.json :as json] |
| 26 | + [clojure.spec.alpha :as s])) |
27 | 27 |
|
28 | 28 | (def ^:private category->kind |
29 | 29 | {:scalar :SCALAR |
|
103 | 103 | (-> % :deprecated is-deprecated? not))) |
104 | 104 | (sort-by :field-name)))))) |
105 | 105 |
|
| 106 | +(defn ^:private convert-location |
| 107 | + "Converts a directive location keyword to its GraphQL enum value format" |
| 108 | + [location] |
| 109 | + (-> location |
| 110 | + name |
| 111 | + str/upper-case |
| 112 | + (str/replace "-" "_") |
| 113 | + keyword)) |
| 114 | + |
| 115 | +(defn ^:private convert-directive-arg |
| 116 | + "Converts a directive argument definition to introspection format" |
| 117 | + [arg-name arg-def] |
| 118 | + (let [{:keys [kind type]} (:type arg-def)] |
| 119 | + {:name (name arg-name) |
| 120 | + :description (:description arg-def) |
| 121 | + ::type-map {:kind kind :type type} |
| 122 | + ::default-value (:default-value arg-def)})) |
| 123 | + |
| 124 | +(defn ^:private convert-directive |
| 125 | + "Converts a directive definition to introspection format" |
| 126 | + [[directive-name directive-def]] |
| 127 | + {:name (name directive-name) |
| 128 | + :description (:description directive-def) |
| 129 | + :locations (vec (map convert-location (:locations directive-def))) |
| 130 | + :repeatable (get directive-def :repeatable false) |
| 131 | + :args (vec (map (fn [[arg-name arg-def]] |
| 132 | + (convert-directive-arg arg-name arg-def)) |
| 133 | + (:args directive-def)))}) |
| 134 | + |
| 135 | +(defn ^:private get-builtin-directives |
| 136 | + "Returns the built-in GraphQL directives for introspection. |
| 137 | + |
| 138 | + TODO: This duplicates some logic from parser.clj's builtin-directives. |
| 139 | + Future improvement: Consolidate all builtin directive definitions into |
| 140 | + a single source of truth shared between parser and schema compilation." |
| 141 | + [] |
| 142 | + (let [not-null-boolean {:kind :non-null |
| 143 | + :type {:kind :root |
| 144 | + :type :Boolean}}] |
| 145 | + [{:name "skip" |
| 146 | + :description "Skip the selection only when the `if` argument is true." |
| 147 | + :locations [:INLINE_FRAGMENT :FIELD :FRAGMENT_SPREAD] |
| 148 | + :repeatable false |
| 149 | + :args [{:name "if" |
| 150 | + :description "Triggering argument for skip directive." |
| 151 | + ::type-map not-null-boolean}]} |
| 152 | + {:name "include" |
| 153 | + :description "Include the selection only when the `if` argument is true." |
| 154 | + :locations [:INLINE_FRAGMENT :FIELD :FRAGMENT_SPREAD] |
| 155 | + :repeatable false |
| 156 | + :args [{:name "if" |
| 157 | + :description "Triggering argument for include directive." |
| 158 | + ::type-map not-null-boolean}]}])) |
| 159 | + |
| 160 | +(defn ^:private get-all-directives |
| 161 | + "Returns all directives (built-in and custom) for the schema." |
| 162 | + [schema] |
| 163 | + (let [directive-defs (:com.walmartlabs.lacinia.schema/directive-defs schema) |
| 164 | + custom-directives (->> directive-defs |
| 165 | + (map convert-directive))] |
| 166 | + (vec (concat (get-builtin-directives) custom-directives)))) |
| 167 | + |
106 | 168 | (defn ^:private resolve-root-schema |
107 | 169 | [context _ _] |
108 | 170 | (let [schema (get context constants/schema-key) |
|
116 | 178 | omit-subs (-> subs-root :fields empty?) |
117 | 179 | type-names' (cond-> (set type-names) |
118 | 180 | omit-mutations (disj (:mutation root)) |
119 | | - omit-subs (disj (:subscription root))) |
120 | | - not-null-boolean {:kind :non-null |
121 | | - :type {:kind :root |
122 | | - :type :Boolean}}] |
123 | | - (cond-> {:directives [{:name "skip" |
124 | | - :description "Skip the selection only when the `if` argument is true." |
125 | | - :locations [:INLINE_FRAGMENT :FIELD :FRAGMENT_SPREAD] |
126 | | - :args [{:name "if" |
127 | | - :description "Triggering argument for skip directive." |
128 | | - ::type-map not-null-boolean}]} |
129 | | - {:name "include" |
130 | | - :description "Include the selection only when the `if` argument is true." |
131 | | - :locations [:INLINE_FRAGMENT :FIELD :FRAGMENT_SPREAD] |
132 | | - :args [{:name "if" |
133 | | - :description "Triggering argument for include directive." |
134 | | - ::type-map not-null-boolean}]}] |
| 181 | + omit-subs (disj (:subscription root)))] |
| 182 | + (cond-> {:directives (get-all-directives schema) |
135 | 183 | :types (->> type-names' |
136 | 184 | sort |
137 | 185 | (map #(type-name->schema-type schema %))) |
|
143 | 191 | (not omit-subs) |
144 | 192 | (assoc :subscriptionType (schema-type schema subs-root))))) |
145 | 193 |
|
146 | | - |
147 | 194 | (defn ^:private resolve-root-type |
148 | 195 | [context args _] |
149 | 196 | (let [schema (get context constants/schema-key) |
|
210 | 257 | (when (::type-map value) |
211 | 258 | (resolve-nested-type context nil value))) |
212 | 259 |
|
| 260 | +(defn ^:private resolve-is-repeatable |
| 261 | + "Resolves isRepeatable field for a directive" |
| 262 | + [_ _ directive] |
| 263 | + (:repeatable directive)) |
| 264 | + |
213 | 265 | (defmulti emit-default-value |
214 | 266 | (fn [schema type-map value] |
215 | 267 | (cond-let |
|
296 | 348 | :interfaces resolve-interfaces |
297 | 349 | :of-type resolve-of-type |
298 | 350 | :possible-types resolve-possible-types |
299 | | - :default-value default-value}))) |
| 351 | + :default-value default-value |
| 352 | + :is-repeatable resolve-is-repeatable}))) |
0 commit comments