Skip to content

Commit

Permalink
Revert changing the semantic of Schema.optional by introducing Schema…
Browse files Browse the repository at this point in the history
….nullable
  • Loading branch information
XiNiHa committed Apr 25, 2024
1 parent 7d8e546 commit d710a70
Show file tree
Hide file tree
Showing 9 changed files with 47 additions and 40 deletions.
4 changes: 2 additions & 2 deletions core/src/main/scala-2/caliban/schema/SchemaDerivation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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),
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala-3/caliban/schema/DerivationUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
59 changes: 33 additions & 26 deletions core/src/main/scala/caliban/schema/Schema.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
Expand All @@ -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 = {
Expand Down Expand Up @@ -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] =
Expand All @@ -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))
Expand All @@ -372,15 +379,15 @@ 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()))
}
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)))
Expand All @@ -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)
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)))
Expand All @@ -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)))
Expand All @@ -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(
Expand All @@ -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))
Expand All @@ -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))
Expand All @@ -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))
Expand All @@ -563,35 +570,35 @@ 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))
}
implicit def streamSchema[R0, R1 >: R0, R2 >: R0, E <: Throwable, A](implicit
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))
}
def customErrorStreamSchema[R0, R1 >: R0, R2 >: R0, E, A](convertError: E => ExecutionError)(implicit
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))
}
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion core/src/test/scala/caliban/TriState.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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] =
Expand All @@ -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] =
Expand Down
Loading

0 comments on commit d710a70

Please sign in to comment.