diff --git a/src/oksa/alpha/api.cljc b/src/oksa/alpha/api.cljc index 36b1586..768ef5d 100644 --- a/src/oksa/alpha/api.cljc +++ b/src/oksa/alpha/api.cljc @@ -286,6 +286,7 @@ - keyword (representing a naked field) - `oksa.alpha.api/fragment-spread` - `oksa.alpha.api/inline-fragment` + - `oksa.alpha.api/select` (but only directly after a field) Tolerates nil entries. @@ -321,7 +322,7 @@ (-selection-set? %) (-fragment-spread? %) (-inline-fragment? %)) selections*)) - "invalid selections, expected `oksa.alpha.api/field`, keyword (naked field), `oksa.alpha.api/fragment-spread`, or `oksa.alpha.api/inline-fragment`") + "invalid selections, expected `oksa.alpha.api/field`, keyword (naked field), `oksa.alpha.api/select`, `oksa.alpha.api/fragment-spread`, or `oksa.alpha.api/inline-fragment`") (oksa.parse/-selection-set selections*))) (defn field diff --git a/src/oksa/parse.cljc b/src/oksa/parse.cljc index 5b849b4..d5b4c02 100644 --- a/src/oksa/parse.cljc +++ b/src/oksa/parse.cljc @@ -99,14 +99,21 @@ [:? [:schema [:ref ::TypeOpts]]] [:schema [:ref ::Type]]] ::SelectionSet [:orn - [::SelectionSet [:+ [:catn - [::node [:schema [:ref ::Selection]]] - [::children [:? [:schema [:ref ::SelectionSet]]]]]]]] + [::SelectionSet [:+ [:alt + [:catn + [::node [:schema [:ref ::Selection]]] + [::children [:? [:schema [:ref ::SelectionSet]]]]] + [:catn + [::node [:schema [:ref ::WrappedField]]]] + ;; Special case where subsequent selection set is allowed + [:catn + [::node [:schema [:ref ::BareField]]] + [::children [:? [:schema [:ref ::SelectionSet]]]]]]]]] + ::WrappedField [:orn [::WrappedField [:schema [:ref ::Field]]]] ::Selection [:orn [::FragmentSpread [:schema [:ref ::FragmentSpread]]] [::InlineFragment [:schema [:ref ::InlineFragment]]] - [::NakedField [:schema [:ref ::NakedField]]] - [::WrappedField [:schema [:ref ::Field]]]] + [::NakedField [:schema [:ref ::NakedField]]]] ::Field [:orn [::Field [:cat [:schema [:ref ::FieldName]] [:map @@ -116,6 +123,14 @@ [:directives {:optional true} [:ref ::Directives]]] [:? [:schema [:ref ::SelectionSet]]]]]] + ::BareField [:orn [::Field [:cat + [:schema [:ref ::FieldName]] + [:map + [:alias {:optional true} [:ref ::Alias]] + [:arguments {:optional true} + [:ref ::Arguments]] + [:directives {:optional true} + [:ref ::Directives]]]]]] ::NakedField [:schema [:ref ::FieldName]] ::FieldName [:and [:schema [:ref ::Name]] @@ -947,6 +962,7 @@ (let [[selection-type value] node] (cond-> (into [] [(case selection-type + :oksa.parse/Field (oksa.util/transform-malli-ast -transform-map node) :oksa.parse/NakedField (oksa.util/transform-malli-ast -transform-map [:oksa.parse/Field [value {}]]) :oksa.parse/WrappedField (oksa.util/transform-malli-ast -transform-map value) :oksa.parse/FragmentSpread (oksa.util/transform-malli-ast -transform-map value) diff --git a/test/oksa/alpha/api_test.cljc b/test/oksa/alpha/api_test.cljc index b1f78a8..5351815 100644 --- a/test/oksa/alpha/api_test.cljc +++ b/test/oksa/alpha/api_test.cljc @@ -2,6 +2,7 @@ (:require [camel-snake-kebab.core :as csk] [#?(:clj clojure.test :cljs cljs.test) :as t] + [oksa.core :as oksa] [oksa.alpha.api :as api]) #?(:clj (:import [graphql.parser Parser]))) @@ -100,7 +101,7 @@ (t/is (= "{bar{qux{baz}}}" (unparse-and-validate (api/select :bar (api/select :qux - (api/select :baz)))) + (api/select :baz)))) (unparse-and-validate (api/select (api/field :bar) (api/select (api/field :qux) (api/select (api/field :baz))))) @@ -482,7 +483,21 @@ (t/is (= "query ($foo:Bar @fooDirective(fooArg:123)){fooField}" (unparse-and-validate (api/query (api/opts (api/variable :foo (api/opts (api/directive :fooDirective {:fooArg 123})) :Bar)) - (api/select :fooField))))))) + (api/select :fooField)))))) + (t/testing "sequentiality" + (t/is (= "{foo{bar}}" + (unparse-and-validate + (api/select + (api/field :foo) + (api/select :bar)))) + "field w/o selection-set + sequential selection-set parses correctly") + (t/testing "sequential selection sets should throw an exception" + (t/is (thrown? #?(:clj Exception :cljs js/Error) + (unparse-and-validate + (api/select + (api/field :foo + (api/select :qux :baz)) + (api/select :basho)))))))) (t/deftest transformers-test (t/testing "names are transformed when transformer fn is provided" diff --git a/test/oksa/core_test.cljc b/test/oksa/core_test.cljc index 318a66c..3b7df19 100644 --- a/test/oksa/core_test.cljc +++ b/test/oksa/core_test.cljc @@ -40,17 +40,27 @@ (t/is (= "{subscription{subscription{baz}}}" (unparse-and-validate [:subscription [:subscription [:baz]]]))))) (t/testing "selection set" (t/is (= "{foo}" - (unparse-and-validate [:foo]))) + (unparse-and-validate [:foo]) + (unparse-and-validate [[:foo {}]]))) (t/is (= "{foo bar}" - (unparse-and-validate [:foo :bar]))) + (unparse-and-validate [:foo :bar]) + (unparse-and-validate [[:foo {}] [:bar {}]]))) (t/is (= "{bar{qux{baz}}}" - (unparse-and-validate [:bar [:qux [:baz]]]))) + (unparse-and-validate [:bar [:qux [:baz]]]) + (unparse-and-validate [[:bar {}] [[:qux {}] [[:baz {}]]]]) + (unparse-and-validate [[:bar {} [[:qux {} [[:baz {}]]]]]]))) (t/is (= "{foo bar{qux{baz}}}" - (unparse-and-validate [:foo :bar [:qux [:baz]]]))) + (unparse-and-validate [:foo :bar [:qux [:baz]]]) + (unparse-and-validate [[:foo {}] [:bar {}] [[:qux {} [[:baz {}]]]]]) + (unparse-and-validate [[:foo {}] [:bar {} [[:qux {} [[:baz {}]]]]]]))) (t/is (= "{foo bar{qux baz}}" - (unparse-and-validate [:foo :bar [:qux :baz]]))) + (unparse-and-validate [:foo :bar [:qux :baz]]) + (unparse-and-validate [[:foo {}] [:bar {}] [[:qux {}] [:baz {}]]]) + (unparse-and-validate [[:foo {}] [:bar {} [[:qux {}] [:baz {}]]]]))) (t/is (= "{foo{bar{baz qux} frob}}" - (unparse-and-validate [:foo [:bar [:baz :qux] :frob]]))) + (unparse-and-validate [:foo [:bar [:baz :qux] :frob]]) + (unparse-and-validate [[:foo {}] [:bar [:baz :qux] :frob]]) + (unparse-and-validate [[:foo {} [:bar [:baz :qux] :frob]]]))) (t/testing "support strings as field names" (t/is (= "{foo}" (unparse-and-validate ["foo"]) @@ -296,7 +306,13 @@ [:fooField]]))) (t/is (= "query ($foo:Bar @fooDirective(fooArg:123)){fooField}" (unparse-and-validate [:oksa/query {:variables [:foo {:directives [[:fooDirective {:arguments {:fooArg 123}}]]} :Bar]} - [:fooField]]))))) + [:fooField]])))) + (t/testing "sequentiality" + (t/is (= "{foo{bar}}" + (unparse-and-validate [[:foo {}] [:bar]])) "field w/o selection-set + sequential selection-set parses correctly") + (t/testing "sequential selection sets should throw an exception" + (t/is (thrown? #?(:clj Exception :cljs js/Error) + (unparse-and-validate [[:foo {} [:qux :baz]] [:basho]])))))) (t/deftest transformers-test (t/testing "names are transformed when transformer fn is provided"