From 17f095997a52a9f2c2812c9f994626a64b0bd7ee Mon Sep 17 00:00:00 2001 From: Yannick Scherer Date: Wed, 22 Mar 2017 16:58:04 +0100 Subject: [PATCH] ensure non-null values for non-nullable field types. --- src/alumbra/claro/projection.clj | 22 ++++++++++++++++++++-- test/alumbra/claro/coercion_test.clj | 19 ++++++++++++++----- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/alumbra/claro/projection.clj b/src/alumbra/claro/projection.clj index 48a3b46..7ee1a6c 100644 --- a/src/alumbra/claro/projection.clj +++ b/src/alumbra/claro/projection.clj @@ -2,6 +2,7 @@ (:require [alumbra.claro [coercion :as c] [values :as v]] + [claro.data :as data] [claro.data.ops :as ops] [claro.projection :as projection])) @@ -69,16 +70,33 @@ (projection/prepare #(ops/then % coercer) projection/leaf) projection/leaf)) +(defn- nullable-value + [_ projection] + (projection/maybe projection)) + +(defn- non-nullable-value + [{:keys [field-name type-name]} projection] + (projection/transform + (fn [value] + (if (nil? value) + (data/error + (format "field '%s' returned 'null' but type '%s!' is non-nullable." + field-name + type-name)) + value)) + projection)) + (defn- field-spec->projection "Generate a projection for a `:alumbra.spec.canonical-operation/field-spec` value." [opts {:keys [field-type non-null? field-spec] :as spec}] - (cond-> + (cond->> (case field-type :leaf (coerced-leaf opts spec) :object (block->projection opts spec) :list [(field-spec->projection opts field-spec)]) - (not non-null?) projection/maybe)) + (not non-null?) (nullable-value spec) + non-null? (non-nullable-value spec))) (defn- key-for-field "Generate the key for the given field. Will use `key-fn` to generate it diff --git a/test/alumbra/claro/coercion_test.clj b/test/alumbra/claro/coercion_test.clj index 5b8eca4..da8d697 100644 --- a/test/alumbra/claro/coercion_test.clj +++ b/test/alumbra/claro/coercion_test.clj @@ -14,12 +14,13 @@ (fix/schema "enum Emotion { HAPPY HAPPIER THE_HAPPIEST } type QueryRoot { - asId(v: ID!): ID! - asInt(v: Int!): Int! - asString(v: String!): String! - asFloat(v: Float!): Float! - asBool(v: Boolean!): Boolean! + asId(v: ID!): ID + asInt(v: Int!): Int + asString(v: String!): String + asFloat(v: Float!): Float + asBool(v: Boolean!): Boolean asEnum(v: Emotion!): Emotion + asNonNull(v: ID!): ID! } schema { query: QueryRoot }")) @@ -198,3 +199,11 @@ (is (= {"asId" nil} (:data result))) (is (= "could not coerce value to 'ID': \"10\"" (-> result :errors first :message))))) + +(deftest t-non-nullable-result + (let [execute! (fix/execute-fn + {:schema schema + :query {:as-non-null (->Identity nil (constantly false))}}) + result (is (execute! "{ asNonNull(v: 10) }"))] + (is (= "field 'asNonNull' returned 'null' but type 'ID!' is non-nullable." + (-> result :errors first :message)))))