diff --git a/core/src/main/scala-2/caliban/schema/SchemaDerivation.scala b/core/src/main/scala-2/caliban/schema/SchemaDerivation.scala index b7526f83a3..a138a556b7 100644 --- a/core/src/main/scala-2/caliban/schema/SchemaDerivation.scala +++ b/core/src/main/scala-2/caliban/schema/SchemaDerivation.scala @@ -76,7 +76,7 @@ trait CommonSchemaDerivation[R] { getName(p), getDescription(p), () => - if (p.typeclass.optional || p.typeclass.canFail) p.typeclass.toType_(isInput, isSubscription) + if (p.typeclass.optional) p.typeclass.toType_(isInput, isSubscription) else p.typeclass.toType_(isInput, isSubscription).nonNull, p.annotations.collectFirst { case GQLDefault(v) => v }, p.annotations.collectFirst { case GQLDeprecated(_) => () }.isDefined, @@ -98,7 +98,7 @@ trait CommonSchemaDerivation[R] { val (isNullable, isNullabilityForced) = { val hasNullableAnn = p.annotations.contains(GQLNullable()) val hasNonNullAnn = p.annotations.contains(GQLNonNullable()) - (!hasNonNullAnn && (hasNullableAnn || p.typeclass.optional), hasNullableAnn || hasNonNullAnn) + (!hasNonNullAnn && (hasNullableAnn || p.typeclass.nullable), hasNullableAnn || hasNonNullAnn) } Types.makeField( getName(p), diff --git a/core/src/main/scala-3/caliban/schema/DerivationUtils.scala b/core/src/main/scala-3/caliban/schema/DerivationUtils.scala index 040b327b65..853c26ca2c 100644 --- a/core/src/main/scala-3/caliban/schema/DerivationUtils.scala +++ b/core/src/main/scala-3/caliban/schema/DerivationUtils.scala @@ -105,7 +105,7 @@ private object DerivationUtils { name, getDescription(fieldAnnotations), () => - if (schema.optional || schema.canFail) schema.toType_(isInput, isSubscription) + if (schema.optional) schema.toType_(isInput, isSubscription) else schema.toType_(isInput, isSubscription).nonNull, getDefaultValue(fieldAnnotations), getDeprecatedReason(fieldAnnotations).isDefined, @@ -130,7 +130,7 @@ private object DerivationUtils { val (isNullable, isNullabilityForced) = { val hasNullableAnn = fieldAnnotations.contains(GQLNullable()) val hasNonNullAnn = fieldAnnotations.contains(GQLNonNullable()) - (!hasNonNullAnn && (hasNullableAnn || schema.optional), hasNullableAnn || hasNonNullAnn) + (!hasNonNullAnn && (hasNullableAnn || schema.nullable), hasNullableAnn || hasNonNullAnn) } Types.makeField( name, diff --git a/core/src/main/scala/caliban/schema/Schema.scala b/core/src/main/scala/caliban/schema/Schema.scala index b5001c5bf3..069e401948 100644 --- a/core/src/main/scala/caliban/schema/Schema.scala +++ b/core/src/main/scala/caliban/schema/Schema.scala @@ -73,9 +73,16 @@ trait Schema[-R, T] { self => def resolve(value: T): Step[R] /** - * Defines if the type is considered optional or non-null. Should be false except for `Option`. + * Defines if the type can resolve to null or not. + * It is true if the type is nullable or if it can fail. */ - def optional: Boolean = false + @deprecatedOverriding("this method will be made final. Override canFail and nullable instead", "2.6.1") + def optional: Boolean = canFail || nullable + + /** + * Defines if the underlying type represents a nullable value. Should be false except for `Option`. + */ + def nullable: Boolean = false /** * Defines if the type can fail during resolution. @@ -95,7 +102,7 @@ trait Schema[-R, T] { self => * @param f a function from `A` to `T`. */ def contramap[A](f: A => T): Schema[R, A] = new Schema[R, A] { - override def optional: Boolean = self.optional + override def nullable: Boolean = self.nullable override def canFail: Boolean = self.canFail override def arguments: List[__InputValue] = self.arguments override def toType(isInput: Boolean, isSubscription: Boolean): __Type = self.toType_(isInput, isSubscription) @@ -108,7 +115,7 @@ trait Schema[-R, T] { self => * @param inputName new name for the type when it's an input type (by default "Input" is added after the name) */ def rename(name: String, inputName: Option[String] = None): Schema[R, T] = new Schema[R, T] { - override def optional: Boolean = self.optional + override def nullable: Boolean = self.nullable override def canFail: Boolean = self.canFail override def arguments: List[__InputValue] = self.arguments override def toType(isInput: Boolean, isSubscription: Boolean): __Type = { @@ -347,7 +354,7 @@ trait GenericSchema[R] extends SchemaDerivation[R] with TemporalSchema { Schema.stringSchema.contramap(Cursor[Base64Cursor].encode) implicit def optionSchema[R0, A](implicit ev: Schema[R0, A]): Schema[R0, Option[A]] = new Schema[R0, Option[A]] { - override def optional: Boolean = true + override def nullable: Boolean = true override def toType(isInput: Boolean, isSubscription: Boolean): __Type = ev.toType_(isInput, isSubscription) override def resolve(value: Option[A]): Step[R0] = @@ -359,7 +366,7 @@ trait GenericSchema[R] extends SchemaDerivation[R] with TemporalSchema { implicit def listSchema[R0, A](implicit ev: Schema[R0, A]): Schema[R0, List[A]] = new Schema[R0, List[A]] { override def toType(isInput: Boolean, isSubscription: Boolean): __Type = { val t = ev.toType_(isInput, isSubscription) - (if (ev.optional || ev.canFail) t else t.nonNull).list + (if (ev.nullable || ev.canFail) t else t.nonNull).list } override def resolve(value: List[A]): Step[R0] = ListStep(value.map(ev.resolve)) @@ -372,7 +379,7 @@ trait GenericSchema[R] extends SchemaDerivation[R] with TemporalSchema { listSchema[R0, A].contramap(_.toList) implicit def functionUnitSchema[R0, A](implicit ev: Schema[R0, A]): Schema[R0, () => A] = new Schema[R0, () => A] { - override def optional: Boolean = ev.optional + override def nullable: Boolean = ev.nullable override def canFail: Boolean = ev.canFail override def toType(isInput: Boolean, isSubscription: Boolean): __Type = ev.toType_(isInput, isSubscription) override def resolve(value: () => A): Step[R0] = FunctionStep(_ => ev.resolve(value())) @@ -380,7 +387,7 @@ trait GenericSchema[R] extends SchemaDerivation[R] with TemporalSchema { implicit def metadataFunctionSchema[R0, A](implicit ev: Schema[R0, A]): Schema[R0, Field => A] = new Schema[R0, Field => A] { override def arguments: List[__InputValue] = ev.arguments - override def optional: Boolean = ev.optional + override def nullable: Boolean = ev.nullable override def canFail: Boolean = ev.canFail override def toType(isInput: Boolean, isSubscription: Boolean): __Type = ev.toType_(isInput, isSubscription) override def resolve(value: Field => A): Step[R0] = MetadataFunctionStep(field => ev.resolve(value(field))) @@ -396,13 +403,13 @@ trait GenericSchema[R] extends SchemaDerivation[R] with TemporalSchema { val description: String = s"Either $typeAName or $typeBName" implicit val leftSchema: Schema[RA, A] = new Schema[RA, A] { - override def optional: Boolean = true + override def nullable: Boolean = true override def canFail: Boolean = evA.canFail override def toType(isInput: Boolean, isSubscription: Boolean): __Type = evA.toType_(isInput, isSubscription) override def resolve(value: A): Step[RA] = evA.resolve(value) } implicit val rightSchema: Schema[RB, B] = new Schema[RB, B] { - override def optional: Boolean = true + override def nullable: Boolean = true override def canFail: Boolean = evB.canFail override def toType(isInput: Boolean, isSubscription: Boolean): __Type = evB.toType_(isInput, isSubscription) override def resolve(value: B): Step[RB] = evB.resolve(value) @@ -471,14 +478,14 @@ trait GenericSchema[R] extends SchemaDerivation[R] with TemporalSchema { __InputValue( unwrappedArgumentName, None, - () => if (ev1.optional || ev1.canFail) inputType else inputType.nonNull, + () => if (ev1.nullable || ev1.canFail) inputType else inputType.nonNull, None ) ) ) } - override def optional: Boolean = ev2.optional + override def nullable: Boolean = ev2.nullable override def canFail: Boolean = ev2.canFail override def toType(isInput: Boolean, isSubscription: Boolean): __Type = ev2.toType_(isInput, isSubscription) @@ -507,7 +514,7 @@ trait GenericSchema[R] extends SchemaDerivation[R] with TemporalSchema { effectSchema[R0, R0, R0, Throwable, A].contramap[Future[A]](future => ZIO.fromFuture(_ => future)(Trace.empty)) implicit def infallibleEffectSchema[R0, R1 >: R0, R2 >: R0, A](implicit ev: Schema[R2, A]): Schema[R0, URIO[R1, A]] = new Schema[R0, URIO[R1, A]] { - override def optional: Boolean = ev.optional + override def nullable: Boolean = ev.nullable override def canFail: Boolean = ev.canFail override def toType(isInput: Boolean, isSubscription: Boolean): __Type = ev.toType_(isInput, isSubscription) override def resolve(value: URIO[R1, A]): Step[R0] = QueryStep(ZQuery.fromZIONow(value.map(ev.resolve))) @@ -516,7 +523,7 @@ trait GenericSchema[R] extends SchemaDerivation[R] with TemporalSchema { ev: Schema[R2, A] ): Schema[R0, ZIO[R1, E, A]] = new Schema[R0, ZIO[R1, E, A]] { - override def optional: Boolean = ev.optional + override def nullable: Boolean = ev.nullable override def canFail: Boolean = true override def toType(isInput: Boolean, isSubscription: Boolean): __Type = ev.toType_(isInput, isSubscription) override def resolve(value: ZIO[R1, E, A]): Step[R0] = QueryStep(ZQuery.fromZIONow(value.map(ev.resolve))) @@ -525,7 +532,7 @@ trait GenericSchema[R] extends SchemaDerivation[R] with TemporalSchema { ev: Schema[R2, A] ): Schema[R0, ZIO[R1, E, A]] = new Schema[R0, ZIO[R1, E, A]] { - override def optional: Boolean = ev.optional + override def nullable: Boolean = ev.nullable override def canFail: Boolean = true override def toType(isInput: Boolean, isSubscription: Boolean): __Type = ev.toType_(isInput, isSubscription) override def resolve(value: ZIO[R1, E, A]): Step[R0] = QueryStep( @@ -536,7 +543,7 @@ trait GenericSchema[R] extends SchemaDerivation[R] with TemporalSchema { ev: Schema[R2, A] ): Schema[R0, ZQuery[R1, Nothing, A]] = new Schema[R0, ZQuery[R1, Nothing, A]] { - override def optional: Boolean = ev.optional + override def nullable: Boolean = ev.nullable override def canFail: Boolean = ev.canFail override def toType(isInput: Boolean, isSubscription: Boolean): __Type = ev.toType_(isInput, isSubscription) override def resolve(value: ZQuery[R1, Nothing, A]): Step[R0] = QueryStep(value.map(ev.resolve)) @@ -545,7 +552,7 @@ trait GenericSchema[R] extends SchemaDerivation[R] with TemporalSchema { ev: Schema[R2, A] ): Schema[R0, ZQuery[R1, E, A]] = new Schema[R0, ZQuery[R1, E, A]] { - override def optional: Boolean = ev.optional + override def nullable: Boolean = ev.nullable override def canFail: Boolean = true override def toType(isInput: Boolean, isSubscription: Boolean): __Type = ev.toType_(isInput, isSubscription) override def resolve(value: ZQuery[R1, E, A]): Step[R0] = QueryStep(value.map(ev.resolve)) @@ -554,7 +561,7 @@ trait GenericSchema[R] extends SchemaDerivation[R] with TemporalSchema { ev: Schema[R2, A] ): Schema[R0, ZQuery[R1, E, A]] = new Schema[R0, ZQuery[R1, E, A]] { - override def optional: Boolean = ev.optional + override def nullable: Boolean = ev.nullable override def canFail: Boolean = true override def toType(isInput: Boolean, isSubscription: Boolean): __Type = ev.toType_(isInput, isSubscription) override def resolve(value: ZQuery[R1, E, A]): Step[R0] = QueryStep(value.mapBoth(convertError, ev.resolve)) @@ -563,11 +570,11 @@ trait GenericSchema[R] extends SchemaDerivation[R] with TemporalSchema { ev: Schema[R2, A] ): Schema[R1, ZStream[R1, Nothing, A]] = new Schema[R1, ZStream[R1, Nothing, A]] { - override def optional: Boolean = false + override def nullable: Boolean = false override def canFail: Boolean = ev.canFail override def toType(isInput: Boolean, isSubscription: Boolean): __Type = { val t = ev.toType_(isInput, isSubscription) - if (isSubscription) t else (if (ev.optional || ev.canFail) t else t.nonNull).list + if (isSubscription) t else (if (ev.nullable || ev.canFail) t else t.nonNull).list } override def resolve(value: ZStream[R1, Nothing, A]): Step[R1] = StreamStep(value.map(ev.resolve)) } @@ -575,11 +582,11 @@ trait GenericSchema[R] extends SchemaDerivation[R] with TemporalSchema { ev: Schema[R2, A] ): Schema[R0, ZStream[R1, E, A]] = new Schema[R0, ZStream[R1, E, A]] { - override def optional: Boolean = ev.optional + override def nullable: Boolean = ev.nullable override def canFail: Boolean = true override def toType(isInput: Boolean, isSubscription: Boolean): __Type = { val t = ev.toType_(isInput, isSubscription) - if (isSubscription) t else (if (ev.optional || ev.canFail) t else t.nonNull).list + if (isSubscription) t else (if (ev.nullable || ev.canFail) t else t.nonNull).list } override def resolve(value: ZStream[R1, E, A]): Step[R0] = StreamStep(value.map(ev.resolve)) } @@ -587,11 +594,11 @@ trait GenericSchema[R] extends SchemaDerivation[R] with TemporalSchema { ev: Schema[R2, A] ): Schema[R0, ZStream[R1, E, A]] = new Schema[R0, ZStream[R1, E, A]] { - override def optional: Boolean = ev.optional + override def nullable: Boolean = ev.nullable override def canFail: Boolean = true override def toType(isInput: Boolean, isSubscription: Boolean): __Type = { val t = ev.toType_(isInput, isSubscription) - if (isSubscription) t else (if (ev.optional || ev.canFail) t else t.nonNull).list + if (isSubscription) t else (if (ev.nullable || ev.canFail) t else t.nonNull).list } override def resolve(value: ZStream[R1, E, A]): Step[R0] = StreamStep(value.mapBoth(convertError, ev.resolve)) } @@ -724,7 +731,7 @@ abstract class PartiallyAppliedFieldBase[V]( description, _ => Nil, () => - if (ev.optional || ev.canFail) ev.toType_(ft.isInput, ft.isSubscription) + if (ev.nullable || ev.canFail) ev.toType_(ft.isInput, ft.isSubscription) else ev.toType_(ft.isInput, ft.isSubscription).nonNull, isDeprecated = Directives.isDeprecated(directives), deprecationReason = Directives.deprecationReason(directives), @@ -768,7 +775,7 @@ case class PartiallyAppliedFieldWithArgs[V, A]( description, ev1.arguments, () => - if (ev1.optional || ev1.canFail) ev1.toType_(fa.isInput, fa.isSubscription) + if (ev1.nullable || ev1.canFail) ev1.toType_(fa.isInput, fa.isSubscription) else ev1.toType_(fa.isInput, fa.isSubscription).nonNull, isDeprecated = Directives.isDeprecated(directives), deprecationReason = Directives.deprecationReason(directives), diff --git a/core/src/test/scala/caliban/TriState.scala b/core/src/test/scala/caliban/TriState.scala index 2e9646587d..f6ae618f00 100644 --- a/core/src/test/scala/caliban/TriState.scala +++ b/core/src/test/scala/caliban/TriState.scala @@ -15,7 +15,7 @@ object TriState { def schemaCustom[R, A](undefined: PureStep)(implicit ev: Schema[R, A]): Schema[R, TriState[A]] = new Schema[R, TriState[A]] { - override val optional = true + override val nullable = true override def toType(isInput: Boolean, isSubscription: Boolean) = ev.toType_(isInput, isSubscription) diff --git a/federation/src/main/scala/caliban/federation/FederationSupport.scala b/federation/src/main/scala/caliban/federation/FederationSupport.scala index 5eef75a02c..e4d695f94c 100644 --- a/federation/src/main/scala/caliban/federation/FederationSupport.scala +++ b/federation/src/main/scala/caliban/federation/FederationSupport.scala @@ -60,7 +60,7 @@ abstract class FederationSupport( import genericSchema.auto._ implicit val entitySchema: Schema[R, _Entity] = new Schema[R, _Entity] { - override def optional: Boolean = true + override def nullable: Boolean = true override def toType(isInput: Boolean, isSubscription: Boolean): __Type = __Type( __TypeKind.UNION, diff --git a/interop/cats/src/main/scala/caliban/interop/cats/CatsInterop.scala b/interop/cats/src/main/scala/caliban/interop/cats/CatsInterop.scala index f387813a68..4883479881 100644 --- a/interop/cats/src/main/scala/caliban/interop/cats/CatsInterop.scala +++ b/interop/cats/src/main/scala/caliban/interop/cats/CatsInterop.scala @@ -190,8 +190,8 @@ object CatsInterop { override def toType(isInput: Boolean, isSubscription: Boolean): __Type = ev.toType_(isInput, isSubscription) - override def optional: Boolean = - ev.optional + override def nullable: Boolean = + ev.nullable override def canFail: Boolean = true override def resolve(value: F[A]): Step[R] = diff --git a/interop/cats/src/main/scala/caliban/interop/fs2/Fs2Interop.scala b/interop/cats/src/main/scala/caliban/interop/fs2/Fs2Interop.scala index 5c6e8f0c92..c2a3239f2a 100644 --- a/interop/cats/src/main/scala/caliban/interop/fs2/Fs2Interop.scala +++ b/interop/cats/src/main/scala/caliban/interop/fs2/Fs2Interop.scala @@ -14,7 +14,7 @@ object Fs2Interop { override def toType(isInput: Boolean, isSubscription: Boolean): __Type = ev.toType_(isInput, isSubscription) - override def optional: Boolean = ev.optional + override def nullable: Boolean = ev.nullable override def canFail: Boolean = true override def resolve(value: Stream[RIO[R, *], A]): Step[R] = @@ -29,7 +29,7 @@ object Fs2Interop { override def toType(isInput: Boolean, isSubscription: Boolean): __Type = ev.toType_(isInput, isSubscription) - override def optional: Boolean = ev.optional + override def nullable: Boolean = ev.nullable override def canFail: Boolean = true override def resolve(value: Stream[F, A]): Step[R] = diff --git a/interop/monix/src/main/scala/caliban/interop/monix/MonixInterop.scala b/interop/monix/src/main/scala/caliban/interop/monix/MonixInterop.scala index daf648929d..c29e38e8e1 100644 --- a/interop/monix/src/main/scala/caliban/interop/monix/MonixInterop.scala +++ b/interop/monix/src/main/scala/caliban/interop/monix/MonixInterop.scala @@ -60,7 +60,7 @@ object MonixInterop { def taskSchema[R, A](implicit ev: Schema[R, A], ev2: ConcurrentEffect[MonixTask]): Schema[R, MonixTask[A]] = new Schema[R, MonixTask[A]] { override def toType(isInput: Boolean, isSubscription: Boolean): __Type = ev.toType_(isInput, isSubscription) - override def optional: Boolean = ev.optional + override def nullable: Boolean = ev.nullable override def canFail: Boolean = true override def resolve(value: MonixTask[A]): Step[R] = QueryStep(ZQuery.fromZIO(value.to[Task].map(ev.resolve))) @@ -70,10 +70,10 @@ object MonixInterop { queueSize: Int )(implicit ev: Schema[R, A], ev2: ConcurrentEffect[MonixTask]): Schema[R, Observable[A]] = new Schema[R, Observable[A]] { - override def optional: Boolean = true + override def nullable: Boolean = true override def toType(isInput: Boolean, isSubscription: Boolean): __Type = { val t = ev.toType_(isInput, isSubscription) - if (isSubscription) t else (if (ev.optional) t else t.nonNull).list + if (isSubscription) t else (if (ev.nullable) t else t.nonNull).list } override def resolve(value: Observable[A]): Step[R] = StreamStep( diff --git a/interop/tapir/src/main/scala/caliban/interop/tapir/package.scala b/interop/tapir/src/main/scala/caliban/interop/tapir/package.scala index 00ef144aee..b7e92669b7 100644 --- a/interop/tapir/src/main/scala/caliban/interop/tapir/package.scala +++ b/interop/tapir/src/main/scala/caliban/interop/tapir/package.scala @@ -106,7 +106,7 @@ package object tapir { Types.makeField( extractPath(serverEndpoint.endpoint.info.name, serverEndpoint.endpoint.input), serverEndpoint.endpoint.info.description, - getArgs(inputSchema.toType_(isInput = true), inputSchema.optional || inputSchema.canFail), + getArgs(inputSchema.toType_(isInput = true), inputSchema.optional), () => if (serverEndpoint.endpoint.errorOutput == EndpointOutput.Void[E]()) outputSchema.toType_().nonNull