├── test └── lacinia_gen │ ├── cljs_tests.cljs │ ├── cljs_runner.cljs │ ├── query_test.cljc │ └── core_test.cljc ├── src └── lacinia_gen │ ├── def.clj │ ├── core.cljc │ └── query.clj ├── .gitignore ├── project.clj └── README.md /test/lacinia_gen/cljs_tests.cljs: -------------------------------------------------------------------------------- 1 | (ns lacinia-gen.cljs-tests 2 | (:require [lacinia-gen.core-test] 3 | [lacinia-gen.query-test])) 4 | -------------------------------------------------------------------------------- /test/lacinia_gen/cljs_runner.cljs: -------------------------------------------------------------------------------- 1 | (ns lacinia-gen.cljs-runner 2 | (:require [cljs.test :as test] 3 | [doo.runner :refer-macros [doo-all-tests]] 4 | [lacinia-gen.cljs-tests])) 5 | 6 | (doo-all-tests #"^lacinia-gen.*-test$") 7 | -------------------------------------------------------------------------------- /src/lacinia_gen/def.clj: -------------------------------------------------------------------------------- 1 | (ns lacinia-gen.def) 2 | 3 | (defmacro defquery [name query] 4 | `(def ~(vary-meta name assoc ::source query) ~query)) 5 | 6 | (defmacro defschema [name schema] 7 | `(def ~(vary-meta name assoc ::source schema) ~schema)) 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/pom.xml 2 | **/pom.xml.asc 3 | **/*jar 4 | /lib/ 5 | /**/classes/ 6 | /**/target/ 7 | /**/checkouts/ 8 | **/.lein-deps-sum 9 | **/.lein-repl-history 10 | **/.lein-plugins/ 11 | **/.lein-failures 12 | **/.nrepl-port 13 | **/.cljs_rhino_repl/ 14 | **/.cljs_nashorn_repl/ 15 | **/nashorn_code_cache/ 16 | /**/out/ -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject lacinia-gen "0.1.1-SNAPSHOT" 2 | :description "Generators for GraphQL" 3 | :url "https://github.com/oliyh/lacinia-gen" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.9.0"] 7 | [org.clojure/test.check "0.10.0-alpha2"] 8 | [com.walmartlabs/lacinia "0.27.0"] 9 | [org.clojure/clojurescript "1.10.312"]] 10 | :release-tasks [["vcs" "assert-committed"] 11 | ["change" "version" "leiningen.release/bump-version" "release"] 12 | ["vcs" "commit"] 13 | ["vcs" "tag" "--no-sign"] 14 | ["deploy" "clojars"] 15 | ["change" "version" "leiningen.release/bump-version"] 16 | ["vcs" "commit"] 17 | ["vcs" "push"]] 18 | :plugins [[lein-cljsbuild "1.1.7"] 19 | [lein-doo "0.1.8"]] 20 | 21 | :profiles {:dev {:dependencies [[lein-doo "0.1.10"]]}} 22 | 23 | :cljsbuild {:builds [{:id "test" 24 | :source-paths ["src" "test"] 25 | :compiler {:output-to "target/unit-test.js" 26 | :main "lacinia-gen.cljs-runner" 27 | :optimizations :whitespace 28 | :parallel-build true}}]} 29 | :aliases {"test" ["do" ["clean"] ["test"] ["doo" "nashorn" "test" "once"]]}) 30 | -------------------------------------------------------------------------------- /test/lacinia_gen/query_test.cljc: -------------------------------------------------------------------------------- 1 | (ns lacinia-gen.query-test 2 | #?(:clj (:require [lacinia-gen.query :as query] 3 | [lacinia-gen.def :refer [defquery defschema]] 4 | [clojure.test :refer [deftest testing is]]) 5 | :cljs (:require [cljs.test :refer-macros [deftest testing is]])) 6 | #?(:cljs (:require-macros [lacinia-gen.query :as query] 7 | [lacinia-gen.def :refer [defquery defschema]]))) 8 | 9 | (defschema schema '{:enums {:position {:values [:goalkeeper :defence :attack]}} 10 | :objects {:team {:fields {:wins {:type Int} 11 | :losses {:type Int} 12 | :players {:type (list :player)}}} 13 | :player {:fields {:name {:type String 14 | :resolve :resolve-leaf} 15 | :age {:type Int} 16 | :position {:type :position}}}} 17 | :queries {:teams {:type (list :team) 18 | :resolve :resolve-teams}}}) 19 | 20 | #?(:clj 21 | (deftest generate-fn-test 22 | (let [f (query/generate-fn schema {})] 23 | 24 | (let [data (:data (f "query { teams { wins players { name } } }" {}))] 25 | (is (:teams data)) 26 | (is (-> data :teams count pos?)) 27 | (is (-> data :teams first :wins int?)) 28 | (is (->> data :teams (keep (comp not-empty :players)) first count pos?)) 29 | (is (->> data :teams (keep (comp not-empty :players)) ffirst :name string?)))))) 30 | 31 | (def literal-macro-data 32 | (query/generate-query {:objects {:team {:fields {:wins {:type Int}}}} 33 | :queries {:teams {:type (list :team) 34 | :resolve :resolve-teams}}} 35 | "query { teams { wins } }" 36 | {} 37 | {})) 38 | 39 | (deftest generate-query-test 40 | (is literal-macro-data) 41 | (let [data (:data literal-macro-data)] 42 | (is (:teams data)) 43 | (is (-> data :teams count pos?)) 44 | (is (-> data :teams first :wins int?)))) 45 | 46 | (defquery query "query { teams { wins players { name } } }") 47 | 48 | (def resolving-macro-data 49 | (query/generate-query* schema query {} {})) 50 | 51 | (deftest generate-query*-test 52 | (is resolving-macro-data) 53 | (let [data (:data resolving-macro-data)] 54 | (is (:teams data)) 55 | (is (-> data :teams count pos?)) 56 | (is (-> data :teams first :wins int?)))) 57 | -------------------------------------------------------------------------------- /src/lacinia_gen/core.cljc: -------------------------------------------------------------------------------- 1 | (ns lacinia-gen.core 2 | (:require [clojure.test.check.generators :as gen])) 3 | 4 | (def ^:private base-scalars 5 | {'String gen/string 6 | 'Float gen/double 7 | 'Int gen/int 8 | 'Boolean gen/boolean}) 9 | 10 | (defn- enum [values] 11 | (let [g (gen/elements values)] 12 | (constantly g))) 13 | 14 | (defn- field [depth width scalars all-gens type] 15 | (cond 16 | ;; sub object 17 | (and (keyword? type) 18 | (contains? all-gens type)) 19 | ((get all-gens type) depth width scalars all-gens) 20 | 21 | ;; list of type 22 | (and (list? type) 23 | (= 'list (first type))) 24 | (let [g (gen/list (field depth width scalars all-gens (second type)))] 25 | (if-let [w (get width (second type))] 26 | (gen/resize w g) 27 | g)) 28 | 29 | ;; non-nullable 30 | (and (list? type) 31 | (= 'non-null (first type))) 32 | (gen/such-that (complement nil?) (field depth width scalars all-gens (second type))) 33 | 34 | ;; scalar 35 | :else 36 | (scalars type))) 37 | 38 | (defn- object [fields] 39 | (fn [depth width scalars all-gens] 40 | (let [fields (keep (fn [[k {:keys [type]}]] 41 | (when (pos? (get depth k 1)) 42 | (gen/fmap (fn [v] 43 | {k v}) 44 | (field (update depth k (fnil dec 1)) width scalars all-gens type)))) 45 | fields)] 46 | (gen/fmap 47 | (fn [kvs] 48 | (apply merge kvs)) 49 | (apply gen/tuple fields))))) 50 | 51 | (defn- make-gens [enums objects queries] 52 | (merge (reduce-kv (fn [all-gens k {:keys [fields]}] 53 | (assoc all-gens k (object fields))) 54 | {} 55 | objects) 56 | (reduce-kv (fn [all-gens k {:keys [values]}] 57 | (assoc all-gens k (enum values))) 58 | {} 59 | enums) 60 | (reduce-kv (fn [all-gens k {:keys [type]}] 61 | (assoc all-gens k (fn [depth width scalars all-gens] 62 | (field depth width scalars all-gens type)))) 63 | {} 64 | queries))) 65 | 66 | (defn generator 67 | "Given a lacinia schema returns a function which takes an object key as an argument 68 | and returns a generator for that object, e.g. 69 | 70 | (let [g (generator my-lacinia-schema)] 71 | (gen/sample (g :my-object) 10)) 72 | 73 | Options: 74 | - depth 75 | A map of object keys to the depth they should recurse to in cyclical graphs 76 | e.g. {:parent 1 77 | :child 2} 78 | 79 | - width 80 | A map of object keys to the maximum number of items that should be present in lists of that object 81 | e.g. {:listed-object 3} 82 | 83 | - scalars 84 | A map of custom scalar types to the generators to be used for them. 85 | e.g. {:DateTime (gen/...)} 86 | " 87 | [schema & [opts]] 88 | (let [{:keys [depth width scalars] 89 | :or {depth {} 90 | width {} 91 | scalars {}}} opts 92 | all-gens (make-gens (:enums schema) (:objects schema) (:queries schema)) 93 | scalars (merge base-scalars scalars)] 94 | (fn [type] 95 | ((get all-gens type) depth width scalars all-gens)))) 96 | -------------------------------------------------------------------------------- /src/lacinia_gen/query.clj: -------------------------------------------------------------------------------- 1 | (ns lacinia-gen.query 2 | (:require [lacinia-gen.core :as lgen] 3 | [lacinia-gen.def :as def] 4 | [com.walmartlabs.lacinia :as lacinia] 5 | [com.walmartlabs.lacinia.schema :as schema] 6 | [com.walmartlabs.lacinia.util :refer [attach-resolvers attach-streamers]] 7 | [clojure.test.check.generators :as g] 8 | [clojure.string :as string] 9 | [cljs.analyzer :as cljs])) 10 | 11 | (defn- resolver-keys [objects] 12 | (->> objects 13 | vals 14 | (map :fields) 15 | (mapcat vals) 16 | (keep :resolve) 17 | set)) 18 | 19 | (defn- root-resolver-keys [root] 20 | (->> root vals (keep :resolve))) 21 | 22 | (defn- generating-resolver [context arguments parent] 23 | (let [generator (:gen context) 24 | {:keys [field field-definition leaf?]} (::lacinia/selection context) 25 | many? (= :list (get-in field-definition [:type :kind]))] 26 | (cond 27 | leaf? (get parent field) 28 | many? (g/sample (generator (get-in field-definition [:type :type :type])) 5) 29 | :else (g/generate (generator field))))) 30 | 31 | (defn- resolver-map [{:keys [objects queries subscriptions]}] 32 | (let [resolver-keys (into (resolver-keys objects) 33 | (mapcat root-resolver-keys [queries subscriptions]))] 34 | (not-empty (zipmap resolver-keys (repeat generating-resolver))))) 35 | 36 | (defn- streamer-map [schema] 37 | (let [streamer-keys (->> schema :subscriptions vals (keep :stream))] 38 | (not-empty (zipmap streamer-keys (repeat (fn [& args])))))) 39 | 40 | (defn- compile-generating-schema [schema] 41 | (let [resolvers (resolver-map schema) 42 | streamers (streamer-map schema)] 43 | (cond-> schema 44 | resolvers (attach-resolvers resolvers) 45 | streamers (attach-streamers streamers) 46 | :always schema/compile))) 47 | 48 | (defn generate-fn 49 | "Call with an (uncompiled) lacinia schema to return a function which 50 | can generate data for an arbitrary query 51 | 52 | Opts are as per lacinia-gen.core/generator 53 | 54 | e.g. (let [f (generate-fn {:objects {:team ... }})] 55 | (f \"query { teams { teamName } }\" {}))" 56 | [schema opts] 57 | (let [s (compile-generating-schema schema) 58 | gen (lgen/generator schema opts)] 59 | (fn [query variables] 60 | (lacinia/execute s query variables {:gen gen})))) 61 | 62 | (defmacro generate-query 63 | "For use with literal schema, query and variables arguments 64 | 65 | Opts are as per lacinia-gen.core/generator 66 | 67 | e.g. (generate-query {:objects {:team ... }} 68 | \"query { teams { teamName } }\" 69 | {} 70 | {})" 71 | [schema query variables opts] 72 | `'~((generate-fn schema opts) 73 | (->> (string/replace query #"^query|subscription" "") 74 | (str "query ")) 75 | variables)) 76 | 77 | (defn- maybe-resolve [cljs-env s] 78 | (if (symbol? s) 79 | (if-let [v (resolve s)] 80 | @v 81 | (when cljs-env 82 | (let [result (-> (cljs/resolve-var cljs-env s (cljs/confirm-var-exists-throw)) 83 | :meta 84 | ::def/source)] 85 | (if (and (list? result) (= 'quote (first result))) 86 | (second result) 87 | result)))) 88 | s)) 89 | 90 | (defmacro generate-query* 91 | "For use from Clojurescript where arguments are symbols that need resolving. 92 | 93 | N.B. you must use defquery to define queries in Clojurescript as the value 94 | cannot be resolved from a normal var. 95 | 96 | Opts are as per lacinia-gen.core/generator 97 | 98 | e.g. 99 | (require '[my.project.graphql :refer [schema]]) 100 | (defquery team-query \"query { teams { teamName } }\") 101 | 102 | (generate-query* schema team-query {} {})" 103 | [schema query variables opts] 104 | (let [cljs-env &env 105 | maybe-resolve (partial maybe-resolve cljs-env)] 106 | `(generate-query ~(maybe-resolve schema) 107 | ~(maybe-resolve query) 108 | ~(maybe-resolve variables) 109 | ~(maybe-resolve opts)))) 110 | -------------------------------------------------------------------------------- /test/lacinia_gen/core_test.cljc: -------------------------------------------------------------------------------- 1 | (ns lacinia-gen.core-test 2 | (:require #?(:clj [clojure.test :refer [deftest is testing]] 3 | :cljs [cljs.test :refer [deftest is testing]]) 4 | [lacinia-gen.core :as lgen] 5 | [clojure.test.check.generators :as g] 6 | [clojure.test.check.generators :as gen])) 7 | 8 | (defn- position? [position] 9 | (contains? #{:goalkeeper :defence :attack} position)) 10 | 11 | (defn- player? [player] 12 | (and (string? (:name player)) 13 | (integer? (:age player)) 14 | (position? (:position player)))) 15 | 16 | (defn team? [team] 17 | (and (integer? (:wins team)) 18 | (integer? (:losses team)) 19 | (every? player? (:players team)))) 20 | 21 | (deftest basic-test 22 | (let [schema '{:enums {:position {:values [:goalkeeper :defence :attack]}} 23 | :objects {:team {:fields {:wins {:type Int} 24 | :losses {:type Int} 25 | :players {:type (list :player)}}} 26 | :player {:fields {:name {:type String} 27 | :age {:type Int} 28 | :position {:type :position}}}} 29 | :queries {:teams {:type (list :team)}}}] 30 | 31 | (let [gen (lgen/generator schema)] 32 | 33 | (testing "can generate enums" 34 | (let [v (g/sample (gen :position) 10)] 35 | (is (every? position? v)))) 36 | 37 | (testing "can generate objects" 38 | (let [v (g/sample (gen :player) 10)] 39 | (is (every? player? v)))) 40 | 41 | (testing "can generate composite objects" 42 | (let [v (g/sample (gen :team) 10)] 43 | (is (every? team? v)))) 44 | 45 | (testing "can generate query roots" 46 | (let [v (g/generate (gen :teams))] 47 | (is v)))))) 48 | 49 | (deftest recursion-test 50 | (let [schema {:objects {:team {:fields {:name {:type 'String} 51 | :players {:type '(list :player)}}} 52 | :player {:fields {:name {:type 'String} 53 | :team {:type :team}}}}}] 54 | 55 | (testing "defaults to one level" 56 | (let [gen (lgen/generator schema)] 57 | 58 | (let [v (g/sample (gen :team) 10)] 59 | (is (every? (fn [team] 60 | (and (:players team) 61 | (or (empty? (:players team)) 62 | (:team (first (:players team)))) 63 | ;; team has players and those players have a team, but no deeper 64 | (not (-> team :players first :team :players)))) 65 | v))) 66 | 67 | (let [v (g/sample (gen :player) 10)] 68 | (is (every? (fn [player] 69 | (and (:team player) 70 | (or (empty? (:players (:team player))) 71 | (:players (:team player))) 72 | (not (-> player :team :players first :team)))) 73 | v))))) 74 | 75 | (testing "can override recursion depth" 76 | 77 | (testing "no recursion" 78 | (let [gen (lgen/generator schema {:depth {:team 0}})] 79 | 80 | (let [v (g/sample (gen :team) 10)] 81 | (is (every? (fn [team] 82 | (and (:players team) 83 | (or (empty? (:players team)) 84 | (not (:team (first (:players team))))))) 85 | v))))) 86 | 87 | (testing "more recursion" 88 | (let [gen (lgen/generator schema {:depth {:team 2 89 | :players 2}})] 90 | 91 | (let [v (last (g/sample (gen :team) 10))] 92 | (is (-> v :players first :team :players first :team)))))))) 93 | 94 | (deftest width-test 95 | (let [schema {:objects {:team {:fields {:name {:type 'String} 96 | :players {:type '(list :player)}}} 97 | :player {:fields {:name {:type 'String} 98 | :team {:type :team}}}}}] 99 | 100 | (testing "limits width to the value provided" 101 | (let [gen (lgen/generator schema {:width {:player 2}})] 102 | 103 | (let [v (g/sample (gen :team) 10)] 104 | (is (every? (fn [team] (< (count (:players team)) 3)) v))))))) 105 | 106 | 107 | (deftest custom-scalar-test 108 | (let [schema {:scalars {:Custom {:parse :parse-custom 109 | :serialize :serialize-custom}} 110 | :objects {:obj-with-custom 111 | {:fields 112 | {:custom {:type :Custom} 113 | :custom-list {:type '(list :Custom)}}}}}] 114 | 115 | (testing "single custom value" 116 | (let [gen (lgen/generator schema {:scalars {:Custom gen/int}}) 117 | v (g/sample (gen :obj-with-custom) 10)] 118 | (is (every? int? (map :custom v))))) 119 | 120 | (testing "list of custom values" 121 | (let [gen (lgen/generator schema {:scalars {:Custom gen/int}}) 122 | v (g/sample (gen :obj-with-custom) 10)] 123 | (is (every? #(every? int? %) (map :custom-list v))))) )) 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lacinia-gen 2 | 3 | `lacinia-gen` lets you generate GraphQL responses using your [lacinia](https://github.com/walmartlabs/lacinia) schema 4 | and GraphQL queries, allowing you to make your tests more rigorous in both Clojure and Clojurescript. 5 | 6 | [![Clojars Project](https://img.shields.io/clojars/v/lacinia-gen.svg)](https://clojars.org/lacinia-gen) 7 | 8 | ## Usage 9 | 10 | There are two main ways to use `lacinia-gen`: 11 | - [Full graph](#full-graph) generates the entire graph from any entry point 12 | - [Query result](#query-result) generates a response for a particular query 13 | 14 | ### Full graph 15 | 16 | You can create a generator for a full graph from any root by using the `generator` function in 17 | `lacinia-gen.core`. This resolves all reachable nodes in the graph up to the desired recursion depth. 18 | 19 | ```clojure 20 | (require '[lacinia-gen.core :as lgen]) 21 | (require '[clojure.test.check.generators :as g]) 22 | 23 | (let [schema {:enums {:position {:values [:goalkeeper :defence :attack]}} 24 | :objects {:team {:fields {:wins {:type 'Int} 25 | :losses {:type 'Int} 26 | :players {:type '(list :player)}}} 27 | :player {:fields {:name {:type 'String} 28 | :age {:type 'Int} 29 | :position {:type :position}}}}}] 30 | 31 | (let [gen (lgen/generator schema)] 32 | 33 | (g/sample (gen :position) 5) 34 | ;; => (:defence :goalkeeper :defence :goalkeeper :goalkeeper) 35 | 36 | (g/sample (gen :player) 5) 37 | ;; => ({:name "", :age 0, :position :attack} 38 | {:name "", :age 0, :position :attack} 39 | {:name "", :age 1, :position :attack} 40 | {:name "–m", :age -2, :position :defence} 41 | {:name "¤", :age 4, :position :defence}) 42 | 43 | (g/sample (gen :team) 5) 44 | ;; => ({:wins 0, :losses 0, :players ()} 45 | {:wins 1, 46 | :losses 1, 47 | :players ({:name "", :age -1, :position :defence})} 48 | {:wins 1, 49 | :losses 2, 50 | :players 51 | ({:name "", :age 1, :position :attack} 52 | {:name "q", :age -2, :position :attack})} 53 | {:wins -3, :losses 1, :players ()} 54 | {:wins 0, 55 | :losses -3, 56 | :players 57 | ({:name "‘ßéÅ", :age 3, :position :attack} 58 | {:name "", :age -4, :position :defence})}))) 59 | ``` 60 | 61 | It supports recursive graphs, so the following works too: 62 | 63 | ```clojure 64 | (let [schema {:objects {:team {:fields {:name {:type 'String} 65 | :players {:type '(list :player)}}} 66 | :player {:fields {:name {:type 'String} 67 | :team {:type :team}}}}}] 68 | 69 | (let [gen (lgen/generator schema)] 70 | (g/sample (gen :team) 5))) 71 | 72 | ;; => {:name "ö–", 73 | :players 74 | ({:name "³2", 75 | :team 76 | {:name "ºN", 77 | :players 78 | ({:name "Ïâ¦", :team {:name "¤¼J"}} 79 | {:name "o", :team {:name "æ‚8‹"}} 80 | {:name "/ç", :team {:name "ãL"}} 81 | {:name "éíª6", :team {:name "v‡"}})}} 82 | ``` 83 | 84 | If you want to limit the depth to which certain objects recurse, you can do so with the following option: 85 | 86 | ```clojure 87 | (lgen/generator schema {:depth {:team 0}}) 88 | ``` 89 | 90 | This will ensure that `:team` only appears once in the graph; the team will have players, but it prevents the players from having a team. 91 | The default value for all objects is 1, meaning each will recur once (a team will have players which have a team which has players, but no further). 92 | You can set any integer value you wish. 93 | 94 | If you want to limit the number of items in lists, you can do so with the following option: 95 | 96 | ```clojure 97 | (lgen/generator schema {:width {:player 2}}) 98 | ``` 99 | 100 | This will ensure that lists of `:player` will have a maximum size of 2. 101 | 102 | ### Query result 103 | 104 | If you have a GraphQL query and wish to generate data for its result, you can use `lacinia-gen.query`. 105 | 106 | ```clojure 107 | (require '[lacinia-gen.query :as query]) 108 | 109 | (def schema '{:enums {:position {:values [:goalkeeper :defence :attack]}} 110 | :objects {:team {:fields {:wins {:type Int} 111 | :losses {:type Int} 112 | :players {:type (list :player)}}} 113 | :player {:fields {:name {:type String} 114 | :age {:type Int} 115 | :position {:type :position}}}} 116 | :queries {:teams {:type (list :team) 117 | :resolve :resolve-teams}}}) 118 | 119 | (let [f (query/generate-fn schema {})] 120 | (f "query { teams { wins players { name } } }" {})) 121 | 122 | ;; => {:data 123 | {:teams 124 | ({:wins 0, :players ()} 125 | {:wins 0, :players ({:name ""})} 126 | {:wins 1, :players ()} 127 | {:wins 0, :players ({:name "÷ "} {:name "¢"})} 128 | {:wins 1, :players ()})}} 129 | ``` 130 | 131 | Currently the queries are interpreted by Lacinia and as such require the JVM. This means 132 | `generate-fn` cannot be used from Clojurescript. The two macros `generate-query` and 133 | `generate-query*` may be used in Clojurescript and will evaluate to the generated result 134 | of the query. 135 | 136 | ```clojure 137 | (ns my.test 138 | (:require-macros [lacinia-gen.query :refer [generate-data*]])) 139 | 140 | (def schema '{:objects {:team {:fields {:wins {:type Int} 141 | :losses {:type Int}}}} 142 | :queries {:teams {:type (list :team) 143 | :resolve :resolve-teams}}}) 144 | 145 | (def query "query { teams { wins } }") 146 | 147 | (def data (generate-data* schema query {} {})) 148 | 149 | data 150 | ;; => {:data 151 | {:teams 152 | ({:wins 0} 153 | {:wins 0} 154 | {:wins 0)} 155 | {:wins -3} 156 | {:wins 0})} 157 | ``` 158 | 159 | ### Custom scalars 160 | 161 | If your schema contains [custom scalars](http://lacinia.readthedocs.io/en/latest/custom-scalars.html) you will need to 162 | provide generators for them. You can do so in the following way: 163 | 164 | ```clojure 165 | (let [schema {:scalars {:Custom {:parse :parse-custom 166 | :serialize :serialize-custom}} 167 | :objects {:obj-with-custom 168 | {:fields 169 | {:custom {:type :Custom} 170 | :custom-list {:type '(list :Custom)}}}}}] 171 | 172 | (let [gen (lgen/generator schema {:scalars {:Custom gen/int}})] 173 | (g/sample (gen :obj-with-custom) 10))) 174 | 175 | ;; => {:custom 23 176 | :custom-list (-1 4 16)} 177 | ``` 178 | 179 | ## Development 180 | 181 | [![CircleCI](https://circleci.com/gh/oliyh/lacinia-gen.svg?style=svg)](https://circleci.com/gh/oliyh/lacinia-gen) 182 | 183 | ## License 184 | 185 | Copyright © 2018 oliyh 186 | 187 | Distributed under the Eclipse Public License either version 1.0 or (at 188 | your option) any later version. 189 | --------------------------------------------------------------------------------