diff --git a/README.md b/README.md index b286f2450b..9b9ff86c88 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ The design principles behind the library are the following: - clean separation between schema definition and implementation: schema is defined and validated at compile time using Scala standard types, resolver (`RootResolver`) is a simple value provided at runtime. - minimal amount of boilerplate: no need to manually define a schema for every type in your API. +Caliban can also be used to build GraphQL frontends thanks to a type-safe and functional DSL. + ### Consult the [Documentation](https://ghostdogpr.github.io/caliban/docs/) to learn how to use Caliban. ### Any questions? Head up to the [#caliban](https://discordapp.com/channels/629491597070827530/633200096393166868) channel on [ZIO Discord](https://discord.gg/EYpumuv). diff --git a/build.sbt b/build.sbt index 927a024441..e8480a92de 100644 --- a/build.sbt +++ b/build.sbt @@ -1,9 +1,13 @@ import sbtcrossproject.CrossPlugin.autoImport.{ crossProject, CrossType } -val mainScala = "2.12.10" -val allScala = Seq("2.13.1", mainScala) +val mainScala = "2.12.10" +val allScala = Seq("2.13.1", mainScala) + val http4sVersion = "0.21.1" val silencerVersion = "1.6.0" +val sttpVersion = "2.0.1" +val zioVersion = "1.0.0-RC17" + inThisBuild( List( organization := "com.github.ghostdogpr", @@ -71,10 +75,10 @@ lazy val core = crossProject(JSPlatform, JVMPlatform) "com.lihaoyi" %%% "fastparse" % "2.2.4", "com.propensive" %%% "magnolia" % "0.12.7", "com.propensive" %%% "mercator" % "0.2.1", - "dev.zio" %%% "zio" % "1.0.0-RC17", - "dev.zio" %%% "zio-streams" % "1.0.0-RC17", - "dev.zio" %%% "zio-test" % "1.0.0-RC17" % "test", - "dev.zio" %%% "zio-test-sbt" % "1.0.0-RC17" % "test", + "dev.zio" %%% "zio" % zioVersion, + "dev.zio" %%% "zio-streams" % zioVersion, + "dev.zio" %%% "zio-test" % zioVersion % "test", + "dev.zio" %%% "zio-test-sbt" % zioVersion % "test", "io.circe" %%% "circe-derivation" % "0.12.0-M7" % Optional, compilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1") ) @@ -98,8 +102,8 @@ lazy val codegen = project libraryDependencies ++= Seq( "org.scalameta" %% "scalafmt-dynamic" % "2.4.2", "org.scalameta" %% "scalafmt-core" % "2.4.2", - "dev.zio" %% "zio-test" % "1.0.0-RC17" % "test", - "dev.zio" %% "zio-test-sbt" % "1.0.0-RC17" % "test" + "dev.zio" %% "zio-test" % zioVersion % "test", + "dev.zio" %% "zio-test-sbt" % zioVersion % "test" ) ) .dependsOn(coreJVM) @@ -196,10 +200,10 @@ lazy val client = crossProject(JSPlatform, JVMPlatform) testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework")), libraryDependencies ++= Seq( "io.circe" %%% "circe-derivation" % "0.12.0-M7", - "com.softwaremill.sttp.client" %%% "core" % "2.0.0", - "com.softwaremill.sttp.client" %%% "circe" % "2.0.0", - "dev.zio" %%% "zio-test" % "1.0.0-RC17" % "test", - "dev.zio" %%% "zio-test-sbt" % "1.0.0-RC17" % "test" + "com.softwaremill.sttp.client" %%% "core" % sttpVersion, + "com.softwaremill.sttp.client" %%% "circe" % sttpVersion, + "dev.zio" %%% "zio-test" % zioVersion % "test", + "dev.zio" %%% "zio-test-sbt" % zioVersion % "test" ) ) lazy val clientJVM = client.jvm @@ -211,7 +215,7 @@ lazy val examples = project .settings(skip in publish := true) .settings( libraryDependencies ++= Seq( - "com.softwaremill.sttp.client" %% "async-http-client-backend-zio" % "2.0.0" + "com.softwaremill.sttp.client" %% "async-http-client-backend-zio" % sttpVersion ) ) .dependsOn(akkaHttp, http4s, catsInteropJVM, finch, monixInterop, clientJVM) diff --git a/codegen/src/test/scala/caliban/codegen/ClientWriterSpec.scala b/codegen/src/test/scala/caliban/codegen/ClientWriterSpec.scala index a55e94cd66..ccb70c56e3 100644 --- a/codegen/src/test/scala/caliban/codegen/ClientWriterSpec.scala +++ b/codegen/src/test/scala/caliban/codegen/ClientWriterSpec.scala @@ -3,7 +3,7 @@ package caliban.codegen import caliban.parsing.Parser import zio.Task import zio.test.Assertion._ -import zio.test.{ assertM, suite, testM, DefaultRunnableSpec } +import zio.test.{ assertM, suite, testM, DefaultRunnableSpec, TestAspect } object ClientWriterSpec extends DefaultRunnableSpec( @@ -361,6 +361,6 @@ object Client { ) ) } - ) + ) @@ TestAspect.sequential } ) diff --git a/codegen/src/test/scala/caliban/codegen/SchemaWriterSpec.scala b/codegen/src/test/scala/caliban/codegen/SchemaWriterSpec.scala index a5800d3f6b..4913fadbb4 100644 --- a/codegen/src/test/scala/caliban/codegen/SchemaWriterSpec.scala +++ b/codegen/src/test/scala/caliban/codegen/SchemaWriterSpec.scala @@ -4,7 +4,7 @@ import caliban.parsing.Parser import caliban.parsing.adt.Document import zio.Task import zio.test.Assertion.equalTo -import zio.test.{ assertM, suite, testM, DefaultRunnableSpec } +import zio.test.{ assertM, suite, testM, DefaultRunnableSpec, TestAspect } object SchemaWriterSpec extends DefaultRunnableSpec({ @@ -307,5 +307,5 @@ object Types { ) ) } - ) + ) @@ TestAspect.sequential }) diff --git a/core/src/main/scala/caliban/validation/Validator.scala b/core/src/main/scala/caliban/validation/Validator.scala index a87daa4626..238e06cd60 100644 --- a/core/src/main/scala/caliban/validation/Validator.scala +++ b/core/src/main/scala/caliban/validation/Validator.scala @@ -31,14 +31,18 @@ object Validator { def validateSchema(rootType: RootType): IO[ValidationError, Unit] = IO.foreach(rootType.types.values) { t => t.kind match { - case __TypeKind.ENUM => validateEnum(t) - case __TypeKind.UNION => validateUnion(t) - case __TypeKind.INTERFACE => validateInterface(t) - case _ => IO.unit + case __TypeKind.ENUM => validateEnum(t) + case __TypeKind.UNION => validateUnion(t) + case __TypeKind.INTERFACE => validateInterface(t) + case __TypeKind.INPUT_OBJECT => validateInputObject(t) + case _ => IO.unit } } .unit + def failValidation[T](msg: String, explanatoryText: String): IO[ValidationError, T] = + IO.fail(ValidationError(msg, explanatoryText)) + /** * Prepare the request for execution. * Fails with a [[caliban.CalibanError.ValidationError]] otherwise. @@ -70,19 +74,19 @@ object Validator { } operation match { - case Left(error) => IO.fail(ValidationError(error, "")) + case Left(error) => failValidation(error, "") case Right(op) => (op.operationType match { case Query => IO.succeed(rootSchema.query) case Mutation => rootSchema.mutation match { case Some(m) => IO.succeed(m) - case None => IO.fail(ValidationError("Mutations are not supported on this schema", "")) + case None => failValidation("Mutations are not supported on this schema", "") } case Subscription => rootSchema.subscription match { case Some(m) => IO.succeed(m) - case None => IO.fail(ValidationError("Subscriptions are not supported on this schema", "")) + case None => failValidation("Subscriptions are not supported on this schema", "") } }).map(operation => ExecutionRequest( @@ -183,11 +187,9 @@ object Validator { private def checkDirectivesUniqueness(directives: List[Directive]): IO[ValidationError, Unit] = IO.whenCase(directives.groupBy(_.name).find { case (_, v) => v.length > 1 }) { case Some((name, _)) => - IO.fail( - ValidationError( - s"Directive '$name' is defined twice.", - "Directives are used to describe some metadata or behavioral change on the definition they apply to. When more than one directive of the same name is used, the expected metadata or behavior becomes ambiguous, therefore only one of each directive is allowed per location." - ) + failValidation( + s"Directive '$name' is defined twice.", + "Directives are used to describe some metadata or behavioral change on the definition they apply to. When more than one directive of the same name is used, the expected metadata or behavior becomes ambiguous, therefore only one of each directive is allowed per location." ) } @@ -198,19 +200,15 @@ object Validator { case (d, location) => Introspector.directives.find(_.name == d.name) match { case None => - IO.fail( - ValidationError( - s"Directive '${d.name}' is not supported.", - "GraphQL servers define what directives they support. For each usage of a directive, the directive must be available on that server." - ) + failValidation( + s"Directive '${d.name}' is not supported.", + "GraphQL servers define what directives they support. For each usage of a directive, the directive must be available on that server." ) case Some(directive) => IO.when(!directive.locations.contains(location))( - IO.fail( - ValidationError( - s"Directive '${d.name}' is used in invalid location '$location'.", - "GraphQL servers define what directives they support and where they support them. For each usage of a directive, the directive must be used in a location that the server has declared support for." - ) + failValidation( + s"Directive '${d.name}' is used in invalid location '$location'.", + "GraphQL servers define what directives they support and where they support them. For each usage of a directive, the directive must be used in a location that the server has declared support for." ) ) } @@ -222,42 +220,34 @@ object Validator { IO.foreach(op.variableDefinitions.groupBy(_.name)) { case (name, variables) => IO.when(variables.length > 1)( - IO.fail( - ValidationError( - s"Variable '$name' is defined more than once.", - "If any operation defines more than one variable with the same name, it is ambiguous and invalid. It is invalid even if the type of the duplicate variable is the same." - ) + failValidation( + s"Variable '$name' is defined more than once.", + "If any operation defines more than one variable with the same name, it is ambiguous and invalid. It is invalid even if the type of the duplicate variable is the same." ) ) } *> IO.foreach(op.variableDefinitions) { v => val t = Type.innerType(v.variableType) IO.whenCase(context.rootType.types.get(t).map(_.kind)) { case Some(__TypeKind.OBJECT) | Some(__TypeKind.UNION) | Some(__TypeKind.INTERFACE) => - IO.fail( - ValidationError( - s"Type of variable '${v.name}' is not a valid input type.", - "Variables can only be input types. Objects, unions, and interfaces cannot be used as inputs." - ) + failValidation( + s"Type of variable '${v.name}' is not a valid input type.", + "Variables can only be input types. Objects, unions, and interfaces cannot be used as inputs." ) } } *> { val variableUsages = collectVariablesUsed(context, op.selectionSet) IO.foreach(variableUsages)(v => IO.when(!op.variableDefinitions.exists(_.name == v))( - IO.fail( - ValidationError( - s"Variable '$v' is not defined.", - "Variables are scoped on a per‐operation basis. That means that any variable used within the context of an operation must be defined at the top level of that operation" - ) + failValidation( + s"Variable '$v' is not defined.", + "Variables are scoped on a per‐operation basis. That means that any variable used within the context of an operation must be defined at the top level of that operation" ) ) ) *> IO.foreach(op.variableDefinitions)(v => IO.when(!variableUsages.contains(v.name))( - IO.fail( - ValidationError( - s"Variable '${v.name}' is not used.", - "All variables defined by an operation must be used in that operation or a fragment transitively included by that operation. Unused variables cause a validation error." - ) + failValidation( + s"Variable '${v.name}' is not used.", + "All variables defined by an operation must be used in that operation or a fragment transitively included by that operation. Unused variables cause a validation error." ) ) ) @@ -273,20 +263,15 @@ object Validator { val spreadNames = spreads.map(_.name).toSet IO.foreach(context.fragments.values)(f => if (!spreadNames.contains(f.name)) - IO.fail( - ValidationError( - s"Fragment '${f.name}' is not used in any spread.", - "Defined fragments must be used within a document." - ) + failValidation( + s"Fragment '${f.name}' is not used in any spread.", + "Defined fragments must be used within a document." ) else - IO.fail( - ValidationError( - s"Fragment '${f.name}' forms a cycle.", - "The graph of fragment spreads must not form any cycles including spreading itself. Otherwise an operation could infinitely spread or infinitely execute on cycles in the underlying data." - ) - ) - .when(detectCycles(context, f)) + failValidation( + s"Fragment '${f.name}' forms a cycle.", + "The graph of fragment spreads must not form any cycles including spreading itself. Otherwise an operation could infinitely spread or infinitely execute on cycles in the underlying data." + ).when(detectCycles(context, f)) ) .unit } @@ -307,11 +292,11 @@ object Validator { case OperationType.Query => validateFields(context, selectionSet, context.rootType.queryType) case OperationType.Mutation => context.rootType.mutationType.fold[IO[ValidationError, Unit]]( - IO.fail(ValidationError("Mutation operations are not supported on this schema.", "")) + failValidation("Mutation operations are not supported on this schema.", "") )(validateFields(context, selectionSet, _)) case OperationType.Subscription => context.rootType.subscriptionType.fold[IO[ValidationError, Unit]]( - IO.fail(ValidationError("Subscription operations are not supported on this schema.", "")) + failValidation("Subscription operations are not supported on this schema.", "") )(validateFields(context, selectionSet, _)) } case _: FragmentDefinition => IO.unit @@ -329,11 +314,9 @@ object Validator { case FragmentSpread(name, _) => context.fragments.get(name) match { case None => - IO.fail( - ValidationError( - s"Fragment spread '$name' is not defined.", - "Named fragment spreads must refer to fragments defined within the document. It is a validation error if the target of a spread is not defined." - ) + failValidation( + s"Fragment spread '$name' is not defined.", + "Named fragment spreads must refer to fragments defined within the document. It is a validation error if the target of a spread is not defined." ) case Some(fragment) => validateSpread(context, Some(name), currentType, Some(fragment.typeCondition), fragment.selectionSet) @@ -356,22 +339,18 @@ object Validator { val possibleFragmentTypes = getPossibleTypes(fragmentType).flatMap(_.name) val applicableTypes = possibleTypes intersect possibleFragmentTypes IO.when(applicableTypes.isEmpty)( - IO.fail( - ValidationError( - s"${name.fold("Inline fragment spread")(n => s"Fragment spread '$n'")} is not possible: possible types are '${possibleTypes - .mkString(", ")}' and possible fragment types are '${possibleFragmentTypes.mkString(", ")}'.", - "Fragments are declared on a type and will only apply when the runtime object type matches the type condition. They also are spread within the context of a parent type. A fragment spread is only valid if its type condition could ever apply within the parent type." - ) + failValidation( + s"${name.fold("Inline fragment spread")(n => s"Fragment spread '$n'")} is not possible: possible types are '${possibleTypes + .mkString(", ")}' and possible fragment types are '${possibleFragmentTypes.mkString(", ")}'.", + "Fragments are declared on a type and will only apply when the runtime object type matches the type condition. They also are spread within the context of a parent type. A fragment spread is only valid if its type condition could ever apply within the parent type." ) ) *> validateFields(context, selectionSet, fragmentType) } case None => lazy val typeConditionName = typeCondition.fold("?")(_.name) - IO.fail( - ValidationError( - s"${name.fold("Inline fragment spread")(n => s"Fragment spread '$n'")} targets an invalid type: '$typeConditionName'.", - "Fragments must be specified on types that exist in the schema. This applies for both named and inline fragments. If they are not defined in the schema, the query does not validate." - ) + failValidation( + s"${name.fold("Inline fragment spread")(n => s"Fragment spread '$n'")} targets an invalid type: '$typeConditionName'.", + "Fragments must be specified on types that exist in the schema. This applies for both named and inline fragments. If they are not defined in the schema, the query does not validate." ) } @@ -402,23 +381,19 @@ object Validator { case (arg, argValue) => f.args.find(_.name == arg) match { case None => - IO.fail( - ValidationError( - s"Argument '$arg' is not defined on field '${field.name}' of type '${currentType.name.getOrElse("")}'.", - "Every argument provided to a field or directive must be defined in the set of possible arguments of that field or directive." - ) + failValidation( + s"Argument '$arg' is not defined on field '${field.name}' of type '${currentType.name.getOrElse("")}'.", + "Every argument provided to a field or directive must be defined in the set of possible arguments of that field or directive." ) case Some(inputValue) => validateInputValues(inputValue, argValue) } } *> IO.foreach(f.args.filter(a => a.`type`().kind == __TypeKind.NON_NULL && a.defaultValue.isEmpty))(arg => IO.when(field.arguments.get(arg.name).forall(_ == NullValue))( - IO.fail( - ValidationError( - s"Required argument '${arg.name}' is null or missing on field '${field.name}' of type '${currentType.name - .getOrElse("")}'.", - "Arguments can be required. An argument is required if the argument type is non‐null and does not have a default value. Otherwise, the argument is optional." - ) + failValidation( + s"Required argument '${arg.name}' is null or missing on field '${field.name}' of type '${currentType.name + .getOrElse("")}'.", + "Arguments can be required. An argument is required if the argument type is non‐null and does not have a default value. Otherwise, the argument is optional." ) ) ) @@ -433,11 +408,9 @@ object Validator { case (k, v) => inputFields.find(_.name == k) match { case None => - IO.fail( - ValidationError( - s"Input field '$k' is not defined on type '${inputType.name.getOrElse("?")}'.", - "Every input field provided in an input object value must be defined in the set of possible fields of that input object’s expected type." - ) + failValidation( + s"Input field '$k' is not defined on type '${inputType.name.getOrElse("?")}'.", + "Every input field provided in an input object value must be defined in the set of possible fields of that input object’s expected type." ) case Some(value) => validateInputValues(value, v) } @@ -448,11 +421,9 @@ object Validator { inputField.`type`().kind == __TypeKind.NON_NULL && !fields.contains(inputField.name) )( - IO.fail( - ValidationError( - s"Required field '${inputField.name}' on object '${inputType.name.getOrElse("?")}' was not provided.", - "Input object fields may be required. Much like a field may have required arguments, an input object may have required fields. An input field is required if it has a non‐null type and does not have a default value. Otherwise, the input object field is optional." - ) + failValidation( + s"Required field '${inputField.name}' on object '${inputType.name.getOrElse("?")}' was not provided.", + "Input object fields may be required. Much like a field may have required arguments, an input object may have required fields. An input field is required if it has a non‐null type and does not have a default value. Otherwise, the input object field is optional." ) ) ) @@ -464,18 +435,14 @@ object Validator { private def validateLeafFieldSelection(selections: List[Selection], currentType: __Type): IO[ValidationError, Unit] = IO.whenCase(currentType.kind) { case __TypeKind.SCALAR | __TypeKind.ENUM if selections.nonEmpty => - IO.fail( - ValidationError( - s"Field selection is impossible on type '${currentType.name.getOrElse("")}'.", - "Field selections on scalars or enums are never allowed, because they are the leaf nodes of any GraphQL query." - ) + failValidation( + s"Field selection is impossible on type '${currentType.name.getOrElse("")}'.", + "Field selections on scalars or enums are never allowed, because they are the leaf nodes of any GraphQL query." ) case __TypeKind.INTERFACE | __TypeKind.UNION | __TypeKind.OBJECT if selections.isEmpty => - IO.fail( - ValidationError( - s"Field selection is mandatory on type '${currentType.name.getOrElse("")}'.", - "Leaf selections on objects, interfaces, and unions without subfields are disallowed." - ) + failValidation( + s"Field selection is mandatory on type '${currentType.name.getOrElse("")}'.", + "Leaf selections on objects, interfaces, and unions without subfields are disallowed." ) } @@ -483,11 +450,9 @@ object Validator { val names = operations.flatMap(_.name).groupBy(identity) val repeatedNames = names.collect { case (name, items) if items.length > 1 => name } IO.when(repeatedNames.nonEmpty)( - IO.fail( - ValidationError( - s"Multiple operations have the same name: ${repeatedNames.mkString(", ")}.", - "Each named operation definition must be unique within a document when referred to by its name." - ) + failValidation( + s"Multiple operations have the same name: ${repeatedNames.mkString(", ")}.", + "Each named operation definition must be unique within a document when referred to by its name." ) ) } @@ -495,11 +460,9 @@ object Validator { private def validateLoneAnonymousOperation(operations: List[OperationDefinition]): IO[ValidationError, Unit] = { val anonymous = operations.filter(_.name.isEmpty) IO.when(operations.length > 1 && anonymous.nonEmpty)( - IO.fail( - ValidationError( - "Found both anonymous and named operations.", - "GraphQL allows a short‐hand form for defining query operations when only that one operation exists in the document." - ) + failValidation( + "Found both anonymous and named operations.", + "GraphQL allows a short‐hand form for defining query operations when only that one operation exists in the document." ) ) } @@ -510,11 +473,9 @@ object Validator { IO.foldLeft(fragments)(Map.empty[String, FragmentDefinition]) { case (fragmentMap, fragment) => if (fragmentMap.contains(fragment.name)) { - IO.fail( - ValidationError( - s"Fragment '${fragment.name}' is defined more than once.", - "Fragment definitions are referenced in fragment spreads by name. To avoid ambiguity, each fragment’s name must be unique within a document." - ) + failValidation( + s"Fragment '${fragment.name}' is defined more than once.", + "Fragment definitions are referenced in fragment spreads by name. To avoid ambiguity, each fragment’s name must be unique within a document." ) } else IO.succeed(fragmentMap.updated(fragment.name, fragment)) } @@ -544,11 +505,9 @@ object Validator { case __TypeKind.UNION | __TypeKind.INTERFACE | __TypeKind.OBJECT => IO.unit case _ => val targetTypeName = targetType.name.getOrElse("") - IO.fail( - ValidationError( - s"${name.fold("Inline fragment")(n => s"Fragment '$n'")} is defined on invalid type '$targetTypeName'", - "Fragments can only be declared on unions, interfaces, and objects. They are invalid on scalars. They can only be applied on non‐leaf fields. This rule applies to both inline and named fragments." - ) + failValidation( + s"${name.fold("Inline fragment")(n => s"Fragment '$n'")} is defined on invalid type '$targetTypeName'", + "Fragments can only be declared on unions, interfaces, and objects. They are invalid on scalars. They can only be applied on non‐leaf fields. This rule applies to both inline and named fragments." ) } @@ -556,20 +515,18 @@ object Validator { t.enumValues(__DeprecatedArgs(Some(true))) match { case Some(_ :: _) => IO.unit case _ => - IO.fail( - ValidationError( - s"Enum ${t.name.getOrElse("")} doesn't contain any values", - "An Enum type must define one or more unique enum values." - ) + failValidation( + s"Enum ${t.name.getOrElse("")} doesn't contain any values", + "An Enum type must define one or more unique enum values." ) } private def validateInterface(t: __Type): IO[ValidationError, Unit] = { def validateName(name: String) = { if (name.isEmpty) { - IO.fail(ValidationError("msg", "explanatory")) + failValidation("msg", "explanatory") } else if (name.startsWith("__")) { - IO.fail(ValidationError("msg", "explanatory")) + failValidation("msg", "explanatory") } else { IO.unit } @@ -597,20 +554,16 @@ object Validator { t.fields(__DeprecatedArgs(Some(true))) match { case None | Some(Nil) => - IO.fail( - ValidationError( + failValidation( s"message", "explanatory" - ) ) case Some(fields) => duplicateFieldName(fields) match { case Some(value) => - IO.fail( - ValidationError( + failValidation( s"message with $value", "explanatory" - ) ) case None => IO.foreach(fields){ validateInterfaceField }.unit @@ -627,25 +580,77 @@ object Validator { t.possibleTypes match { case None | Some(Nil) => - IO.fail( - ValidationError( - s"Union ${t.name.getOrElse("")} doesn't contain any type.", - "A Union type must include one or more unique member types." - ) + failValidation( + s"Union ${t.name.getOrElse("")} doesn't contain any type.", + "A Union type must include one or more unique member types." ) case Some(types) if !types.forall(isObject) => - IO.fail( - ValidationError( - s"Union ${t.name.getOrElse("")} contains the following non Object types: " + - types.filterNot(isObject).map(_.name.getOrElse("")).filterNot(_.isEmpty).mkString("", ", ", "."), - s"The member types of a Union type must all be Object base types." - ) + failValidation( + s"Union ${t.name.getOrElse("")} contains the following non Object types: " + + types.filterNot(isObject).map(_.name.getOrElse("")).filterNot(_.isEmpty).mkString("", ", ", "."), + s"The member types of a Union type must all be Object base types." ) case _ => IO.unit } } + private def validateInputObject(t: __Type): IO[ValidationError, Unit] = { + // https://spec.graphql.org/June2018/#IsInputType() + def isInputType(t: __Type): Either[__Type, Unit] = t.kind match { + case __TypeKind.LIST | __TypeKind.NON_NULL => t.ofType.fold[Either[__Type, Unit]](Left(t))(isInputType) + case __TypeKind.SCALAR | __TypeKind.ENUM | __TypeKind.INPUT_OBJECT => Right(()) + case _ => Left(t) + } + + def validateFields(fields: List[__InputValue]): IO[ValidationError, Unit] = + duplicateFieldName(fields) <* + IO.foreach(fields)(field => + for { + _ <- doesNotStartWithUnderscore(field) + _ <- onlyInputFieldType(field) + } yield () + ) + + def duplicateFieldName(fields: List[__InputValue]): IO[ValidationError, Unit] = + fields + .groupBy(_.name) + .collectFirst { case (_, f :: _ :: _) => f } + .fold[IO[ValidationError, Unit]](IO.unit)(duplicateField => + failValidation( + s"InputObject has repeated fields: ${duplicateField.name}", + "The input field must have a unique name within that Input Object type; no two input fields may share the same name" + ) + ) + + def doesNotStartWithUnderscore(field: __InputValue): IO[ValidationError, Unit] = + IO.when(field.name.startsWith("__"))( + failValidation( + s"InputObject can't start with '__': ${field.name}", + """The input field must not have a name which begins with the +characters {"__"} (two underscores)""" + ) + ) + + def onlyInputFieldType(field: __InputValue): IO[ValidationError, Unit] = + IO.whenCase(isInputType(field.`type`())) { + case Left(errorType) => + failValidation( + s"${errorType.name.getOrElse("")} is of kind ${errorType.kind}, must be an InputType", + """The input field must accept a type where IsInputType(inputFieldType) returns true, https://spec.graphql.org/June2018/#IsInputType()""" + ) + } + + t.inputFields match { + case None | Some(Nil) => + failValidation( + s"InputObject ${t.name.getOrElse("")} does not have fields", + "An Input Object type must define one or more input fields" + ) + case Some(fields) => validateFields(fields) + } + } + case class Context( document: Document, rootType: RootType, diff --git a/core/src/test/scala/caliban/RenderingSpec.scala b/core/src/test/scala/caliban/RenderingSpec.scala index 58357853af..58ac23200a 100644 --- a/core/src/test/scala/caliban/RenderingSpec.scala +++ b/core/src/test/scala/caliban/RenderingSpec.scala @@ -27,7 +27,6 @@ object RenderingSpec | name: String! @external | nicknames: [String!]! @required | origin: Origin! - | role: Role |} | |type Captain { diff --git a/core/src/test/scala/caliban/TestUtils.scala b/core/src/test/scala/caliban/TestUtils.scala index 90b362a616..81dfc425c3 100644 --- a/core/src/test/scala/caliban/TestUtils.scala +++ b/core/src/test/scala/caliban/TestUtils.scala @@ -4,7 +4,7 @@ import caliban.TestUtils.Origin._ import caliban.TestUtils.Role._ import caliban.Value.StringValue import caliban.parsing.adt.Directive -import caliban.schema.Annotations.{ GQLDeprecated, GQLDescription, GQLDirective, GQLInterface } +import caliban.schema.Annotations.{ GQLDeprecated, GQLDescription, GQLDirective, GQLInputName, GQLInterface } import caliban.schema.Schema import zio.UIO import zio.stream.ZStream @@ -44,6 +44,13 @@ object TestUtils { role: Option[Role] ) + @GQLInputName("CharacterInput") + case class CharacterInput( + @GQLDirective(Directive("external")) name: String, + @GQLDirective(Directive("required")) nicknames: List[String], + origin: Origin + ) + object Character { implicit val schema: Schema[Any, Character] = Schema.gen[Character] } @@ -61,7 +68,7 @@ object TestUtils { case class CharactersArgs(origin: Option[Origin]) case class CharacterArgs(name: String) case class CharacterInArgs(@GQLDirective(Directive("lowercase")) names: List[String]) - case class CharacterObjectArgs(character: Character) + case class CharacterObjectArgs(character: CharacterInput) @GQLDescription("Queries") case class Query( @@ -107,4 +114,28 @@ object TestUtils { SubscriptionIO(ZStream.empty) ) + object InvalidSchemas { + case class DoubleUnderscoreArg(__name: String) + case class DoubleUnderscoreInputObjectArg(wrong: DoubleUnderscoreArg) + case class WrongMutationUnderscore(w: DoubleUnderscoreInputObjectArg => UIO[Unit]) + + val resolverWrongMutationUnderscore = RootResolver( + resolverIO.queryResolver, + WrongMutationUnderscore(_ => UIO.unit) + ) + + sealed trait UnionInput + object UnionInput { + case class A(value: String) extends UnionInput + case class B(value: String) extends UnionInput + } + case class UnionArg(union: UnionInput) + case class UnionInputObjectArg(wrong: UnionArg) + case class WrongMutationUnion(w: UnionInputObjectArg => UIO[Unit]) + + val resolverWrongMutationUnion = RootResolver( + resolverIO.queryResolver, + WrongMutationUnion(_ => UIO.unit) + ) + } } diff --git a/core/src/test/scala/caliban/validation/ValidationSchemaSpec.scala b/core/src/test/scala/caliban/validation/ValidationSchemaSpec.scala new file mode 100644 index 0000000000..80a5c22495 --- /dev/null +++ b/core/src/test/scala/caliban/validation/ValidationSchemaSpec.scala @@ -0,0 +1,32 @@ +package caliban.validation + +import caliban.CalibanError.ValidationError +import caliban.GraphQL +import caliban.GraphQL.graphQL +import caliban.TestUtils.InvalidSchemas._ +import zio.IO +import zio.test.Assertion._ +import zio.test._ + +object ValidationSchemaSpec + extends DefaultRunnableSpec({ + def check(gql: GraphQL[Any], expectedMessage: String): IO[ValidationError, TestResult] = + assertM(gql.interpreter.run, fails[ValidationError](hasField("msg", _.msg, equalTo(expectedMessage)))) + + suite("ValidationSchemaSpec")({ + suite("InputObjects")( + testM("name can't start with '__'") { + check( + graphQL(resolverWrongMutationUnderscore), + "InputObject can't start with '__': __name" + ) + }, + testM("should only contain types for which IsInputType(type) is true") { + check( + graphQL(resolverWrongMutationUnion), + "UnionInput is of kind UNION, must be an InputType" + ) + } + ) + }) + }) diff --git a/docs/404.html b/docs/404.html index a5e5b9bdd9..d62617e799 100644 --- a/docs/404.html +++ b/docs/404.html @@ -4,17 +4,17 @@ Caliban - - + + - - + +

404

Looks like we've got some broken links.
Take me home.
- + diff --git a/docs/about/index.html b/docs/about/index.html index 4d5989677c..38e14b774a 100644 --- a/docs/about/index.html +++ b/docs/about/index.html @@ -4,12 +4,12 @@ About | Caliban - - + + - - + +
- +

# About

Caliban is a project developed by Pierre Ricadat aka @ghostdogpr.

The name is inspired by the SF novel and tv series The Expanse.

Thanks:

+ diff --git a/docs/assets/css/0.styles.ce455d84.css b/docs/assets/css/0.styles.3f660e69.css similarity index 100% rename from docs/assets/css/0.styles.ce455d84.css rename to docs/assets/css/0.styles.3f660e69.css diff --git a/docs/assets/js/10.56cd42ab.js b/docs/assets/js/10.56cd42ab.js new file mode 100644 index 0000000000..3f7cc32922 --- /dev/null +++ b/docs/assets/js/10.56cd42ab.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[10],{210:function(t,a,s){"use strict";s.r(a);var n=s(28),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"interop-cats-monix"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#interop-cats-monix"}},[t._v("#")]),t._v(" Interop (Cats, Monix)")]),t._v(" "),s("p",[t._v("If you prefer using "),s("a",{attrs:{href:"https://github.com/typelevel/cats-effect",target:"_blank",rel:"noopener noreferrer"}},[t._v("Cats Effect"),s("OutboundLink")],1),t._v(" or "),s("a",{attrs:{href:"https://github.com/monix/monix",target:"_blank",rel:"noopener noreferrer"}},[t._v("Monix"),s("OutboundLink")],1),t._v(" rather than ZIO, you can use the respective "),s("code",[t._v("caliban-cats")]),t._v(" and "),s("code",[t._v("caliban-monix")]),t._v(" modules.")]),t._v(" "),s("h2",{attrs:{id:"cats-effect"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#cats-effect"}},[t._v("#")]),t._v(" Cats Effect")]),t._v(" "),s("p",[t._v("You first need to import "),s("code",[t._v("caliban.interop.cats.implicits._")]),t._v(" and have an implicit "),s("code",[t._v("zio.Runtime")]),t._v(" in scope. Then a few helpers are available:")]),t._v(" "),s("ul",[s("li",[t._v("the GraphQL object is enriched with "),s("code",[t._v("interpreterAsync")]),t._v(", "),s("code",[t._v("executeAsync")]),t._v(" and "),s("code",[t._v("checkAsync")]),t._v(", variants of "),s("code",[t._v("interpreter")]),t._v(", "),s("code",[t._v("execute")]),t._v(" and "),s("code",[t._v("check")]),t._v(" that return an "),s("code",[t._v("F[_]: Async")]),t._v(" instead of a "),s("code",[t._v("ZIO")]),t._v(".")]),t._v(" "),s("li",[t._v("the "),s("code",[t._v("Http4sAdapter")]),t._v(" also has cats-effect variants named "),s("code",[t._v("makeRestServiceF")]),t._v(" and "),s("code",[t._v("makeWebSocketServiceF")]),t._v(".")])]),t._v(" "),s("p",[t._v("In addition to that, a "),s("code",[t._v("Schema")]),t._v(" for any "),s("code",[t._v("F[_]: Effect")]),t._v(" is provided. That means you can include fields returning Monix Task for Cats IO in your queries, mutations or subscriptions.")]),t._v(" "),s("p",[t._v("The following example shows how to create an interpreter and run a query while only using Cats IO.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" caliban"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("GraphQL"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("graphQL\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" caliban"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("RootResolver\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("caliban"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("interop"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("cats"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("implicits")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("cats"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("effect")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" ExitCode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" IO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" IOApp "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" zio"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("DefaultRuntime\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" ExampleCatsInterop "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" IOApp "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("implicit")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" runtime "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" DefaultRuntime "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" Queries"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("numbers"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" List"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" randomNumber"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" IO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" queries "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Queries"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("List"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" IO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("scala"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("util"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Random"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("nextInt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" api "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" graphQL"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("RootResolver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("queries"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" query "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token triple-quoted-string string"}},[t._v('"""\n {\n numbers\n randomNumber\n }"""')]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" run"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("args"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" List"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" IO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("ExitCode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n interpreter "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" api"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("interpreterAsync"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("IO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n result "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" interpreter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("executeAsync"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("IO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("query"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n _ "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" IO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("println"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("data"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" ExitCode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Success\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("You can find this example within the "),s("a",{attrs:{href:"https://github.com/ghostdogpr/caliban/blob/master/examples/src/main/scala/caliban/interop/cats/ExampleCatsInterop.scala",target:"_blank",rel:"noopener noreferrer"}},[t._v("examples"),s("OutboundLink")],1),t._v(" project.")]),t._v(" "),s("h2",{attrs:{id:"monix"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#monix"}},[t._v("#")]),t._v(" Monix")]),t._v(" "),s("p",[t._v("You first need to import "),s("code",[t._v("caliban.interop.monix.implicits._")]),t._v(" and have an implicit "),s("code",[t._v("zio.Runtime")]),t._v(" in scope. Then a few helpers are available:")]),t._v(" "),s("ul",[s("li",[t._v("the GraphQL object is enriched with "),s("code",[t._v("interpreterAsync")]),t._v(", "),s("code",[t._v("executeAsync")]),t._v(" and "),s("code",[t._v("checkAsync")]),t._v(", variants of "),s("code",[t._v("interpreter")]),t._v(", "),s("code",[t._v("execute")]),t._v(" and "),s("code",[t._v("check")]),t._v(" that return a Monix "),s("code",[t._v("Task")]),t._v(" instead of a "),s("code",[t._v("ZIO")]),t._v(".")])]),t._v(" "),s("p",[t._v("In addition to that, a "),s("code",[t._v("Schema")]),t._v(" for any Monix "),s("code",[t._v("Task")]),t._v(" as well as "),s("code",[t._v("Observable")]),t._v(" is provided.")]),t._v(" "),s("p",[t._v("The following example shows how to create an interpreter and run a query while only using Monix Task.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" caliban"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("GraphQL"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("graphQL\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" caliban"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("RootResolver\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("caliban"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("interop"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("monix"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("implicits")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("cats"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("effect")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ExitCode\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("monix"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("eval")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" TaskApp "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("monix"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("execution")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Scheduler\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" zio"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("DefaultRuntime\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" ExampleMonixInterop "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" TaskApp "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("implicit")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" runtime "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" DefaultRuntime "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("implicit")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" monixScheduler"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Scheduler "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" scheduler\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" Queries"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("numbers"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" List"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" randomNumber"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" queries "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Queries"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("List"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("eval"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("scala"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("util"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Random"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("nextInt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" api "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" graphQL"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("RootResolver"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("queries"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" query "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token triple-quoted-string string"}},[t._v('"""\n {\n numbers\n randomNumber\n }"""')]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" run"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("args"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" List"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("ExitCode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n interpreter "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" api"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("interpreterAsync\n result "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" interpreter"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("executeAsync"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("query"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n _ "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("eval"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("println"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("data"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" ExitCode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Success\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("You can find this example within the "),s("a",{attrs:{href:"https://github.com/ghostdogpr/caliban/blob/master/examples/src/main/scala/caliban/interop/monix/ExampleMonixInterop.scala",target:"_blank",rel:"noopener noreferrer"}},[t._v("examples"),s("OutboundLink")],1),t._v(" project.")])])}),[],!1,null,null,null);a.default=e.exports}}]); \ No newline at end of file diff --git a/docs/assets/js/10.b8fd5856.js b/docs/assets/js/10.b8fd5856.js deleted file mode 100644 index 503d31a306..0000000000 --- a/docs/assets/js/10.b8fd5856.js +++ /dev/null @@ -1 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[10],{210:function(t,e,r){"use strict";r.r(e);var n=r(0),a=Object(n.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[r("h1",{attrs:{id:"introspection"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#introspection"}},[t._v("#")]),t._v(" Introspection")]),t._v(" "),r("p",[t._v("Introspection queries are fully supported, which means you can use your favorite tool to inspect your schema and generate documentation for free.")]),t._v(" "),r("p",[t._v("Here's an example of documentation generated by introspection in "),r("a",{attrs:{href:"https://altair.sirmuel.design/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Altair GraphQL Client"),r("OutboundLink")],1),t._v(":")]),t._v(" "),r("p",[r("img",{attrs:{src:"/caliban/altair.png",alt:"altair screenshot"}})])])}),[],!1,null,null,null);e.default=a.exports}}]); \ No newline at end of file diff --git a/docs/assets/js/11.151b57de.js b/docs/assets/js/11.151b57de.js deleted file mode 100644 index 466bf0afb6..0000000000 --- a/docs/assets/js/11.151b57de.js +++ /dev/null @@ -1 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[11],{209:function(t,a,e){"use strict";e.r(a);var s=e(0),n=Object(s.a)({},(function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("h1",{attrs:{id:"middleware"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#middleware"}},[t._v("#")]),t._v(" Middleware")]),t._v(" "),e("p",[t._v("Caliban allows you to perform additional actions at various levels of a query processing, via the concept of "),e("code",[t._v("Wrapper")]),t._v(". Using wrappers, you can:")]),t._v(" "),e("ul",[e("li",[t._v("verify that a query doesn't reach some limit (e.g. depth, complexity)")]),t._v(" "),e("li",[t._v("modify a query before it's executed")]),t._v(" "),e("li",[t._v("add timeouts to queries or fields")]),t._v(" "),e("li",[t._v("log each field execution time")]),t._v(" "),e("li",[t._v("support "),e("a",{attrs:{href:"https://github.com/apollographql/apollo-tracing",target:"_blank",rel:"noopener noreferrer"}},[t._v("Apollo Tracing"),e("OutboundLink")],1),t._v(" or anything similar")]),t._v(" "),e("li",[t._v("etc.")])]),t._v(" "),e("h2",{attrs:{id:"wrapper-types"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#wrapper-types"}},[t._v("#")]),t._v(" Wrapper types")]),t._v(" "),e("p",[t._v("There are 5 basic types of wrappers:")]),t._v(" "),e("ul",[e("li",[e("code",[t._v("OverallWrapper")]),t._v(" to wrap the whole query processing")]),t._v(" "),e("li",[e("code",[t._v("ParsingWrapper")]),t._v(" to wrap the query parsing only")]),t._v(" "),e("li",[e("code",[t._v("ValidationWrapper")]),t._v(" to wrap the query validation only")]),t._v(" "),e("li",[e("code",[t._v("ExecutionWrapper")]),t._v(" to wrap the query execution only")]),t._v(" "),e("li",[e("code",[t._v("FieldWrapper")]),t._v(" to wrap each field execution")])]),t._v(" "),e("p",[t._v("Each one requires a function that takes a "),e("code",[t._v("ZIO")]),t._v(" or "),e("code",[t._v("ZQuery")]),t._v(" computation together with some contextual information (e.g. the query string) and should return another computation.")]),t._v(" "),e("p",[t._v("Let's see how to implement a wrapper that times out the whole query if its processing takes longer that 1 minute.")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" wrapper "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" OverallWrapper "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("io"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" query"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v("\n io"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("timeout"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" minute"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("map"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n _"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("getOrElse"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n GraphQLResponse"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n NullValue"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n List"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ExecutionError"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Query was interrupted after timeout of ${duration.render}:\\n$query"')]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),e("p",[t._v("You can also combine wrappers using "),e("code",[t._v("|+|")]),t._v(" and create a wrapper that requires an effect to be run at each query using "),e("code",[t._v("EffectfulWrapper")]),t._v(".")]),t._v(" "),e("p",[t._v("To use your wrapper, call "),e("code",[t._v("GraphQL#withWrapper")]),t._v(" or its alias "),e("code",[t._v("@@")]),t._v(".")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" api "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" graphQL"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("withWrapper"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("wrapper"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// or")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" api "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" graphQL"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" @@ wrapper\n")])])]),e("h2",{attrs:{id:"pre-defined-wrappers"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#pre-defined-wrappers"}},[t._v("#")]),t._v(" Pre-defined wrappers")]),t._v(" "),e("p",[t._v("Caliban comes with a few pre-made wrappers in "),e("code",[t._v("caliban.wrappers.Wrappers")]),t._v(":")]),t._v(" "),e("ul",[e("li",[e("code",[t._v("maxDepth")]),t._v(" returns a wrapper that fails queries whose depth is higher than a given value")]),t._v(" "),e("li",[e("code",[t._v("maxFields")]),t._v(" returns a wrapper that fails queries whose number of fields is higher than a given value")]),t._v(" "),e("li",[e("code",[t._v("timeout")]),t._v(" returns a wrapper that fails queries taking more than a specified time")]),t._v(" "),e("li",[e("code",[t._v("printSlowQueries")]),t._v(" returns a wrapper that prints slow queries")]),t._v(" "),e("li",[e("code",[t._v("onSlowQueries")]),t._v(" returns a wrapper that can run a given function on slow queries")])]),t._v(" "),e("p",[t._v("In addition to those, "),e("code",[t._v("caliban.wrappers.ApolloTracing.apolloTracing")]),t._v(" returns a wrapper that adds tracing data into the "),e("code",[t._v("extensions")]),t._v(" field of each response following "),e("a",{attrs:{href:"https://github.com/apollographql/apollo-tracing",target:"_blank",rel:"noopener noreferrer"}},[t._v("Apollo Tracing"),e("OutboundLink")],1),t._v(" format.")]),t._v(" "),e("p",[t._v("They can be used like this:")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" api "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n graphQL"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" @@\n maxDepth"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("50")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" @@\n timeout"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(" seconds"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" @@\n printSlowQueries"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("500")]),t._v(" millis"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" @@\n apolloTracing\n")])])]),e("h2",{attrs:{id:"wrapping-the-interpreter"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#wrapping-the-interpreter"}},[t._v("#")]),t._v(" Wrapping the interpreter")]),t._v(" "),e("p",[t._v("All the wrappers mentioned above require that you don't modify the environment "),e("code",[t._v("R")]),t._v(" and the error type which is always a "),e("code",[t._v("CalibanError")]),t._v(". It is also possible to wrap your "),e("code",[t._v("GraphQLInterpreter")]),t._v(" by calling "),e("code",[t._v("wrapExecutionWith")]),t._v(" on it. This method takes in a function "),e("code",[t._v("f")]),t._v(" and returns a new "),e("code",[t._v("GraphQLInterpreter")]),t._v(" that will wrap the "),e("code",[t._v("execute")]),t._v(" method with this function "),e("code",[t._v("f")]),t._v(".")]),t._v(" "),e("p",[t._v("It is used internally to implement "),e("code",[t._v("mapError")]),t._v(" (customize errors) and "),e("code",[t._v("provide")]),t._v(" (eliminate the environment), but you can use it for other purposes such as adding a general timeout, logging response times, etc.")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// create an interpreter")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" i"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" GraphQLInterpreter"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("MyEnv"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" CalibanError"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" graphqQL"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("interpreter\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// change error type to String")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" i2"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" GraphQLInterpreter"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("MyEnv"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" i"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mapError"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("toString"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// provide the environment")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" i3"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" GraphQLInterpreter"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),e("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" CalibanError"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" i"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("provide"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("myEnv"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// add a timeout on every query execution")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" i4"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" GraphQLInterpreter"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("MyEnv "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" Clock"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" CalibanError"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n i"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("wrapExecutionWith"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n _"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("timeout"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("30")]),t._v(" seconds"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("map"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n _"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("getOrElse"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("GraphQLResponse"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("NullValue"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" List"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ExecutionError"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Timeout!"')]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])])}),[],!1,null,null,null);a.default=n.exports}}]); \ No newline at end of file diff --git a/docs/assets/js/11.5f34d6b0.js b/docs/assets/js/11.5f34d6b0.js new file mode 100644 index 0000000000..85cc705201 --- /dev/null +++ b/docs/assets/js/11.5f34d6b0.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[11],{209:function(t,e,r){"use strict";r.r(e);var n=r(28),a=Object(n.a)({},(function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[r("h1",{attrs:{id:"introspection"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#introspection"}},[t._v("#")]),t._v(" Introspection")]),t._v(" "),r("p",[t._v("Introspection queries are fully supported, which means you can use your favorite tool to inspect your schema and generate documentation for free.")]),t._v(" "),r("p",[t._v("Here's an example of documentation generated by introspection in "),r("a",{attrs:{href:"https://altair.sirmuel.design/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Altair GraphQL Client"),r("OutboundLink")],1),t._v(":")]),t._v(" "),r("p",[r("img",{attrs:{src:"/caliban/altair.png",alt:"altair screenshot"}})])])}),[],!1,null,null,null);e.default=a.exports}}]); \ No newline at end of file diff --git a/docs/assets/js/12.381b7c5a.js b/docs/assets/js/12.381b7c5a.js new file mode 100644 index 0000000000..f77e36b824 --- /dev/null +++ b/docs/assets/js/12.381b7c5a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[12],{211:function(t,a,e){"use strict";e.r(a);var s=e(28),n=Object(s.a)({},(function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("h1",{attrs:{id:"middleware"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#middleware"}},[t._v("#")]),t._v(" Middleware")]),t._v(" "),e("p",[t._v("Caliban allows you to perform additional actions at various levels of a query processing, via the concept of "),e("code",[t._v("Wrapper")]),t._v(". Using wrappers, you can:")]),t._v(" "),e("ul",[e("li",[t._v("verify that a query doesn't reach some limit (e.g. depth, complexity)")]),t._v(" "),e("li",[t._v("modify a query before it's executed")]),t._v(" "),e("li",[t._v("add timeouts to queries or fields")]),t._v(" "),e("li",[t._v("log each field execution time")]),t._v(" "),e("li",[t._v("support "),e("a",{attrs:{href:"https://github.com/apollographql/apollo-tracing",target:"_blank",rel:"noopener noreferrer"}},[t._v("Apollo Tracing"),e("OutboundLink")],1),t._v(", "),e("a",{attrs:{href:"https://github.com/apollographql/apollo-cache-control",target:"_blank",rel:"noopener noreferrer"}},[t._v("Apollo Caching"),e("OutboundLink")],1),t._v(" or anything similar")]),t._v(" "),e("li",[t._v("etc.")])]),t._v(" "),e("h2",{attrs:{id:"wrapper-types"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#wrapper-types"}},[t._v("#")]),t._v(" Wrapper types")]),t._v(" "),e("p",[t._v("There are 5 basic types of wrappers:")]),t._v(" "),e("ul",[e("li",[e("code",[t._v("OverallWrapper")]),t._v(" to wrap the whole query processing")]),t._v(" "),e("li",[e("code",[t._v("ParsingWrapper")]),t._v(" to wrap the query parsing only")]),t._v(" "),e("li",[e("code",[t._v("ValidationWrapper")]),t._v(" to wrap the query validation only")]),t._v(" "),e("li",[e("code",[t._v("ExecutionWrapper")]),t._v(" to wrap the query execution only")]),t._v(" "),e("li",[e("code",[t._v("FieldWrapper")]),t._v(" to wrap each field execution")])]),t._v(" "),e("p",[t._v("Each one requires a function that takes a "),e("code",[t._v("ZIO")]),t._v(" or "),e("code",[t._v("ZQuery")]),t._v(" computation together with some contextual information (e.g. the query string) and should return another computation.")]),t._v(" "),e("p",[t._v("Let's see how to implement a wrapper that times out the whole query if its processing takes longer that 1 minute.")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" wrapper "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" OverallWrapper "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("io"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" query"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v("\n io"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("timeout"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" minute"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("map"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n _"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("getOrElse"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n GraphQLResponse"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n NullValue"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n List"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ExecutionError"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Query was interrupted after timeout of ${duration.render}:\\n$query"')]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),e("p",[t._v("You can also combine wrappers using "),e("code",[t._v("|+|")]),t._v(" and create a wrapper that requires an effect to be run at each query using "),e("code",[t._v("EffectfulWrapper")]),t._v(".")]),t._v(" "),e("p",[t._v("To use your wrapper, call "),e("code",[t._v("GraphQL#withWrapper")]),t._v(" or its alias "),e("code",[t._v("@@")]),t._v(".")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" api "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" graphQL"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("withWrapper"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("wrapper"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// or")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" api "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" graphQL"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" @@ wrapper\n")])])]),e("h2",{attrs:{id:"pre-defined-wrappers"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#pre-defined-wrappers"}},[t._v("#")]),t._v(" Pre-defined wrappers")]),t._v(" "),e("p",[t._v("Caliban comes with a few pre-made wrappers in "),e("code",[t._v("caliban.wrappers.Wrappers")]),t._v(":")]),t._v(" "),e("ul",[e("li",[e("code",[t._v("maxDepth")]),t._v(" returns a wrapper that fails queries whose depth is higher than a given value")]),t._v(" "),e("li",[e("code",[t._v("maxFields")]),t._v(" returns a wrapper that fails queries whose number of fields is higher than a given value")]),t._v(" "),e("li",[e("code",[t._v("timeout")]),t._v(" returns a wrapper that fails queries taking more than a specified time")]),t._v(" "),e("li",[e("code",[t._v("printSlowQueries")]),t._v(" returns a wrapper that prints slow queries")]),t._v(" "),e("li",[e("code",[t._v("onSlowQueries")]),t._v(" returns a wrapper that can run a given function on slow queries")])]),t._v(" "),e("p",[t._v("In addition to those, Caliban also ships with some non-spec but standard wrappers")]),t._v(" "),e("ul",[e("li",[e("code",[t._v("caliban.wrappers.ApolloTracing.apolloTracing")]),t._v(" returns a wrapper that adds tracing data into the "),e("code",[t._v("extensions")]),t._v(" field of each response following "),e("a",{attrs:{href:"https://github.com/apollographql/apollo-tracing",target:"_blank",rel:"noopener noreferrer"}},[t._v("Apollo Tracing"),e("OutboundLink")],1),t._v(" format.")]),t._v(" "),e("li",[e("code",[t._v("caliban.wrappers.ApolloCaching.apolloCaching")]),t._v(" returns a wrapper that adds caching hints to properly annotated fields using the "),e("a",{attrs:{href:"https://github.com/apollographql/apollo-cache-control",target:"_blank",rel:"noopener noreferrer"}},[t._v("Apollo Caching"),e("OutboundLink")],1),t._v(" format.")])]),t._v(" "),e("p",[t._v("They can be used like this:")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" api "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n graphQL"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" @@\n maxDepth"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("50")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" @@\n timeout"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(" seconds"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" @@\n printSlowQueries"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("500")]),t._v(" millis"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" @@\n apolloTracing @@\n apolloCaching\n")])])]),e("h2",{attrs:{id:"wrapping-the-interpreter"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#wrapping-the-interpreter"}},[t._v("#")]),t._v(" Wrapping the interpreter")]),t._v(" "),e("p",[t._v("All the wrappers mentioned above require that you don't modify the environment "),e("code",[t._v("R")]),t._v(" and the error type which is always a "),e("code",[t._v("CalibanError")]),t._v(". It is also possible to wrap your "),e("code",[t._v("GraphQLInterpreter")]),t._v(" by calling "),e("code",[t._v("wrapExecutionWith")]),t._v(" on it. This method takes in a function "),e("code",[t._v("f")]),t._v(" and returns a new "),e("code",[t._v("GraphQLInterpreter")]),t._v(" that will wrap the "),e("code",[t._v("execute")]),t._v(" method with this function "),e("code",[t._v("f")]),t._v(".")]),t._v(" "),e("p",[t._v("It is used internally to implement "),e("code",[t._v("mapError")]),t._v(" (customize errors) and "),e("code",[t._v("provide")]),t._v(" (eliminate the environment), but you can use it for other purposes such as adding a general timeout, logging response times, etc.")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" i"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" GraphQLInterpreter"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("MyEnv"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" CalibanError"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// change error type to String")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" i2"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" GraphQLInterpreter"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("MyEnv"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" i"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mapError"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("toString"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// provide the environment")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" i3"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" GraphQLInterpreter"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),e("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" CalibanError"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" i"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("provide"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("myEnv"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// add a timeout on every query execution")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" i4"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" GraphQLInterpreter"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("MyEnv "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" Clock"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" CalibanError"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n i"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("wrapExecutionWith"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n _"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("timeout"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("30")]),t._v(" seconds"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("map"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n _"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("getOrElse"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("GraphQLResponse"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("NullValue"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" List"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ExecutionError"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Timeout!"')]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])])}),[],!1,null,null,null);a.default=n.exports}}]); \ No newline at end of file diff --git a/docs/assets/js/12.40588870.js b/docs/assets/js/12.40588870.js deleted file mode 100644 index 39f241314f..0000000000 --- a/docs/assets/js/12.40588870.js +++ /dev/null @@ -1 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[12],{211:function(t,a,s){"use strict";s.r(a);var e=s(0),n=Object(e.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"query-optimization"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#query-optimization"}},[t._v("#")]),t._v(" Query optimization")]),t._v(" "),s("p",[t._v("A GraphQL query may request multiple fields that are using the same resolver. It's not a problem if the resolver is a simple value, but it can be less than optimal when the resolver runs an effect (such as reading from a database).")]),t._v(" "),s("p",[t._v("We might want to:")]),t._v(" "),s("ul",[s("li",[s("strong",[t._v("cache")]),t._v(" identical queries (deduplication)")]),t._v(" "),s("li",[s("strong",[t._v("batch")]),t._v(" queries to the same source")])]),t._v(" "),s("p",[t._v("This is possible in Caliban using the "),s("code",[t._v("ZQuery")]),t._v(" data type.")]),t._v(" "),s("h2",{attrs:{id:"introducing-zquery"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#introducing-zquery"}},[t._v("#")]),t._v(" Introducing ZQuery")]),t._v(" "),s("p",[t._v("A "),s("code",[t._v("ZQuery[R, E, A]")]),t._v(" is a purely functional description of an effectual query that may contain requests to one or more data sources. Similarly to "),s("code",[t._v("ZIO[R, E, A]")]),t._v(", it requires an environment "),s("code",[t._v("R")]),t._v(", may fail with an "),s("code",[t._v("E")]),t._v(" or succeed with an "),s("code",[t._v("A")]),t._v(". All requests that do not need to be performed sequentially will automatically be batched, allowing for aggressive data source specific optimizations. Requests will also automatically be deduplicated and cached.")]),t._v(" "),s("p",[t._v("This allows for writing queries in a high level, compositional style, with confidence that they will automatically be optimized. For example, consider the following query from a user service.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" getAllUserIds"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ZQuery"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Nothing")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" List"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" getUserNameById"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ZQuery"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Nothing")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n userIds "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" getAllUserIds\n userNames "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" ZQuery"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("foreachPar"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("userIds"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("getUserNameById"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" userNames\n")])])]),s("p",[t._v("This would normally require N + 1 queries, one for "),s("code",[t._v("getAllUserIds")]),t._v(" and one for each call to "),s("code",[t._v("getUserNameById")]),t._v(". In contrast, "),s("code",[t._v("ZQuery")]),t._v(" will automatically optimize this to two queries, one for "),s("code",[t._v("userIds")]),t._v(" and one for "),s("code",[t._v("userNames")]),t._v(", assuming an implementation of the user service that supports batching.")]),t._v(" "),s("h2",{attrs:{id:"building-a-datasource"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#building-a-datasource"}},[t._v("#")]),t._v(" Building a DataSource")]),t._v(" "),s("p",[t._v("To build a "),s("code",[t._v("ZQuery")]),t._v(" that executes a request, you first need to build a "),s("code",[t._v("DataSource")]),t._v(". A "),s("code",[t._v("DataSource[R, E, A]")]),t._v(" defines how to execute requests of type "),s("code",[t._v("A")]),t._v(" and it requires 2 things:")]),t._v(" "),s("ul",[s("li",[t._v("an "),s("code",[t._v("identifier")]),t._v(" that uniquely identifies the data source (requests from "),s("em",[t._v("different")]),t._v(" data sources will "),s("em",[t._v("not")]),t._v(" be batched together)")]),t._v(" "),s("li",[t._v("a effectful function "),s("code",[t._v("run")]),t._v(" from an "),s("code",[t._v("Iterable")]),t._v(" of requests to a "),s("code",[t._v("Map")]),t._v(" of requests and results")])]),t._v(" "),s("p",[t._v("Let's consider "),s("code",[t._v("getUserNameById")]),t._v(" from the previous example. We need to define a corresponding request type that extends "),s("code",[t._v("zquery.Request")]),t._v(" for a given response type:")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" GetUserName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" Request"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Throwable"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n")])])]),s("p",[t._v("Now let's build the corresponding "),s("code",[t._v("DataSource")]),t._v(". We need to implement the following functions:")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" UserDataSource "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" DataSource"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" GetUserName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" identifier"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" run"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("requests"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Iterable"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("GetUserName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ZIO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Throwable"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" CompletedRequestMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v('We will use "UserDataSource" as our identifier. This name should not be reused for other data sources.')]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" identifier"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"UserDataSource"')]),t._v("\n")])])]),s("p",[t._v("We will define two different behaviors depending on whether we receive a single request or multiple requests at once.\nFor each request, we need to insert into the result map a value of type "),s("code",[t._v("Either")]),t._v(" ("),s("code",[t._v("Left")]),t._v(" for an error and "),s("code",[t._v("Right")]),t._v(" for a success).")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" run"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("requests"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Iterable"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("GetUserName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ZIO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Nothing")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" CompletedRequestMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" resultMap "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" CompletedRequestMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("empty\n requests"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("toList "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("match")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" request "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("::")]),t._v(" Nil "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// get user by ID e.g. SELECT name FROM users WHERE id = $id")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" result"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v("\n result"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("either"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("map"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("resultMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("insert"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("request"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" batch "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// get multiple users at once e.g. SELECT id, name FROM users WHERE id IN ($ids)")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" result"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("List"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v("\n result"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("fold"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n err "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" requests"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("foldLeft"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("resultMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("map"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" req"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" map"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("insert"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("req"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Left"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n _"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("foldLeft"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("resultMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("map"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" map"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("insert"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("GetUserName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Right"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("Now to build a "),s("code",[t._v("ZQuery")]),t._v(" from it, we can use "),s("code",[t._v("ZQuery.fromRequest")]),t._v(" and just pass the request and the data source:")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" getUserNameById"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ZQuery"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Throwable"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n ZQuery"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("fromRequest"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("GetUserName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("UserDataSource"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("To run a "),s("code",[t._v("ZQuery")]),t._v(", simply use "),s("code",[t._v("ZQuery#run")]),t._v(" which will return a "),s("code",[t._v("ZIO[R, E, A]")]),t._v(".")]),t._v(" "),s("h2",{attrs:{id:"zquery-constructors-and-operators"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#zquery-constructors-and-operators"}},[t._v("#")]),t._v(" ZQuery constructors and operators")]),t._v(" "),s("p",[t._v("There are several ways to create a "),s("code",[t._v("ZQuery")]),t._v(". We've seen "),s("code",[t._v("ZQuery.fromRequest")]),t._v(", but you can also:")]),t._v(" "),s("ul",[s("li",[t._v("create from a pure value with "),s("code",[t._v("ZQuery.succeed")])]),t._v(" "),s("li",[t._v("create from an effect value with "),s("code",[t._v("ZQuery.fromEffect")])]),t._v(" "),s("li",[t._v("create from multiple queries with "),s("code",[t._v("ZQuery.collectAllPar")]),t._v(" and "),s("code",[t._v("ZQuery.foreachPar")]),t._v(" and their sequential equivalents "),s("code",[t._v("ZQuery.collectAll")]),t._v(" and "),s("code",[t._v("ZQuery.foreach")])])]),t._v(" "),s("p",[t._v("If you have a "),s("code",[t._v("ZQuery")]),t._v(" object, you can use:")]),t._v(" "),s("ul",[s("li",[s("code",[t._v("map")]),t._v(" and "),s("code",[t._v("mapError")]),t._v(" to modify the returned result or error")]),t._v(" "),s("li",[s("code",[t._v("flatMap")]),t._v(" or "),s("code",[t._v("zip")]),t._v(" to combine it with other "),s("code",[t._v("ZQuery")]),t._v(" objects")]),t._v(" "),s("li",[s("code",[t._v("provide")]),t._v(" and "),s("code",[t._v("provideSome")]),t._v(" to eliminate some of the "),s("code",[t._v("R")]),t._v(" requirements")])]),t._v(" "),s("p",[t._v("There are several ways to run a "),s("code",[t._v("ZQuery")]),t._v(":")]),t._v(" "),s("ul",[s("li",[s("code",[t._v("runCache")]),t._v(' runs the query using a given pre-populated cache. This can be useful for deterministically "replaying" a query without executing any new requests.')]),t._v(" "),s("li",[s("code",[t._v("runLog")]),t._v(" runs the query and returns its result along with the cache containing a complete log of all requests executed and their results. This can be useful for logging or analysis of query execution.")]),t._v(" "),s("li",[s("code",[t._v("run")]),t._v(" runs the query and returns its result.")])]),t._v(" "),s("h2",{attrs:{id:"using-zquery-with-caliban"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#using-zquery-with-caliban"}},[t._v("#")]),t._v(" Using ZQuery with Caliban")]),t._v(" "),s("p",[t._v("To use "),s("code",[t._v("ZQuery")]),t._v(" with Caliban, you can simply include fields of type "),s("code",[t._v("ZQuery")]),t._v(" in your API definition.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" Queries"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n users"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ZQuery"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Nothing")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" List"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("User"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n user"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" UserArgs "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" ZQuery"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Nothing")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" User"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("During the query execution, Caliban will merge all the requested fields that return a "),s("code",[t._v("ZQuery")]),t._v(" into a single "),s("code",[t._v("ZQuery")]),t._v(" and run it, so that all the possible optimizations are applied.")]),t._v(" "),s("p",[t._v("The "),s("a",{attrs:{href:"https://github.com/ghostdogpr/caliban/tree/master/examples",target:"_blank",rel:"noopener noreferrer"}},[t._v("examples"),s("OutboundLink")],1),t._v(" project provides 2 versions of the problem described in "),s("a",{attrs:{href:"https://blog.apollographql.com/optimizing-your-graphql-request-waterfalls-7c3f3360b051",target:"_blank",rel:"noopener noreferrer"}},[t._v("this article about GraphQL query optimization"),s("OutboundLink")],1),t._v(":")]),t._v(" "),s("ul",[s("li",[t._v("a "),s("a",{attrs:{href:"https://github.com/ghostdogpr/caliban/tree/master/examples/src/main/scala/caliban/optimizations/NaiveTest.scala",target:"_blank",rel:"noopener noreferrer"}},[t._v("naive"),s("OutboundLink")],1),t._v(" version where fields are just returning "),s("code",[t._v("IO")]),t._v(", resulting in 47 requests")]),t._v(" "),s("li",[t._v("an "),s("a",{attrs:{href:"https://github.com/ghostdogpr/caliban/tree/master/examples/src/main/scala/caliban/optimizations/OptimizedTest.scala",target:"_blank",rel:"noopener noreferrer"}},[t._v("optimized"),s("OutboundLink")],1),t._v(" version where fields are returning "),s("code",[t._v("ZQuery")]),t._v(", resulting in 8 requests only")])])])}),[],!1,null,null,null);a.default=n.exports}}]); \ No newline at end of file diff --git a/docs/assets/js/13.1c02ea8d.js b/docs/assets/js/13.1c02ea8d.js new file mode 100644 index 0000000000..9e944669f9 --- /dev/null +++ b/docs/assets/js/13.1c02ea8d.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[13],{212:function(t,a,s){"use strict";s.r(a);var e=s(28),n=Object(e.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"query-optimization"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#query-optimization"}},[t._v("#")]),t._v(" Query optimization")]),t._v(" "),s("p",[t._v("A GraphQL query may request multiple fields that are using the same resolver. It's not a problem if the resolver is a simple value, but it can be less than optimal when the resolver runs an effect (such as reading from a database).")]),t._v(" "),s("p",[t._v("We might want to:")]),t._v(" "),s("ul",[s("li",[s("strong",[t._v("cache")]),t._v(" identical queries (deduplication)")]),t._v(" "),s("li",[s("strong",[t._v("batch")]),t._v(" queries to the same source")])]),t._v(" "),s("p",[t._v("This is possible in Caliban using the "),s("code",[t._v("ZQuery")]),t._v(" data type.")]),t._v(" "),s("h2",{attrs:{id:"introducing-zquery"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#introducing-zquery"}},[t._v("#")]),t._v(" Introducing ZQuery")]),t._v(" "),s("p",[t._v("A "),s("code",[t._v("ZQuery[R, E, A]")]),t._v(" is a purely functional description of an effectual query that may contain requests to one or more data sources. Similarly to "),s("code",[t._v("ZIO[R, E, A]")]),t._v(", it requires an environment "),s("code",[t._v("R")]),t._v(", may fail with an "),s("code",[t._v("E")]),t._v(" or succeed with an "),s("code",[t._v("A")]),t._v(". All requests that do not need to be performed sequentially will automatically be batched, allowing for aggressive data source specific optimizations. Requests will also automatically be deduplicated and cached.")]),t._v(" "),s("p",[t._v("This allows for writing queries in a high level, compositional style, with confidence that they will automatically be optimized. For example, consider the following query from a user service.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" getAllUserIds"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ZQuery"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Nothing")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" List"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" getUserNameById"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ZQuery"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Nothing")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n userIds "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" getAllUserIds\n userNames "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" ZQuery"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("foreachPar"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("userIds"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("getUserNameById"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" userNames\n")])])]),s("p",[t._v("This would normally require N + 1 queries, one for "),s("code",[t._v("getAllUserIds")]),t._v(" and one for each call to "),s("code",[t._v("getUserNameById")]),t._v(". In contrast, "),s("code",[t._v("ZQuery")]),t._v(" will automatically optimize this to two queries, one for "),s("code",[t._v("userIds")]),t._v(" and one for "),s("code",[t._v("userNames")]),t._v(", assuming an implementation of the user service that supports batching.")]),t._v(" "),s("h2",{attrs:{id:"building-a-datasource"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#building-a-datasource"}},[t._v("#")]),t._v(" Building a DataSource")]),t._v(" "),s("p",[t._v("To build a "),s("code",[t._v("ZQuery")]),t._v(" that executes a request, you first need to build a "),s("code",[t._v("DataSource")]),t._v(". A "),s("code",[t._v("DataSource[R, E, A]")]),t._v(" defines how to execute requests of type "),s("code",[t._v("A")]),t._v(" and it requires 2 things:")]),t._v(" "),s("ul",[s("li",[t._v("an "),s("code",[t._v("identifier")]),t._v(" that uniquely identifies the data source (requests from "),s("em",[t._v("different")]),t._v(" data sources will "),s("em",[t._v("not")]),t._v(" be batched together)")]),t._v(" "),s("li",[t._v("a effectful function "),s("code",[t._v("run")]),t._v(" from an "),s("code",[t._v("Iterable")]),t._v(" of requests to a "),s("code",[t._v("Map")]),t._v(" of requests and results")])]),t._v(" "),s("p",[t._v("Let's consider "),s("code",[t._v("getUserNameById")]),t._v(" from the previous example. We need to define a corresponding request type that extends "),s("code",[t._v("zquery.Request")]),t._v(" for a given response type:")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" GetUserName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" Request"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Throwable"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n")])])]),s("p",[t._v("Now let's build the corresponding "),s("code",[t._v("DataSource")]),t._v(". We need to implement the following functions:")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" UserDataSource "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" DataSource"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" GetUserName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" identifier"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" run"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("requests"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Iterable"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("GetUserName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ZIO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Throwable"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" CompletedRequestMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v('We will use "UserDataSource" as our identifier. This name should not be reused for other data sources.')]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" identifier"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"UserDataSource"')]),t._v("\n")])])]),s("p",[t._v("We will define two different behaviors depending on whether we receive a single request or multiple requests at once.\nFor each request, we need to insert into the result map a value of type "),s("code",[t._v("Either")]),t._v(" ("),s("code",[t._v("Left")]),t._v(" for an error and "),s("code",[t._v("Right")]),t._v(" for a success).")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" run"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("requests"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Iterable"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("GetUserName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ZIO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Nothing")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" CompletedRequestMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" resultMap "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" CompletedRequestMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("empty\n requests"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("toList "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("match")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" request "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("::")]),t._v(" Nil "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// get user by ID e.g. SELECT name FROM users WHERE id = $id")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" result"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v("\n result"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("either"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("map"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("resultMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("insert"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("request"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" batch "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// get multiple users at once e.g. SELECT id, name FROM users WHERE id IN ($ids)")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" result"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("List"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v("\n result"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("fold"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n err "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" requests"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("foldLeft"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("resultMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("map"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" req"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" map"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("insert"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("req"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Left"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n _"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("foldLeft"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("resultMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("map"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" map"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("insert"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("GetUserName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Right"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("Now to build a "),s("code",[t._v("ZQuery")]),t._v(" from it, we can use "),s("code",[t._v("ZQuery.fromRequest")]),t._v(" and just pass the request and the data source:")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" getUserNameById"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ZQuery"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Throwable"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n ZQuery"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("fromRequest"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("GetUserName"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("UserDataSource"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("To run a "),s("code",[t._v("ZQuery")]),t._v(", simply use "),s("code",[t._v("ZQuery#run")]),t._v(" which will return a "),s("code",[t._v("ZIO[R, E, A]")]),t._v(".")]),t._v(" "),s("h2",{attrs:{id:"zquery-constructors-and-operators"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#zquery-constructors-and-operators"}},[t._v("#")]),t._v(" ZQuery constructors and operators")]),t._v(" "),s("p",[t._v("There are several ways to create a "),s("code",[t._v("ZQuery")]),t._v(". We've seen "),s("code",[t._v("ZQuery.fromRequest")]),t._v(", but you can also:")]),t._v(" "),s("ul",[s("li",[t._v("create from a pure value with "),s("code",[t._v("ZQuery.succeed")])]),t._v(" "),s("li",[t._v("create from an effect value with "),s("code",[t._v("ZQuery.fromEffect")])]),t._v(" "),s("li",[t._v("create from multiple queries with "),s("code",[t._v("ZQuery.collectAllPar")]),t._v(" and "),s("code",[t._v("ZQuery.foreachPar")]),t._v(" and their sequential equivalents "),s("code",[t._v("ZQuery.collectAll")]),t._v(" and "),s("code",[t._v("ZQuery.foreach")])])]),t._v(" "),s("p",[t._v("If you have a "),s("code",[t._v("ZQuery")]),t._v(" object, you can use:")]),t._v(" "),s("ul",[s("li",[s("code",[t._v("map")]),t._v(" and "),s("code",[t._v("mapError")]),t._v(" to modify the returned result or error")]),t._v(" "),s("li",[s("code",[t._v("flatMap")]),t._v(" or "),s("code",[t._v("zip")]),t._v(" to combine it with other "),s("code",[t._v("ZQuery")]),t._v(" objects")]),t._v(" "),s("li",[s("code",[t._v("provide")]),t._v(" and "),s("code",[t._v("provideSome")]),t._v(" to eliminate some of the "),s("code",[t._v("R")]),t._v(" requirements")])]),t._v(" "),s("p",[t._v("There are several ways to run a "),s("code",[t._v("ZQuery")]),t._v(":")]),t._v(" "),s("ul",[s("li",[s("code",[t._v("runCache")]),t._v(' runs the query using a given pre-populated cache. This can be useful for deterministically "replaying" a query without executing any new requests.')]),t._v(" "),s("li",[s("code",[t._v("runLog")]),t._v(" runs the query and returns its result along with the cache containing a complete log of all requests executed and their results. This can be useful for logging or analysis of query execution.")]),t._v(" "),s("li",[s("code",[t._v("run")]),t._v(" runs the query and returns its result.")])]),t._v(" "),s("h2",{attrs:{id:"using-zquery-with-caliban"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#using-zquery-with-caliban"}},[t._v("#")]),t._v(" Using ZQuery with Caliban")]),t._v(" "),s("p",[t._v("To use "),s("code",[t._v("ZQuery")]),t._v(" with Caliban, you can simply include fields of type "),s("code",[t._v("ZQuery")]),t._v(" in your API definition.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" Queries"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n users"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ZQuery"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Nothing")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" List"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("User"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n user"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" UserArgs "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" ZQuery"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Nothing")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" User"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("During the query execution, Caliban will merge all the requested fields that return a "),s("code",[t._v("ZQuery")]),t._v(" into a single "),s("code",[t._v("ZQuery")]),t._v(" and run it, so that all the possible optimizations are applied.")]),t._v(" "),s("p",[t._v("The "),s("a",{attrs:{href:"https://github.com/ghostdogpr/caliban/tree/master/examples",target:"_blank",rel:"noopener noreferrer"}},[t._v("examples"),s("OutboundLink")],1),t._v(" project provides 2 versions of the problem described in "),s("a",{attrs:{href:"https://blog.apollographql.com/optimizing-your-graphql-request-waterfalls-7c3f3360b051",target:"_blank",rel:"noopener noreferrer"}},[t._v("this article about GraphQL query optimization"),s("OutboundLink")],1),t._v(":")]),t._v(" "),s("ul",[s("li",[t._v("a "),s("a",{attrs:{href:"https://github.com/ghostdogpr/caliban/tree/master/examples/src/main/scala/caliban/optimizations/NaiveTest.scala",target:"_blank",rel:"noopener noreferrer"}},[t._v("naive"),s("OutboundLink")],1),t._v(" version where fields are just returning "),s("code",[t._v("IO")]),t._v(", resulting in 47 requests")]),t._v(" "),s("li",[t._v("an "),s("a",{attrs:{href:"https://github.com/ghostdogpr/caliban/tree/master/examples/src/main/scala/caliban/optimizations/OptimizedTest.scala",target:"_blank",rel:"noopener noreferrer"}},[t._v("optimized"),s("OutboundLink")],1),t._v(" version where fields are returning "),s("code",[t._v("ZQuery")]),t._v(", resulting in 8 requests only")])])])}),[],!1,null,null,null);a.default=n.exports}}]); \ No newline at end of file diff --git a/docs/assets/js/13.a0ebd332.js b/docs/assets/js/13.a0ebd332.js deleted file mode 100644 index 721edff3d7..0000000000 --- a/docs/assets/js/13.a0ebd332.js +++ /dev/null @@ -1 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[13],{213:function(t,a,s){"use strict";s.r(a);var e=s(0),n=Object(e.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"schemas"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#schemas"}},[t._v("#")]),t._v(" Schemas")]),t._v(" "),s("p",[t._v("A GraphQL schema will be derived automatically at compile-time (no reflection) from the types present in your resolver.\nThe table below shows how common Scala types are converted to GraphQL types.")]),t._v(" "),s("table",[s("thead",[s("tr",[s("th",[t._v("Scala Type")]),t._v(" "),s("th",[t._v("GraphQL Type")])])]),t._v(" "),s("tbody",[s("tr",[s("td",[t._v("Boolean")]),t._v(" "),s("td",[t._v("Boolean")])]),t._v(" "),s("tr",[s("td",[t._v("Int")]),t._v(" "),s("td",[t._v("Int")])]),t._v(" "),s("tr",[s("td",[t._v("Float")]),t._v(" "),s("td",[t._v("Float")])]),t._v(" "),s("tr",[s("td",[t._v("Double")]),t._v(" "),s("td",[t._v("Float")])]),t._v(" "),s("tr",[s("td",[t._v("String")]),t._v(" "),s("td",[t._v("String")])]),t._v(" "),s("tr",[s("td",[t._v("java.util.UUID")]),t._v(" "),s("td",[t._v("ID")])]),t._v(" "),s("tr",[s("td",[t._v("Unit")]),t._v(" "),s("td",[t._v("Unit (custom scalar)")])]),t._v(" "),s("tr",[s("td",[t._v("Long")]),t._v(" "),s("td",[t._v("Long (custom scalar)")])]),t._v(" "),s("tr",[s("td",[t._v("BigInt")]),t._v(" "),s("td",[t._v("BigInt (custom scalar)")])]),t._v(" "),s("tr",[s("td",[t._v("BigDecimal")]),t._v(" "),s("td",[t._v("BigDecimal (custom scalar)")])]),t._v(" "),s("tr",[s("td",[t._v("Case Class")]),t._v(" "),s("td",[t._v("Object")])]),t._v(" "),s("tr",[s("td",[t._v("Sealed Trait")]),t._v(" "),s("td",[t._v("Enum or Union")])]),t._v(" "),s("tr",[s("td",[t._v("Option[A]")]),t._v(" "),s("td",[t._v("Nullable A")])]),t._v(" "),s("tr",[s("td",[t._v("List[A]")]),t._v(" "),s("td",[t._v("List of A")])]),t._v(" "),s("tr",[s("td",[t._v("Set[A]")]),t._v(" "),s("td",[t._v("List of A")])]),t._v(" "),s("tr",[s("td",[t._v("A => B")]),t._v(" "),s("td",[t._v("A and B")])]),t._v(" "),s("tr",[s("td",[t._v("(A, B)")]),t._v(" "),s("td",[t._v("Object with 2 fields "),s("code",[t._v("_1")]),t._v(" and "),s("code",[t._v("_2")])])]),t._v(" "),s("tr",[s("td",[t._v("Either[A, B]")]),t._v(" "),s("td",[t._v("Object with 2 nullable fields "),s("code",[t._v("left")]),t._v(" and "),s("code",[t._v("right")])])]),t._v(" "),s("tr",[s("td",[t._v("Map[A, B]")]),t._v(" "),s("td",[t._v("List of Object with 2 fields "),s("code",[t._v("key")]),t._v(" and "),s("code",[t._v("value")])])]),t._v(" "),s("tr",[s("td",[t._v("ZIO[R, Nothing, A]")]),t._v(" "),s("td",[t._v("A")])]),t._v(" "),s("tr",[s("td",[t._v("ZIO[R, E, A]")]),t._v(" "),s("td",[t._v("Nullable A")])]),t._v(" "),s("tr",[s("td",[t._v("Future[A]")]),t._v(" "),s("td",[t._v("Nullable A")])]),t._v(" "),s("tr",[s("td",[t._v("ZStream[R, E, A]")]),t._v(" "),s("td",[t._v("A (subscription) or List of A (query, mutation)")])]),t._v(" "),s("tr",[s("td",[t._v("Json (from "),s("a",{attrs:{href:"https://github.com/circe/circe",target:"_blank",rel:"noopener noreferrer"}},[t._v("Circe"),s("OutboundLink")],1),t._v(")")]),t._v(" "),s("td",[t._v("Json (custom scalar, need "),s("code",[t._v("import caliban.interop.circe.json._")]),t._v(")")])])])]),t._v(" "),s("p",[t._v("See the "),s("a",{attrs:{href:"#custom-types"}},[t._v("Custom Types")]),t._v(" section to find out how to support your own types.")]),t._v(" "),s("p",[t._v("If you want Caliban to support other standard types, feel free to "),s("a",{attrs:{href:"https://github.com/ghostdogpr/caliban/issues",target:"_blank",rel:"noopener noreferrer"}},[t._v("file an issue"),s("OutboundLink")],1),t._v(" or even a PR.")]),t._v(" "),s("div",{staticClass:"custom-block warning"},[s("p",{staticClass:"custom-block-title"},[t._v("Schema derivation issues")]),t._v(" "),s("p",[t._v("Magnolia (the library used to derive the schema at compile-time) sometimes has some trouble generating schemas with a lot of nested types, or types reused in multiple places.\nto deal with this, you can declare schemas for your case classes and sealed traits explicitly:")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("implicit")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" roleSchema "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Schema"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("gen"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Role"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("implicit")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" characterSchema "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Schema"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("gen"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Character"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n")])])]),s("p",[t._v("Make sure those implicits are in scope when you call "),s("code",[t._v("graphQL(...)")]),t._v(". This will make Magnolia's job easier by pre-generating schemas for those classes and re-using them when needed.\nThis will also improve compilation times and generate less bytecode.")])]),t._v(" "),s("h2",{attrs:{id:"enum-and-union"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#enum-and-union"}},[t._v("#")]),t._v(" Enum and union")]),t._v(" "),s("p",[t._v("A sealed trait will be converted to a different GraphQL type depending on its content:")]),t._v(" "),s("ul",[s("li",[t._v("a sealed trait with only case objects will be converted to an "),s("code",[t._v("ENUM")])]),t._v(" "),s("li",[t._v("a sealed trait with only case classes will be converted to a "),s("code",[t._v("UNION")])])]),t._v(" "),s("p",[t._v('GraphQL does not support empty objects, so in case a sealed trait mixes case classes and case objects, a union type will be created and the case objects will have a "fake" field named '),s("code",[t._v("_")]),t._v(" which is not queryable.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("sealed")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" ORIGIN\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" ORIGIN "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" EARTH "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" ORIGIN\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" MARS "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" ORIGIN\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" BELT "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" ORIGIN\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("The snippet above will produce the following GraphQL type:")]),t._v(" "),s("div",{staticClass:"language-graphql extra-class"},[s("pre",{pre:!0,attrs:{class:"language-graphql"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Origin")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token constant"}},[t._v("BELT")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token constant"}},[t._v("EARTH")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token constant"}},[t._v("MARS")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("Here's an example of union:")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("sealed")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Role\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" Role "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" Captain"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("shipName"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" Role\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" Engineer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("specialty"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" Role\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" Mechanic "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" Role\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("The snippet above will produce the following GraphQL type:")]),t._v(" "),s("div",{staticClass:"language-graphql extra-class"},[s("pre",{pre:!0,attrs:{class:"language-graphql"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("union")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Role")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Captain "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" Engineer "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" Mechanic\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Captain")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("shipName")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Engineer")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("specialty")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Mechanic")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("_")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Boolean"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("h2",{attrs:{id:"arguments"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#arguments"}},[t._v("#")]),t._v(" Arguments")]),t._v(" "),s("p",[t._v("To declare a field that take arguments, create a dedicated case class representing the arguments and make the field a "),s("em",[t._v("function")]),t._v(" from this class to the result type.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" FilterArgs"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("origin"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Option"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Origin"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" Queries"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("characters"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" FilterArgs "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" List"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Character"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("The snippet above will produce the following GraphQL type:")]),t._v(" "),s("div",{staticClass:"language-graphql extra-class"},[s("pre",{pre:!0,attrs:{class:"language-graphql"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Queries")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("characters")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("origin")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Origin"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Character"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("Caliban provides auto-derivation for common types such as "),s("code",[t._v("Int")]),t._v(", "),s("code",[t._v("String")]),t._v(", "),s("code",[t._v("List")]),t._v(", "),s("code",[t._v("Option")]),t._v(", etc. but you can also support your own types by providing an implicit instance of "),s("code",[t._v("caliban.schema.ArgBuilder")]),t._v(".")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),s("p",[t._v("There is no "),s("code",[t._v("ArgBuilder")]),t._v(" for tuples. If you have multiple arguments, use a case class containing all of them instead of a tuple.")])]),t._v(" "),s("h2",{attrs:{id:"effects"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#effects"}},[t._v("#")]),t._v(" Effects")]),t._v(" "),s("p",[t._v("Fields can return ZIO effects. This allows you to leverage all the features provided by ZIO: timeouts, retries, access to ZIO environment, memoizing, etc. An effect will be run every time a query requiring the corresponding field is executed.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" Queries"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("characters"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("List"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Character"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n character"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" CharacterName "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" RIO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Console"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Character"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("If you don't use ZIO environment ("),s("code",[t._v("R")]),t._v(" = "),s("code",[t._v("Any")]),t._v("), there is nothing special to do to get it working.")]),t._v(" "),s("p",[t._v("If you require a ZIO environment, you will need to have the content of "),s("code",[t._v("caliban.schema.GenericSchema[R]")]),t._v(" for your custom "),s("code",[t._v("R")]),t._v(" in scope when you call "),s("code",[t._v("graphQL(...)")]),t._v(".")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" schema "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" GenericSchema"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("MyEnv"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" schema"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_\n")])])]),s("h2",{attrs:{id:"annotations"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#annotations"}},[t._v("#")]),t._v(" Annotations")]),t._v(" "),s("p",[t._v("Caliban supports a few annotations to enrich data types:")]),t._v(" "),s("ul",[s("li",[s("code",[t._v('@GQLName("name")')]),t._v(" allows you to specify a different name for a data type or a field.")]),t._v(" "),s("li",[s("code",[t._v('@GQLInputName("name")')]),t._v(" allows you to specify a different name for a data type used as an input (by default, the suffix "),s("code",[t._v("Input")]),t._v(" is appended to the type name).")]),t._v(" "),s("li",[s("code",[t._v('@GQLDescription("description")')]),t._v(" lets you provide a description for a data type or field. This description will be visible when your schema is introspected.")]),t._v(" "),s("li",[s("code",[t._v('@GQLDeprecated("reason")')]),t._v(" allows deprecating a field or an enum value.")])]),t._v(" "),s("h2",{attrs:{id:"custom-types"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#custom-types"}},[t._v("#")]),t._v(" Custom types")]),t._v(" "),s("p",[t._v("Caliban provides auto-derivation for common types such as "),s("code",[t._v("Int")]),t._v(", "),s("code",[t._v("String")]),t._v(", "),s("code",[t._v("List")]),t._v(", "),s("code",[t._v("Option")]),t._v(", etc. but you can also support your own types by providing an implicit instance of "),s("code",[t._v("caliban.schema.Schema")]),t._v(".")]),t._v(" "),s("p",[t._v("An easy way to do this is to reuse existing instances and use "),s("code",[t._v("contramap")]),t._v(" to map from your type to the original type. Here's an example of creating an instance for "),s("a",{attrs:{href:"https://github.com/fthomas/refined",target:"_blank",rel:"noopener noreferrer"}},[t._v("refined"),s("OutboundLink")],1),t._v("'s "),s("code",[t._v("NonEmptyString")]),t._v(" reusing existing instance for "),s("code",[t._v("String")]),t._v(":")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("caliban"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("schema")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("implicit")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" nonEmptyStringSchema"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Schema"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" NonEmptyString"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Schema"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("stringSchema"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("contramap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("value"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("You can also use the "),s("code",[t._v("scalarSchema")]),t._v(" helper to create your own scalar types, providing a name, an optional description, and a function from your type to a "),s("code",[t._v("ResponseValue")]),t._v(":")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("caliban"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("schema")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("implicit")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" unitSchema"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Schema"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Unit")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" scalarSchema"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Unit"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" None"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" _ "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" ObjectValue"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Nil"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("h2",{attrs:{id:"code-generation"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#code-generation"}},[t._v("#")]),t._v(" Code generation")]),t._v(" "),s("p",[t._v("Caliban can automatically generate Scala code from a GraphQL schema.")]),t._v(" "),s("p",[t._v("In order to use this feature, add the "),s("code",[t._v("caliban-codegen")]),t._v(" sbt plugin to your project and enable it.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[t._v("addSbtPlugin"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"com.github.ghostdogpr"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"caliban-codegen"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"0.5.2"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nenablePlugins"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("CodegenPlugin"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("Then call the "),s("code",[t._v("codegen")]),t._v(" sbt command.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[t._v("calibanGenSchema schemaPath outPath "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v("scalafmtPath\n\ncalibanGenSchema project"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("schema"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("json src"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("main"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("GQLSchema"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("scala\n")])])]),s("p",[t._v("This command will create a Scala file in "),s("code",[t._v("outputPath")]),t._v(" containing all the types defined in the provided GraphQL schema defined at "),s("code",[t._v("schemaPath")]),t._v(". The generated code will be formatted with Scalafmt using the configuration defined by "),s("code",[t._v("scalafmtPath")]),t._v(".")]),t._v(" "),s("div",{staticClass:"custom-block warning"},[s("p",{staticClass:"custom-block-title"},[t._v("Unsupported features")]),t._v(" "),s("p",[t._v("Some features are not supported by Caliban and will cause an error during code generation:")]),t._v(" "),s("ul",[s("li",[t._v("interfaces")]),t._v(" "),s("li",[t._v("extensions")])])])])}),[],!1,null,null,null);a.default=n.exports}}]); \ No newline at end of file diff --git a/docs/assets/js/14.0bd8bec9.js b/docs/assets/js/14.0bd8bec9.js new file mode 100644 index 0000000000..244c8f4d50 --- /dev/null +++ b/docs/assets/js/14.0bd8bec9.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[14],{213:function(t,a,e){"use strict";e.r(a);var s=e(28),n=Object(s.a)({},(function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("h1",{attrs:{id:"schemas"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#schemas"}},[t._v("#")]),t._v(" Schemas")]),t._v(" "),e("p",[t._v("A GraphQL schema will be derived automatically at compile-time (no reflection) from the types present in your resolver.\nThe table below shows how common Scala types are converted to GraphQL types.")]),t._v(" "),e("table",[e("thead",[e("tr",[e("th",[t._v("Scala Type")]),t._v(" "),e("th",[t._v("GraphQL Type")])])]),t._v(" "),e("tbody",[e("tr",[e("td",[t._v("Boolean")]),t._v(" "),e("td",[t._v("Boolean")])]),t._v(" "),e("tr",[e("td",[t._v("Int")]),t._v(" "),e("td",[t._v("Int")])]),t._v(" "),e("tr",[e("td",[t._v("Float")]),t._v(" "),e("td",[t._v("Float")])]),t._v(" "),e("tr",[e("td",[t._v("Double")]),t._v(" "),e("td",[t._v("Float")])]),t._v(" "),e("tr",[e("td",[t._v("String")]),t._v(" "),e("td",[t._v("String")])]),t._v(" "),e("tr",[e("td",[t._v("java.util.UUID")]),t._v(" "),e("td",[t._v("ID")])]),t._v(" "),e("tr",[e("td",[t._v("Unit")]),t._v(" "),e("td",[t._v("Unit (custom scalar)")])]),t._v(" "),e("tr",[e("td",[t._v("Long")]),t._v(" "),e("td",[t._v("Long (custom scalar)")])]),t._v(" "),e("tr",[e("td",[t._v("BigInt")]),t._v(" "),e("td",[t._v("BigInt (custom scalar)")])]),t._v(" "),e("tr",[e("td",[t._v("BigDecimal")]),t._v(" "),e("td",[t._v("BigDecimal (custom scalar)")])]),t._v(" "),e("tr",[e("td",[t._v("Case Class")]),t._v(" "),e("td",[t._v("Object")])]),t._v(" "),e("tr",[e("td",[t._v("Sealed Trait")]),t._v(" "),e("td",[t._v("Enum or Union")])]),t._v(" "),e("tr",[e("td",[t._v("Option[A]")]),t._v(" "),e("td",[t._v("Nullable A")])]),t._v(" "),e("tr",[e("td",[t._v("List[A]")]),t._v(" "),e("td",[t._v("List of A")])]),t._v(" "),e("tr",[e("td",[t._v("Set[A]")]),t._v(" "),e("td",[t._v("List of A")])]),t._v(" "),e("tr",[e("td",[t._v("A => B")]),t._v(" "),e("td",[t._v("A and B")])]),t._v(" "),e("tr",[e("td",[t._v("(A, B)")]),t._v(" "),e("td",[t._v("Object with 2 fields "),e("code",[t._v("_1")]),t._v(" and "),e("code",[t._v("_2")])])]),t._v(" "),e("tr",[e("td",[t._v("Either[A, B]")]),t._v(" "),e("td",[t._v("Object with 2 nullable fields "),e("code",[t._v("left")]),t._v(" and "),e("code",[t._v("right")])])]),t._v(" "),e("tr",[e("td",[t._v("Map[A, B]")]),t._v(" "),e("td",[t._v("List of Object with 2 fields "),e("code",[t._v("key")]),t._v(" and "),e("code",[t._v("value")])])]),t._v(" "),e("tr",[e("td",[t._v("ZIO[R, Nothing, A]")]),t._v(" "),e("td",[t._v("A")])]),t._v(" "),e("tr",[e("td",[t._v("ZIO[R, E, A]")]),t._v(" "),e("td",[t._v("Nullable A")])]),t._v(" "),e("tr",[e("td",[t._v("Future[A]")]),t._v(" "),e("td",[t._v("Nullable A")])]),t._v(" "),e("tr",[e("td",[t._v("ZStream[R, E, A]")]),t._v(" "),e("td",[t._v("A (subscription) or List of A (query, mutation)")])]),t._v(" "),e("tr",[e("td",[t._v("Json (from "),e("a",{attrs:{href:"https://github.com/circe/circe",target:"_blank",rel:"noopener noreferrer"}},[t._v("Circe"),e("OutboundLink")],1),t._v(")")]),t._v(" "),e("td",[t._v("Json (custom scalar, need "),e("code",[t._v("import caliban.interop.circe.json._")]),t._v(")")])])])]),t._v(" "),e("p",[t._v("See the "),e("a",{attrs:{href:"#custom-types"}},[t._v("Custom Types")]),t._v(" section to find out how to support your own types.")]),t._v(" "),e("p",[t._v("If you want Caliban to support other standard types, feel free to "),e("a",{attrs:{href:"https://github.com/ghostdogpr/caliban/issues",target:"_blank",rel:"noopener noreferrer"}},[t._v("file an issue"),e("OutboundLink")],1),t._v(" or even a PR.")]),t._v(" "),e("div",{staticClass:"custom-block warning"},[e("p",{staticClass:"custom-block-title"},[t._v("Schema derivation issues")]),t._v(" "),e("p",[t._v("Magnolia (the library used to derive the schema at compile-time) sometimes has some trouble generating schemas with a lot of nested types, or types reused in multiple places.\nto deal with this, you can declare schemas for your case classes and sealed traits explicitly:")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("implicit")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" roleSchema "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Schema"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("gen"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Role"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("implicit")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" characterSchema "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Schema"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("gen"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Character"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n")])])]),e("p",[t._v("Make sure those implicits are in scope when you call "),e("code",[t._v("graphQL(...)")]),t._v(". This will make Magnolia's job easier by pre-generating schemas for those classes and re-using them when needed.\nThis will also improve compilation times and generate less bytecode.")])]),t._v(" "),e("h2",{attrs:{id:"enums-unions-interfaces"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#enums-unions-interfaces"}},[t._v("#")]),t._v(" Enums, unions, interfaces")]),t._v(" "),e("p",[t._v("A sealed trait will be converted to a different GraphQL type depending on its content:")]),t._v(" "),e("ul",[e("li",[t._v("a sealed trait with only case objects will be converted to an "),e("code",[t._v("ENUM")])]),t._v(" "),e("li",[t._v("a sealed trait with only case classes will be converted to a "),e("code",[t._v("UNION")])])]),t._v(" "),e("p",[t._v('GraphQL does not support empty objects, so in case a sealed trait mixes case classes and case objects, a union type will be created and the case objects will have a "fake" field named '),e("code",[t._v("_")]),t._v(" which is not queryable.")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("sealed")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" ORIGIN\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" ORIGIN "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" EARTH "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" ORIGIN\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" MARS "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" ORIGIN\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" BELT "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" ORIGIN\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),e("p",[t._v("The snippet above will produce the following GraphQL type:")]),t._v(" "),e("div",{staticClass:"language-graphql extra-class"},[e("pre",{pre:!0,attrs:{class:"language-graphql"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Origin")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("BELT")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("EARTH")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("MARS")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),e("p",[t._v("Here's an example of union:")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("sealed")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Role\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" Role "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" Captain"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("shipName"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" Role\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" Engineer"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("specialty"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" Role\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" Mechanic "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" Role\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),e("p",[t._v("The snippet above will produce the following GraphQL type:")]),t._v(" "),e("div",{staticClass:"language-graphql extra-class"},[e("pre",{pre:!0,attrs:{class:"language-graphql"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("union")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Role")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Captain "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" Engineer "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" Mechanic\n\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Captain")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("shipName")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Engineer")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("specialty")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Mechanic")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("_")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Boolean"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),e("p",[t._v("If you prefer an "),e("code",[t._v("Interface")]),t._v(" instead of a "),e("code",[t._v("Union")]),t._v(" type, add the "),e("code",[t._v("@GQLInterface")]),t._v(" annotation to your sealed trait.\nAn interface will be created with all the fields that are common to the case classes extending the sealed trait.")]),t._v(" "),e("h2",{attrs:{id:"arguments"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#arguments"}},[t._v("#")]),t._v(" Arguments")]),t._v(" "),e("p",[t._v("To declare a field that take arguments, create a dedicated case class representing the arguments and make the field a "),e("em",[t._v("function")]),t._v(" from this class to the result type.")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" FilterArgs"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("origin"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Option"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Origin"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" Queries"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("characters"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" FilterArgs "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" List"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Character"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),e("p",[t._v("The snippet above will produce the following GraphQL type:")]),t._v(" "),e("div",{staticClass:"language-graphql extra-class"},[e("pre",{pre:!0,attrs:{class:"language-graphql"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Queries")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("characters")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("origin")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Origin"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Character"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),e("p",[t._v("Caliban provides auto-derivation for common types such as "),e("code",[t._v("Int")]),t._v(", "),e("code",[t._v("String")]),t._v(", "),e("code",[t._v("List")]),t._v(", "),e("code",[t._v("Option")]),t._v(", etc. but you can also support your own types by providing an implicit instance of "),e("code",[t._v("caliban.schema.ArgBuilder")]),t._v(".")]),t._v(" "),e("div",{staticClass:"custom-block tip"},[e("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),e("p",[t._v("There is no "),e("code",[t._v("ArgBuilder")]),t._v(" for tuples. If you have multiple arguments, use a case class containing all of them instead of a tuple.")])]),t._v(" "),e("h2",{attrs:{id:"effects"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#effects"}},[t._v("#")]),t._v(" Effects")]),t._v(" "),e("p",[t._v("Fields can return ZIO effects. This allows you to leverage all the features provided by ZIO: timeouts, retries, access to ZIO environment, memoizing, etc. An effect will be run every time a query requiring the corresponding field is executed.")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" Queries"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("characters"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("List"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Character"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n character"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" CharacterName "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" RIO"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Console"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Character"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),e("p",[t._v("If you don't use ZIO environment ("),e("code",[t._v("R")]),t._v(" = "),e("code",[t._v("Any")]),t._v("), there is nothing special to do to get it working.")]),t._v(" "),e("p",[t._v("If you require a ZIO environment, you will need to have the content of "),e("code",[t._v("caliban.schema.GenericSchema[R]")]),t._v(" for your custom "),e("code",[t._v("R")]),t._v(" in scope when you call "),e("code",[t._v("graphQL(...)")]),t._v(".")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" schema "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" GenericSchema"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("MyEnv"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" schema"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_\n")])])]),e("h2",{attrs:{id:"annotations"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#annotations"}},[t._v("#")]),t._v(" Annotations")]),t._v(" "),e("p",[t._v("Caliban supports a few annotations to enrich data types:")]),t._v(" "),e("ul",[e("li",[e("code",[t._v('@GQLName("name")')]),t._v(" allows you to specify a different name for a data type or a field.")]),t._v(" "),e("li",[e("code",[t._v('@GQLInputName("name")')]),t._v(" allows you to specify a different name for a data type used as an input (by default, the suffix "),e("code",[t._v("Input")]),t._v(" is appended to the type name).")]),t._v(" "),e("li",[e("code",[t._v('@GQLDescription("description")')]),t._v(" lets you provide a description for a data type or field. This description will be visible when your schema is introspected.")]),t._v(" "),e("li",[e("code",[t._v('@GQLDeprecated("reason")')]),t._v(" allows deprecating a field or an enum value.")]),t._v(" "),e("li",[e("code",[t._v("@GQLInterface")]),t._v(" to force a sealed trait generating an interface instead of a union.")]),t._v(" "),e("li",[e("code",[t._v("@GQLDirective(directive: Directive)")]),t._v(" to add a directive to a field or type.")])]),t._v(" "),e("h2",{attrs:{id:"custom-types"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#custom-types"}},[t._v("#")]),t._v(" Custom types")]),t._v(" "),e("p",[t._v("Caliban provides auto-derivation for common types such as "),e("code",[t._v("Int")]),t._v(", "),e("code",[t._v("String")]),t._v(", "),e("code",[t._v("List")]),t._v(", "),e("code",[t._v("Option")]),t._v(", etc. but you can also support your own types by providing an implicit instance of "),e("code",[t._v("caliban.schema.Schema")]),t._v(".")]),t._v(" "),e("p",[t._v("An easy way to do this is to reuse existing instances and use "),e("code",[t._v("contramap")]),t._v(" to map from your type to the original type. Here's an example of creating an instance for "),e("a",{attrs:{href:"https://github.com/fthomas/refined",target:"_blank",rel:"noopener noreferrer"}},[t._v("refined"),e("OutboundLink")],1),t._v("'s "),e("code",[t._v("NonEmptyString")]),t._v(" reusing existing instance for "),e("code",[t._v("String")]),t._v(":")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("caliban"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("schema")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("implicit")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" nonEmptyStringSchema"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Schema"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),e("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" NonEmptyString"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Schema"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("stringSchema"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("contramap"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("value"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),e("p",[t._v("You can also use the "),e("code",[t._v("scalarSchema")]),t._v(" helper to create your own scalar types, providing a name, an optional description, and a function from your type to a "),e("code",[t._v("ResponseValue")]),t._v(":")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("caliban"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("schema")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("_\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("implicit")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" unitSchema"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Schema"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),e("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Unit")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" scalarSchema"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Unit"')]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" None"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" _ "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" ObjectValue"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Nil"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),e("h2",{attrs:{id:"code-generation"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#code-generation"}},[t._v("#")]),t._v(" Code generation")]),t._v(" "),e("p",[t._v("Caliban can automatically generate Scala code from a GraphQL schema.")]),t._v(" "),e("p",[t._v("In order to use this feature, add the "),e("code",[t._v("caliban-codegen")]),t._v(" sbt plugin to your project and enable it.")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[t._v("addSbtPlugin"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"com.github.ghostdogpr"')]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"caliban-codegen"')]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"0.6.0"')]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nenablePlugins"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("CodegenPlugin"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),e("p",[t._v("Then call the "),e("code",[t._v("calibanGenSchema")]),t._v(" sbt command.")]),t._v(" "),e("div",{staticClass:"language-scala extra-class"},[e("pre",{pre:!0,attrs:{class:"language-scala"}},[e("code",[t._v("calibanGenSchema schemaPath outPath "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v("scalafmtPath\n\ncalibanGenSchema project"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("schema"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("graphql src"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("main"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("MyAPI"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("scala\n")])])]),e("p",[t._v("This command will create a Scala file in "),e("code",[t._v("outputPath")]),t._v(" containing all the types defined in the provided GraphQL schema defined at "),e("code",[t._v("schemaPath")]),t._v(". The generated code will be formatted with Scalafmt using the configuration defined by "),e("code",[t._v("scalafmtPath")]),t._v(" (default: "),e("code",[t._v(".scalafmt.conf")]),t._v(").")]),t._v(" "),e("div",{staticClass:"custom-block warning"},[e("p",{staticClass:"custom-block-title"},[t._v("Unsupported features")]),t._v(" "),e("p",[t._v("Some features are not supported by Caliban and will cause an error during code generation:")]),t._v(" "),e("ul",[e("li",[t._v("interfaces")]),t._v(" "),e("li",[t._v("extensions")])])])])}),[],!1,null,null,null);a.default=n.exports}}]); \ No newline at end of file diff --git a/docs/assets/js/14.fe3db0cd.js b/docs/assets/js/14.fe3db0cd.js deleted file mode 100644 index 64b63d4005..0000000000 --- a/docs/assets/js/14.fe3db0cd.js +++ /dev/null @@ -1 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[14],{214:function(a,t,s){"use strict";s.r(t);var e=s(0),n=Object(e.a)({},(function(){var a=this,t=a.$createElement,s=a._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":a.$parent.slotKey}},[s("h1",{attrs:{id:"validation"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#validation"}},[a._v("#")]),a._v(" Validation")]),a._v(" "),s("p",[a._v("Caliban provides a little macro called "),s("code",[a._v("gqldoc")]),a._v(" that can check at "),s("strong",[a._v("compile-time")]),a._v(" that a GraphQL query (a "),s("em",[a._v("document")]),a._v(" to be exact) has valid syntax.")]),a._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[a._v("import")]),a._v(" caliban"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(".")]),a._v("Macros"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(".")]),a._v("gqldoc\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[a._v("val")]),a._v(" query "),s("span",{pre:!0,attrs:{class:"token operator"}},[a._v("=")]),a._v(" gqldoc"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("(")]),s("span",{pre:!0,attrs:{class:"token triple-quoted-string string"}},[a._v('"""\n query test {\n amos: character(name: "Amos Burton") {\n name\n }\n }"""')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(")")]),a._v("\n")])])]),s("p",[a._v("At "),s("strong",[a._v("runtime")]),a._v(", it is possible to validate a query against your schema by calling the method "),s("code",[a._v("check")]),a._v(" on your API.")]),a._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[a._v("def")]),a._v(" check"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("(")]),a._v("query"),s("span",{pre:!0,attrs:{class:"token operator"}},[a._v(":")]),a._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[a._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[a._v(":")]),a._v(" IO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("[")]),a._v("CalibanError"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(",")]),a._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[a._v("Unit")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("]")]),a._v("\n")])])]),s("p",[a._v("It is also possible to skip validation when executing a query by passing "),s("code",[a._v("skipValidation = true")]),a._v(" when calling "),s("code",[a._v("execute")]),a._v(". This will slightly improve performance.")])])}),[],!1,null,null,null);t.default=n.exports}}]); \ No newline at end of file diff --git a/docs/assets/js/15.1f4e823e.js b/docs/assets/js/15.1f4e823e.js new file mode 100644 index 0000000000..4d0cdd90e9 --- /dev/null +++ b/docs/assets/js/15.1f4e823e.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[15],{214:function(a,t,s){"use strict";s.r(t);var e=s(28),n=Object(e.a)({},(function(){var a=this,t=a.$createElement,s=a._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":a.$parent.slotKey}},[s("h1",{attrs:{id:"validation"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#validation"}},[a._v("#")]),a._v(" Validation")]),a._v(" "),s("p",[a._v("Caliban provides a little macro called "),s("code",[a._v("gqldoc")]),a._v(" that can check at "),s("strong",[a._v("compile-time")]),a._v(" that a GraphQL query (a "),s("em",[a._v("document")]),a._v(" to be exact) has valid syntax.")]),a._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[a._v("import")]),a._v(" caliban"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(".")]),a._v("Macros"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(".")]),a._v("gqldoc\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[a._v("val")]),a._v(" query "),s("span",{pre:!0,attrs:{class:"token operator"}},[a._v("=")]),a._v(" gqldoc"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("(")]),s("span",{pre:!0,attrs:{class:"token triple-quoted-string string"}},[a._v('"""\n query test {\n amos: character(name: "Amos Burton") {\n name\n }\n }"""')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(")")]),a._v("\n")])])]),s("p",[a._v("At "),s("strong",[a._v("runtime")]),a._v(", it is possible to validate a query against your schema by calling the method "),s("code",[a._v("check")]),a._v(" on your API.")]),a._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[a._v("def")]),a._v(" check"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("(")]),a._v("query"),s("span",{pre:!0,attrs:{class:"token operator"}},[a._v(":")]),a._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[a._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[a._v(":")]),a._v(" IO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("[")]),a._v("CalibanError"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v(",")]),a._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[a._v("Unit")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[a._v("]")]),a._v("\n")])])]),s("p",[a._v("It is also possible to skip validation when executing a query by passing "),s("code",[a._v("skipValidation = true")]),a._v(" when calling "),s("code",[a._v("execute")]),a._v(". This will slightly improve performance.")])])}),[],!1,null,null,null);t.default=n.exports}}]); \ No newline at end of file diff --git a/docs/assets/js/15.a58fc205.js b/docs/assets/js/15.a58fc205.js deleted file mode 100644 index 7a4b00e0c5..0000000000 --- a/docs/assets/js/15.a58fc205.js +++ /dev/null @@ -1 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[15],{215:function(r,t,a){"use strict";a.r(t);var e=a(0),n=Object(e.a)({},(function(){var r=this,t=r.$createElement,a=r._self._c||t;return a("ContentSlotsDistributor",{attrs:{"slot-key":r.$parent.slotKey}},[a("h1",{attrs:{id:"resources"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#resources"}},[r._v("#")]),r._v(" Resources")]),r._v(" "),a("h2",{attrs:{id:"talks"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#talks"}},[r._v("#")]),r._v(" Talks")]),r._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"https://www.youtube.com/watch?v=OC8PbviYUlQ",target:"_blank",rel:"noopener noreferrer"}},[r._v("Caliban: Designing a Functional GraphQL Library"),a("OutboundLink")],1),r._v(" by Pierre Ricadat at "),a("a",{attrs:{href:"https://www.functionalscala.com/",target:"_blank",rel:"noopener noreferrer"}},[r._v("Functional Scala"),a("OutboundLink")],1),r._v(" in December 2019 (slides available "),a("a",{attrs:{href:"https://www.slideshare.net/PierreRicadat/designing-a-functional-graphql-library-204680947",target:"_blank",rel:"noopener noreferrer"}},[r._v("here"),a("OutboundLink")],1),r._v(")")])]),r._v(" "),a("h2",{attrs:{id:"blog-articles"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#blog-articles"}},[r._v("#")]),r._v(" Blog Articles")]),r._v(" "),a("ul",[a("li",[a("strong",[a("em",[r._v("GraphQL in Scala with Caliban")])]),r._v(" by Pierre Ricadat (February 2020)\n"),a("ul",[a("li",[a("a",{attrs:{href:"https://medium.com/@ghostdogpr/graphql-in-scala-with-caliban-part-1-8ceb6099c3c2",target:"_blank",rel:"noopener noreferrer"}},[r._v("Part 1: Turn a simple API into GraphQL"),a("OutboundLink")],1)]),r._v(" "),a("li",[a("a",{attrs:{href:"https://medium.com/@ghostdogpr/graphql-in-scala-with-caliban-part-2-c7762110c0f9",target:"_blank",rel:"noopener noreferrer"}},[r._v("Part 2: Query optimization"),a("OutboundLink")],1)]),r._v(" "),a("li",[a("a",{attrs:{href:"https://medium.com/@ghostdogpr/graphql-in-scala-with-caliban-part-3-8962a02d5d64",target:"_blank",rel:"noopener noreferrer"}},[r._v("Part 3: Customization"),a("OutboundLink")],1)])])]),r._v(" "),a("li",[a("a",{attrs:{href:"http://fokot.github.io/post/caliban-auth.html",target:"_blank",rel:"noopener noreferrer"}},[r._v("Authentication in Caliban"),a("OutboundLink")],1),r._v(" by František Kocun (December 2019)")])])])}),[],!1,null,null,null);t.default=n.exports}}]); \ No newline at end of file diff --git a/docs/assets/js/16.09e4e2e8.js b/docs/assets/js/16.09e4e2e8.js deleted file mode 100644 index 546eb61f54..0000000000 --- a/docs/assets/js/16.09e4e2e8.js +++ /dev/null @@ -1 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[16],{9:function(n,w,o){}}]); \ No newline at end of file diff --git a/docs/assets/js/16.285ebbb2.js b/docs/assets/js/16.285ebbb2.js new file mode 100644 index 0000000000..2e4c7cf58f --- /dev/null +++ b/docs/assets/js/16.285ebbb2.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[16],{215:function(a,r,t){"use strict";t.r(r);var e=t(28),n=Object(e.a)({},(function(){var a=this,r=a.$createElement,t=a._self._c||r;return t("ContentSlotsDistributor",{attrs:{"slot-key":a.$parent.slotKey}},[t("h1",{attrs:{id:"resources"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#resources"}},[a._v("#")]),a._v(" Resources")]),a._v(" "),t("h2",{attrs:{id:"talks"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#talks"}},[a._v("#")]),a._v(" Talks")]),a._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://www.youtube.com/watch?v=OC8PbviYUlQ",target:"_blank",rel:"noopener noreferrer"}},[a._v("Caliban: Designing a Functional GraphQL Library"),t("OutboundLink")],1),a._v(" by Pierre Ricadat at "),t("a",{attrs:{href:"https://www.functionalscala.com/",target:"_blank",rel:"noopener noreferrer"}},[a._v("Functional Scala"),t("OutboundLink")],1),a._v(" in December 2019 (slides available "),t("a",{attrs:{href:"https://www.slideshare.net/PierreRicadat/designing-a-functional-graphql-library-204680947",target:"_blank",rel:"noopener noreferrer"}},[a._v("here"),t("OutboundLink")],1),a._v(")")])]),a._v(" "),t("h2",{attrs:{id:"blog-articles"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#blog-articles"}},[a._v("#")]),a._v(" Blog Articles")]),a._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://medium.com/@ghostdogpr/caliban-client-a-type-safe-graphql-client-for-scala-and-scala-js-718aa42c5ef7",target:"_blank",rel:"noopener noreferrer"}},[a._v("Caliban Client: a type-safe GraphQL Client for Scala and Scala.js"),t("OutboundLink")],1),a._v(" by Pierre Ricadat (February 2020)")]),a._v(" "),t("li",[t("strong",[t("em",[a._v("GraphQL in Scala with Caliban")])]),a._v(" by Pierre Ricadat (February 2020)\n"),t("ul",[t("li",[t("a",{attrs:{href:"https://medium.com/@ghostdogpr/graphql-in-scala-with-caliban-part-1-8ceb6099c3c2",target:"_blank",rel:"noopener noreferrer"}},[a._v("Part 1: Turn a simple API into GraphQL"),t("OutboundLink")],1)]),a._v(" "),t("li",[t("a",{attrs:{href:"https://medium.com/@ghostdogpr/graphql-in-scala-with-caliban-part-2-c7762110c0f9",target:"_blank",rel:"noopener noreferrer"}},[a._v("Part 2: Query optimization"),t("OutboundLink")],1)]),a._v(" "),t("li",[t("a",{attrs:{href:"https://medium.com/@ghostdogpr/graphql-in-scala-with-caliban-part-3-8962a02d5d64",target:"_blank",rel:"noopener noreferrer"}},[a._v("Part 3: Customization using wrappers"),t("OutboundLink")],1)])])]),a._v(" "),t("li",[t("a",{attrs:{href:"http://fokot.github.io/post/caliban-auth.html",target:"_blank",rel:"noopener noreferrer"}},[a._v("Authentication in Caliban"),t("OutboundLink")],1),a._v(" by František Kocun (December 2019)")])])])}),[],!1,null,null,null);r.default=n.exports}}]); \ No newline at end of file diff --git a/docs/assets/js/17.fa6e8c03.js b/docs/assets/js/17.fa6e8c03.js new file mode 100644 index 0000000000..fb5b859842 --- /dev/null +++ b/docs/assets/js/17.fa6e8c03.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[17],{141:function(n,w,o){}}]); \ No newline at end of file diff --git a/docs/assets/js/2.07c68b8b.js b/docs/assets/js/2.07c68b8b.js new file mode 100644 index 0000000000..e137d94774 --- /dev/null +++ b/docs/assets/js/2.07c68b8b.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[2],Array(142).concat([function(t,e,n){"use strict";n.d(e,"d",(function(){return i})),n.d(e,"a",(function(){return a})),n.d(e,"i",(function(){return s})),n.d(e,"f",(function(){return u})),n.d(e,"g",(function(){return l})),n.d(e,"h",(function(){return c})),n.d(e,"b",(function(){return f})),n.d(e,"e",(function(){return h})),n.d(e,"k",(function(){return p})),n.d(e,"l",(function(){return d})),n.d(e,"c",(function(){return g})),n.d(e,"j",(function(){return v}));n(17),n(66),n(103),n(159),n(168),n(40),n(29),n(143),n(41),n(169),n(67);var i=/#.*$/,r=/\.(md|html)$/,a=/\/$/,s=/^[a-z]+:/i;function o(t){return decodeURI(t).replace(i,"").replace(r,"")}function u(t){return s.test(t)}function l(t){return/^mailto:/.test(t)}function c(t){return/^tel:/.test(t)}function f(t){if(u(t))return t;var e=t.match(i),n=e?e[0]:"",r=o(t);return a.test(r)?t:r+".html"+n}function h(t,e){var n=decodeURIComponent(t.hash),r=function(t){var e=t.match(i);if(e)return e[0]}(e);return(!r||n===r)&&o(t.path)===o(e)}function p(t,e,n){if(u(e))return{type:"external",path:e};n&&(e=function(t,e,n){var i=t.charAt(0);if("/"===i)return t;if("?"===i||"#"===i)return e+t;var r=e.split("/");n&&r[r.length-1]||r.pop();for(var a=t.replace(/^\//,"").split("/"),s=0;s3&&void 0!==arguments[3]?arguments[3]:1;if("string"==typeof e)return p(n,e,i);if(Array.isArray(e))return Object.assign(p(n,e[0],i),{title:e[1]});r>3&&console.error("[vuepress] detected a too deep nested sidebar group.");var a=e.children||[];return 0===a.length&&e.path?Object.assign(p(n,e.path,i),{title:e.title}):{type:"group",path:e.path,title:e.title,sidebarDepth:e.sidebarDepth,children:a.map((function(e){return t(e,n,i,r+1)})),collapsable:!1!==e.collapsable}}(t,r,l)})):[]}return[]}function g(t){var e;return(t=t.map((function(t){return Object.assign({},t)}))).forEach((function(t){2===t.level?e=t:e&&(e.children||(e.children=[])).push(t)})),t.filter((function(t){return 2===t.level}))}function v(t){return Object.assign(t,{type:t.items&&t.items.length?"links":"link"})}},function(t,e,n){"use strict";var i=n(100),r=n(8),a=n(12),s=n(16),o=n(101),u=n(102);i("match",1,(function(t,e,n){return[function(e){var n=s(this),i=null==e?void 0:e[t];return void 0!==i?i.call(e,n):new RegExp(e)[t](String(n))},function(t){var i=n(e,t,this);if(i.done)return i.value;var s=r(t),l=String(this);if(!s.global)return u(s,l);var c=s.unicode;s.lastIndex=0;for(var f,h=[],p=0;null!==(f=u(s,l));){var d=String(f[0]);h[p]=d,""===d&&(s.lastIndex=o(l,a(s.lastIndex),c)),p++}return 0===p?null:h}]}))},function(t,e,n){},function(t,e){t.exports="\t\n\v\f\r                 \u2028\u2029\ufeff"},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},,function(t,e,n){n(0)({target:"Array",stat:!0},{isArray:n(42)})},function(t,e,n){var i=n(16),r="["+n(145)+"]",a=RegExp("^"+r+r+"*"),s=RegExp(r+r+"*$"),o=function(t){return function(e){var n=String(i(e));return 1&t&&(n=n.replace(a,"")),2&t&&(n=n.replace(s,"")),n}};t.exports={start:o(1),end:o(2),trim:o(3)}},function(t,e){t.exports=function(t){return null==t}},function(t,e,n){var i=n(189).Symbol;t.exports=i},function(t,e,n){"use strict";n.r(e);n(97);var i=n(142),r={name:"SidebarGroup",components:{DropdownTransition:n(164).a},props:["item","open","collapsable","depth"],beforeCreate:function(){this.$options.components.SidebarLinks=n(163).default},methods:{isActive:i.e}},a=(n(197),n(28)),s=Object(a.a)(r,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("section",{staticClass:"sidebar-group",class:[{collapsable:t.collapsable,"is-sub-group":0!==t.depth},"depth-"+t.depth]},[t.item.path?n("RouterLink",{staticClass:"sidebar-heading clickable",class:{open:t.open,active:t.isActive(t.$route,t.item.path)},attrs:{to:t.item.path},nativeOn:{click:function(e){return t.$emit("toggle")}}},[n("span",[t._v(t._s(t.item.title))]),t._v(" "),t.collapsable?n("span",{staticClass:"arrow",class:t.open?"down":"right"}):t._e()]):n("p",{staticClass:"sidebar-heading",class:{open:t.open},on:{click:function(e){return t.$emit("toggle")}}},[n("span",[t._v(t._s(t.item.title))]),t._v(" "),t.collapsable?n("span",{staticClass:"arrow",class:t.open?"down":"right"}):t._e()]),t._v(" "),n("DropdownTransition",[t.open||!t.collapsable?n("SidebarLinks",{staticClass:"sidebar-group-items",attrs:{items:t.item.children,"sidebar-depth":t.item.sidebarDepth,depth:t.depth+1}}):t._e()],1)],1)}),[],!1,null,null,null).exports;n(198),n(40);function o(t,e,n,i,r){var a={props:{to:e,activeClass:"",exactActiveClass:""},class:{active:i,"sidebar-link":!0}};return r>2&&(a.style={"padding-left":r+"rem"}),t("RouterLink",a,n)}function u(t,e,n,r,a){var s=arguments.length>5&&void 0!==arguments[5]?arguments[5]:1;return!e||s>a?null:t("ul",{class:"sidebar-sub-headers"},e.map((function(e){var l=Object(i.e)(r,n+"#"+e.slug);return t("li",{class:"sidebar-sub-header"},[o(t,n+"#"+e.slug,e.title,l,e.level-1),u(t,e.children,n,r,a,s+1)])})))}var l={functional:!0,props:["item","sidebarDepth"],render:function(t,e){var n=e.parent,r=n.$page,a=(n.$site,n.$route),s=n.$themeConfig,l=n.$themeLocaleConfig,c=e.props,f=c.item,h=c.sidebarDepth,p=Object(i.e)(a,f.path),d="auto"===f.type?p||f.children.some((function(t){return Object(i.e)(a,f.basePath+"#"+t.slug)})):p,g="external"===f.type?function(t,e,n){return t("a",{attrs:{href:e,target:"_blank",rel:"noopener noreferrer"},class:{"sidebar-link":!0}},[n,t("OutboundLink")])}(t,f.path,f.title||f.path):o(t,f.path,f.title||f.path,d),v=[r.frontmatter.sidebarDepth,h,l.sidebarDepth,s.sidebarDepth,1].find((function(t){return void 0!==t})),m=l.displayAllHeaders||s.displayAllHeaders;return"auto"===f.type?[g,u(t,f.children,f.basePath,a,v)]:(d||m)&&f.headers&&!i.d.test(f.path)?[g,u(t,Object(i.c)(f.headers),f.path,a,v)]:g}};n(199);function c(t,e){return"group"===e.type&&e.children.some((function(e){return"group"===e.type?c(t,e):"page"===e.type&&Object(i.e)(t,e.path)}))}var f={name:"SidebarLinks",components:{SidebarGroup:s,SidebarLink:Object(a.a)(l,void 0,void 0,!1,null,null,null).exports},props:["items","depth","sidebarDepth"],data:function(){return{openGroupIndex:0}},watch:{$route:function(){this.refreshIndex()}},created:function(){this.refreshIndex()},methods:{refreshIndex:function(){var t=function(t,e){for(var n=0;n-1&&(this.openGroupIndex=t)},toggleGroup:function(t){this.openGroupIndex=t===this.openGroupIndex?-1:t},isActive:function(t){return Object(i.e)(this.$route,t.regularPath)}}},h=Object(a.a)(f,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return t.items.length?n("ul",{staticClass:"sidebar-links"},t._l(t.items,(function(e,i){return n("li",{key:i},["group"===e.type?n("SidebarGroup",{attrs:{item:e,open:i===t.openGroupIndex,collapsable:e.collapsable||e.collapsible,depth:t.depth},on:{toggle:function(e){return t.toggleGroup(i)}}}):n("SidebarLink",{attrs:{"sidebar-depth":t.sidebarDepth,item:e}})],1)})),0):t._e()}),[],!1,null,null,null);e.default=h.exports},function(t,e,n){"use strict";var i={name:"DropdownTransition",methods:{setHeight:function(t){t.style.height=t.scrollHeight+"px"},unsetHeight:function(t){t.style.height=""}}},r=(n(181),n(28)),a=Object(r.a)(i,(function(){var t=this.$createElement;return(this._self._c||t)("transition",{attrs:{name:"dropdown"},on:{enter:this.setHeight,"after-enter":this.unsetHeight,"before-leave":this.setHeight}},[this._t("default")],2)}),[],!1,null,null,null);e.a=a.exports},function(t,e,n){"use strict";var i=n(0),r=n(166);i({target:"String",proto:!0,forced:n(167)("link")},{link:function(t){return r(this,"a","href",t)}})},function(t,e,n){var i=n(16),r=/"/g;t.exports=function(t,e,n,a){var s=String(i(t)),o="<"+e;return""!==n&&(o+=" "+n+'="'+String(a).replace(r,""")+'"'),o+">"+s+""}},function(t,e,n){var i=n(2);t.exports=function(t){return i((function(){var e=""[t]('"');return e!==e.toLowerCase()||e.split('"').length>3}))}},function(t,e,n){"use strict";var i=n(0),r=n(43),a=n(10),s=n(30),o=[].join,u=r!=Object,l=s("join",",");i({target:"Array",proto:!0,forced:u||!l},{join:function(t){return o.call(a(this),void 0===t?",":t)}})},function(t,e,n){"use strict";var i=n(100),r=n(98),a=n(8),s=n(16),o=n(170),u=n(101),l=n(12),c=n(102),f=n(46),h=n(2),p=[].push,d=Math.min,g=!h((function(){return!RegExp(4294967295,"y")}));i("split",2,(function(t,e,n){var i;return i="c"=="abbc".split(/(b)*/)[1]||4!="test".split(/(?:)/,-1).length||2!="ab".split(/(?:ab)*/).length||4!=".".split(/(.?)(.?)/).length||".".split(/()()/).length>1||"".split(/.?/).length?function(t,n){var i=String(s(this)),a=void 0===n?4294967295:n>>>0;if(0===a)return[];if(void 0===t)return[i];if(!r(t))return e.call(i,t,a);for(var o,u,l,c=[],h=(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.unicode?"u":"")+(t.sticky?"y":""),d=0,g=new RegExp(t.source,h+"g");(o=f.call(g,i))&&!((u=g.lastIndex)>d&&(c.push(i.slice(d,o.index)),o.length>1&&o.index=a));)g.lastIndex===o.index&&g.lastIndex++;return d===i.length?!l&&g.test("")||c.push(""):c.push(i.slice(d)),c.length>a?c.slice(0,a):c}:"0".split(void 0,0).length?function(t,n){return void 0===t&&0===n?[]:e.call(this,t,n)}:e,[function(e,n){var r=s(this),a=null==e?void 0:e[t];return void 0!==a?a.call(e,r,n):i.call(String(r),e,n)},function(t,r){var s=n(i,t,this,r,i!==e);if(s.done)return s.value;var f=a(t),h=String(this),p=o(f,RegExp),v=f.unicode,m=(f.ignoreCase?"i":"")+(f.multiline?"m":"")+(f.unicode?"u":"")+(g?"y":"g"),b=new p(g?f:"^(?:"+f.source+")",m),k=void 0===r?4294967295:r>>>0;if(0===k)return[];if(0===h.length)return null===c(b,h)?[h]:[];for(var _=0,x=0,y=[];x>>0||(o.test(n)?16:10))}:s},function(t,e,n){"use strict";var i=n(0),r=n(160).trim;i({target:"String",proto:!0,forced:n(175)("trim")},{trim:function(){return r(this)}})},function(t,e,n){var i=n(2),r=n(145);t.exports=function(t){return i((function(){return!!r[t]()||"​…᠎"!="​…᠎"[t]()||r[t].name!==t}))}},function(t,e,n){"use strict";var i=n(146);n.n(i).a},function(t,e,n){"use strict";var i=n(147);n.n(i).a},function(t,e,n){"use strict";var i=n(0),r=n(2),a=n(42),s=n(4),o=n(14),u=n(12),l=n(64),c=n(104),f=n(45),h=n(1),p=n(105),d=h("isConcatSpreadable"),g=p>=51||!r((function(){var t=[];return t[d]=!1,t.concat()[0]!==t})),v=f("concat"),m=function(t){if(!s(t))return!1;var e=t[d];return void 0!==e?!!e:a(t)};i({target:"Array",proto:!0,forced:!g||!v},{concat:function(t){var e,n,i,r,a,s=o(this),f=c(s,0),h=0;for(e=-1,i=arguments.length;e9007199254740991)throw TypeError("Maximum allowed index exceeded");for(n=0;n=9007199254740991)throw TypeError("Maximum allowed index exceeded");l(f,h++,a)}return f.length=h,f}})},function(t,e,n){var i=n(6),r=n(3),a=n(62),s=n(109),o=n(7).f,u=n(44).f,l=n(98),c=n(99),f=n(107),h=n(13),p=n(2),d=n(18).set,g=n(110),v=n(1)("match"),m=r.RegExp,b=m.prototype,k=/a/g,_=/a/g,x=new m(k)!==k,y=f.UNSUPPORTED_Y;if(i&&a("RegExp",!x||y||p((function(){return _[v]=!1,m(k)!=k||m(_)==_||"/a/i"!=m(k,"i")})))){for(var C=function(t,e){var n,i=this instanceof C,r=l(t),a=void 0===e;if(!i&&r&&t.constructor===C&&a)return t;x?r&&!a&&(t=t.source):t instanceof C&&(a&&(e=c.call(t)),t=t.source),y&&(n=!!e&&e.indexOf("y")>-1)&&(e=e.replace(/y/g,""));var o=s(x?new m(t,e):m(t,e),i?this:b,C);return y&&n&&d(o,{sticky:n}),o},$=function(t){t in C||o(C,t,{configurable:!0,get:function(){return m[t]},set:function(e){m[t]=e}})},L=u(m),S=0;L.length>S;)$(L[S++]);b.constructor=C,C.prototype=b,h(r,"RegExp",C)}g("RegExp")},function(t,e,n){"use strict";var i=n(13),r=n(8),a=n(2),s=n(99),o=RegExp.prototype,u=o.toString,l=a((function(){return"/a/b"!=u.call({source:"a",flags:"b"})})),c="toString"!=u.name;(l||c)&&i(RegExp.prototype,"toString",(function(){var t=r(this),e=String(t.source),n=t.flags;return"/"+e+"/"+String(void 0===n&&t instanceof RegExp&&!("flags"in o)?s.call(t):n)}),{unsafe:!0})},function(t,e,n){"use strict";var i=n(148);n.n(i).a},function(t,e){t.exports=function(t){var e=null==t?0:t.length;return e?t[e-1]:void 0}},function(t,e,n){"use strict";var i=n(149);n.n(i).a},function(t,e,n){"use strict";var i=n(150);n.n(i).a},function(t,e,n){"use strict";var i=n(151);n.n(i).a},function(t,e,n){"use strict";var i=n(152);n.n(i).a},function(t,e,n){var i=n(188),r=n(193),a=n(194);t.exports=function(t){return"string"==typeof t||!r(t)&&a(t)&&"[object String]"==i(t)}},function(t,e,n){var i=n(162),r=n(191),a=n(192),s=i?i.toStringTag:void 0;t.exports=function(t){return null==t?void 0===t?"[object Undefined]":"[object Null]":s&&s in Object(t)?r(t):a(t)}},function(t,e,n){var i=n(190),r="object"==typeof self&&self&&self.Object===Object&&self,a=i||r||Function("return this")();t.exports=a},function(t,e){var n="object"==typeof global&&global&&global.Object===Object&&global;t.exports=n},function(t,e,n){var i=n(162),r=Object.prototype,a=r.hasOwnProperty,s=r.toString,o=i?i.toStringTag:void 0;t.exports=function(t){var e=a.call(t,o),n=t[o];try{t[o]=void 0;var i=!0}catch(t){}var r=s.call(t);return i&&(e?t[o]=n:delete t[o]),r}},function(t,e){var n=Object.prototype.toString;t.exports=function(t){return n.call(t)}},function(t,e){var n=Array.isArray;t.exports=n},function(t,e){t.exports=function(t){return null!=t&&"object"==typeof t}},function(t,e,n){"use strict";var i=n(153);n.n(i).a},function(t,e,n){"use strict";var i=n(154);n.n(i).a},function(t,e,n){"use strict";var i=n(155);n.n(i).a},function(t,e,n){"use strict";var i=n(0),r=n(19).find,a=n(65),s=n(11),o=!0,u=s("find");"find"in[]&&Array(1).find((function(){o=!1})),i({target:"Array",proto:!0,forced:o||!u},{find:function(t){return r(this,t,arguments.length>1?arguments[1]:void 0)}}),a("find")},function(t,e,n){"use strict";var i=n(156);n.n(i).a},function(t,e,n){"use strict";var i=n(157);n.n(i).a},,function(t,e,n){"use strict";n.r(e);n(97),n(61),n(165);var i=n(142),r={name:"NavLink",props:{item:{required:!0}},computed:{link:function(){return Object(i.b)(this.item.link)},exact:function(){var t=this;return this.$site.locales?Object.keys(this.$site.locales).some((function(e){return e===t.link})):"/"===this.link},isNonHttpURI:function(){return Object(i.g)(this.link)||Object(i.h)(this.link)},isBlankTarget:function(){return"_blank"===this.target},isInternal:function(){return!Object(i.f)(this.link)&&!this.isBlankTarget},target:function(){return this.isNonHttpURI?null:this.item.target?this.item.target:Object(i.f)(this.link)?"_blank":""},rel:function(){return this.isNonHttpURI?null:this.item.rel?this.item.rel:this.isBlankTarget?"noopener noreferrer":""}},methods:{focusoutAction:function(){this.$emit("focusout")}}},a=n(28),s=Object(a.a)(r,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return t.isInternal?n("RouterLink",{staticClass:"nav-link",attrs:{to:t.link,exact:t.exact},nativeOn:{focusout:function(e){return t.focusoutAction(e)}}},[t._v("\n "+t._s(t.item.text)+"\n")]):n("a",{staticClass:"nav-link external",attrs:{href:t.link,target:t.target,rel:t.rel},on:{focusout:t.focusoutAction}},[t._v("\n "+t._s(t.item.text)+"\n "),t.isBlankTarget?n("OutboundLink"):t._e()],1)}),[],!1,null,null,null).exports,o={name:"Home",components:{NavLink:s},computed:{data:function(){return this.$page.frontmatter},actionLink:function(){return{link:this.data.actionLink,text:this.data.actionText}}}},u=(n(171),Object(a.a)(o,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("main",{staticClass:"home",attrs:{"aria-labelledby":"main-title"}},[n("header",{staticClass:"hero"},[t.data.heroImage?n("img",{attrs:{src:t.$withBase(t.data.heroImage),alt:t.data.heroAlt||"hero"}}):t._e(),t._v(" "),null!==t.data.heroText?n("h1",{attrs:{id:"main-title"}},[t._v("\n "+t._s(t.data.heroText||t.$title||"Hello")+"\n ")]):t._e(),t._v(" "),null!==t.data.tagline?n("p",{staticClass:"description"},[t._v("\n "+t._s(t.data.tagline||t.$description||"Welcome to your VuePress site")+"\n ")]):t._e(),t._v(" "),t.data.actionText&&t.data.actionLink?n("p",{staticClass:"action"},[n("NavLink",{staticClass:"action-button",attrs:{item:t.actionLink}})],1):t._e()]),t._v(" "),t.data.features&&t.data.features.length?n("div",{staticClass:"features"},t._l(t.data.features,(function(e,i){return n("div",{key:i,staticClass:"feature"},[n("h2",[t._v(t._s(e.title))]),t._v(" "),n("p",[t._v(t._s(e.details))])])})),0):t._e(),t._v(" "),n("Content",{staticClass:"theme-default-content custom"}),t._v(" "),t.data.footer?n("div",{staticClass:"footer"},[t._v("\n "+t._s(t.data.footer)+"\n ")]):t._e()],1)}),[],!1,null,null,null).exports),l=(n(172),n(17),n(106),n(103),n(159),n(29),n(108),n(143),n(174),{name:"SearchBox",data:function(){return{query:"",focused:!1,focusIndex:0,placeholder:void 0}},computed:{showSuggestions:function(){return this.focused&&this.suggestions&&this.suggestions.length},suggestions:function(){var t=this.query.trim().toLowerCase();if(t){for(var e=this.$site.pages,n=this.$site.themeConfig.searchMaxSuggestions||5,i=this.$localePath,r=function(e){return e&&e.title&&e.title.toLowerCase().indexOf(t)>-1},a=[],s=0;s=n);s++){var o=e[s];if(this.getPageLocalePath(o)===i&&this.isSearchable(o))if(r(o))a.push(o);else if(o.headers)for(var u=0;u=n);u++){var l=o.headers[u];r(l)&&a.push(Object.assign({},o,{path:o.path+"#"+l.slug,header:l}))}}return a}},alignRight:function(){return(this.$site.themeConfig.nav||[]).length+(this.$site.repo?1:0)<=2}},mounted:function(){this.placeholder=this.$site.themeConfig.searchPlaceholder||"",document.addEventListener("keydown",this.onHotkey)},beforeDestroy:function(){document.removeEventListener("keydown",this.onHotkey)},methods:{getPageLocalePath:function(t){for(var e in this.$site.locales||{})if("/"!==e&&0===t.path.indexOf(e))return e;return"/"},isSearchable:function(t){var e=null;return null===e||(e=Array.isArray(e)?e:new Array(e)).filter((function(e){return t.path.match(e)})).length>0},onHotkey:function(t){t.srcElement===document.body&&["s","/"].includes(t.key)&&(this.$refs.input.focus(),t.preventDefault())},onUp:function(){this.showSuggestions&&(this.focusIndex>0?this.focusIndex--:this.focusIndex=this.suggestions.length-1)},onDown:function(){this.showSuggestions&&(this.focusIndex "+t._s(e.header.title))]):t._e()])])})),0):t._e()])}),[],!1,null,null,null).exports),f=(n(177),Object(a.a)({},(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"sidebar-button",on:{click:function(e){return t.$emit("toggle-sidebar")}}},[n("svg",{staticClass:"icon",attrs:{xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",role:"img",viewBox:"0 0 448 512"}},[n("path",{attrs:{fill:"currentColor",d:"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"}})])])}),[],!1,null,null,null).exports),h=(n(178),n(40),n(179),n(180),n(41),n(38)),p=n(164),d=n(182),g=n.n(d),v={name:"DropdownLink",components:{NavLink:s,DropdownTransition:p.a},props:{item:{required:!0}},data:function(){return{open:!1}},computed:{dropdownAriaLabel:function(){return this.item.ariaLabel||this.item.text}},watch:{$route:function(){this.open=!1}},methods:{setOpen:function(t){this.open=t},isLastItemOfArray:function(t,e){return g()(e)===t}}},m=(n(183),{name:"NavLinks",components:{NavLink:s,DropdownLink:Object(a.a)(v,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"dropdown-wrapper",class:{open:t.open}},[n("button",{staticClass:"dropdown-title",attrs:{type:"button","aria-label":t.dropdownAriaLabel},on:{click:function(e){return t.setOpen(!t.open)}}},[n("span",{staticClass:"title"},[t._v(t._s(t.item.text))]),t._v(" "),n("span",{staticClass:"arrow",class:t.open?"down":"right"})]),t._v(" "),n("DropdownTransition",[n("ul",{directives:[{name:"show",rawName:"v-show",value:t.open,expression:"open"}],staticClass:"nav-dropdown"},t._l(t.item.items,(function(e,i){return n("li",{key:e.link||i,staticClass:"dropdown-item"},["links"===e.type?n("h4",[t._v("\n "+t._s(e.text)+"\n ")]):t._e(),t._v(" "),"links"===e.type?n("ul",{staticClass:"dropdown-subitem-wrapper"},t._l(e.items,(function(i){return n("li",{key:i.link,staticClass:"dropdown-subitem"},[n("NavLink",{attrs:{item:i},on:{focusout:function(n){t.isLastItemOfArray(i,e.items)&&t.isLastItemOfArray(e,t.item.items)&&t.setOpen(!1)}}})],1)})),0):n("NavLink",{attrs:{item:e},on:{focusout:function(n){t.isLastItemOfArray(e,t.item.items)&&t.setOpen(!1)}}})],1)})),0)])],1)}),[],!1,null,null,null).exports},computed:{userNav:function(){return this.$themeLocaleConfig.nav||this.$site.themeConfig.nav||[]},nav:function(){var t=this,e=this.$site.locales;if(e&&Object.keys(e).length>1){var n=this.$page.path,i=this.$router.options.routes,r=this.$site.themeConfig.locales||{},a={text:this.$themeLocaleConfig.selectText||"Languages",ariaLabel:this.$themeLocaleConfig.ariaLabel||"Select language",items:Object.keys(e).map((function(a){var s,o=e[a],u=r[a]&&r[a].label||o.lang;return o.lang===t.$lang?s=n:(s=n.replace(t.$localeConfig.path,a),i.some((function(t){return t.path===s}))||(s=a)),{text:u,link:s}}))};return[].concat(Object(h.a)(this.userNav),[a])}return this.userNav},userLinks:function(){return(this.nav||[]).map((function(t){return Object.assign(Object(i.j)(t),{items:(t.items||[]).map(i.j)})}))},repoLink:function(){var t=this.$site.themeConfig.repo;return t?/^https?:/.test(t)?t:"https://github.com/".concat(t):null},repoLabel:function(){if(this.repoLink){if(this.$site.themeConfig.repoLabel)return this.$site.themeConfig.repoLabel;for(var t=this.repoLink.match(/^https?:\/\/[^/]+/)[0],e=["GitHub","GitLab","Bitbucket"],n=0;nMath.abs(n)&&Math.abs(e)>40&&(e>0&&this.touchStart.x<=80?this.toggleSidebar(!0):this.toggleSidebar(!1))}}}),D=Object(a.a)(R,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"theme-container",class:t.pageClasses,on:{touchstart:t.onTouchStart,touchend:t.onTouchEnd}},[t.shouldShowNavbar?n("Navbar",{on:{"toggle-sidebar":t.toggleSidebar}}):t._e(),t._v(" "),n("div",{staticClass:"sidebar-mask",on:{click:function(e){return t.toggleSidebar(!1)}}}),t._v(" "),n("Sidebar",{attrs:{items:t.sidebarItems},on:{"toggle-sidebar":t.toggleSidebar},scopedSlots:t._u([{key:"top",fn:function(){return[t._t("sidebar-top")]},proxy:!0},{key:"bottom",fn:function(){return[t._t("sidebar-bottom")]},proxy:!0}],null,!0)}),t._v(" "),t.$page.frontmatter.home?n("Home"):n("Page",{attrs:{"sidebar-items":t.sidebarItems},scopedSlots:t._u([{key:"top",fn:function(){return[t._t("page-top")]},proxy:!0},{key:"bottom",fn:function(){return[t._t("page-bottom")]},proxy:!0}],null,!0)})],1)}),[],!1,null,null,null);e.default=D.exports}])]); \ No newline at end of file diff --git a/docs/assets/js/2.fa272699.js b/docs/assets/js/2.fa272699.js deleted file mode 100644 index b0e942aed1..0000000000 --- a/docs/assets/js/2.fa272699.js +++ /dev/null @@ -1 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[2],[,,,,,,,,,,function(t,e,n){var r=n(12),i=n(28),o=n(16),a=n(25),s=n(46),u=function(t,e,n){var c,l,f,p,h=t&u.F,d=t&u.G,v=t&u.S,g=t&u.P,m=t&u.B,b=d?r:v?r[e]||(r[e]={}):(r[e]||{}).prototype,y=d?i:i[e]||(i[e]={}),x=y.prototype||(y.prototype={});for(c in d&&(n=e),n)f=((l=!h&&b&&void 0!==b[c])?b:n)[c],p=m&&l?s(f,r):g&&"function"==typeof f?s(Function.call,f):f,b&&a(b,c,f,t&u.U),y[c]!=f&&o(y,c,p),g&&x[c]!=f&&(x[c]=f)};r.core=i,u.F=1,u.G=2,u.S=4,u.P=8,u.B=16,u.W=32,u.U=64,u.R=128,t.exports=u},function(t,e,n){var r=n(42)("wks"),i=n(43),o=n(12).Symbol,a="function"==typeof o;(t.exports=function(t){return r[t]||(r[t]=a&&o[t]||(a?o:i)("Symbol."+t))}).store=r},function(t,e){var n=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(t,e,n){var r=n(97)("wks"),i=n(98),o=n(21).Symbol,a="function"==typeof o;(t.exports=function(t){return r[t]||(r[t]=a&&o[t]||(a?o:i)("Symbol."+t))}).store=r},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e,n){"use strict";n.d(e,"d",(function(){return r})),n.d(e,"a",(function(){return o})),n.d(e,"i",(function(){return a})),n.d(e,"f",(function(){return u})),n.d(e,"g",(function(){return c})),n.d(e,"h",(function(){return l})),n.d(e,"b",(function(){return f})),n.d(e,"e",(function(){return p})),n.d(e,"k",(function(){return h})),n.d(e,"l",(function(){return d})),n.d(e,"c",(function(){return v})),n.d(e,"j",(function(){return g}));const r=/#.*$/,i=/\.(md|html)$/,o=/\/$/,a=/^[a-z]+:/i;function s(t){return decodeURI(t).replace(r,"").replace(i,"")}function u(t){return a.test(t)}function c(t){return/^mailto:/.test(t)}function l(t){return/^tel:/.test(t)}function f(t){if(u(t))return t;const e=t.match(r),n=e?e[0]:"",i=s(t);return o.test(i)?t:i+".html"+n}function p(t,e){const n=t.hash,i=function(t){const e=t.match(r);if(e)return e[0]}(e);return(!i||n===i)&&s(t.path)===s(e)}function h(t,e,n){if(u(e))return{type:"external",path:e};n&&(e=function(t,e,n){const r=t.charAt(0);if("/"===r)return t;if("?"===r||"#"===r)return e+t;const i=e.split("/");n&&i[i.length-1]||i.pop();const o=t.replace(/^\//,"").split("/");for(let t=0;t({type:"auto",title:e.title,basePath:t.path,path:t.path+"#"+e.slug,children:e.children||[]}))}]}(t);const s=a.sidebar||o.sidebar;if(s){const{base:t,config:n}=function(t,e){if(Array.isArray(e))return{base:"/",config:e};for(const r in e)if(0===(n=t,/(\.html|\/)$/.test(n)?n:n+"/").indexOf(encodeURI(r)))return{base:r,config:e[r]};var n;return{}}(e,s);return n?n.map(e=>function t(e,n,r,i=1){if("string"==typeof e)return h(n,e,r);if(Array.isArray(e))return Object.assign(h(n,e[0],r),{title:e[1]});{i>3&&console.error("[vuepress] detected a too deep nested sidebar group.");const o=e.children||[];return 0===o.length&&e.path?Object.assign(h(n,e.path,r),{title:e.title}):{type:"group",path:e.path,title:e.title,sidebarDepth:e.sidebarDepth,children:o.map(e=>t(e,n,r,i+1)),collapsable:!1!==e.collapsable}}}(e,i,t)):[]}return[]}function v(t){let e;return(t=t.map(t=>Object.assign({},t))).forEach(t=>{2===t.level?e=t:e&&(e.children||(e.children=[])).push(t)}),t.filter(t=>2===t.level)}function g(t){return Object.assign(t,{type:t.items&&t.items.length?"links":"link"})}},function(t,e,n){var r=n(24),i=n(44);t.exports=n(19)?function(t,e,n){return r.f(t,e,i(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e,n){var r=n(18);t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,e,n){t.exports=!n(14)((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a}))},function(t,e){t.exports=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t}},function(t,e){var n=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(t,e){var n=t.exports={version:"2.6.11"};"number"==typeof __e&&(__e=n)},function(t,e){t.exports={}},function(t,e,n){var r=n(17),i=n(75),o=n(77),a=Object.defineProperty;e.f=n(19)?Object.defineProperty:function(t,e,n){if(r(t),e=o(e,!0),r(n),i)try{return a(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[e]=n.value),t}},function(t,e,n){var r=n(12),i=n(16),o=n(26),a=n(43)("src"),s=n(112),u=(""+s).split("toString");n(28).inspectSource=function(t){return s.call(t)},(t.exports=function(t,e,n,s){var c="function"==typeof n;c&&(o(n,"name")||i(n,"name",e)),t[e]!==n&&(c&&(o(n,a)||i(n,a,t[e]?""+t[e]:u.join(String(e)))),t===r?t[e]=n:s?t[e]?t[e]=n:i(t,e,n):(delete t[e],i(t,e,n)))})(Function.prototype,"toString",(function(){return"function"==typeof this&&this[a]||s.call(this)}))},function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},function(t,e,n){var r=n(37),i=n(56);t.exports=n(39)?function(t,e,n){return r.f(t,e,i(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e){var n=t.exports={version:"2.6.11"};"number"==typeof __e&&(__e=n)},function(t,e,n){var r=n(78),i=n(20);t.exports=function(t){return r(i(t))}},function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},function(t,e,n){var r=n(32),i=Math.min;t.exports=function(t){return t>0?i(r(t),9007199254740991):0}},function(t,e){var n=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:n)(t)}},function(t,e,n){var r=n(20);t.exports=function(t){return Object(r(t))}},function(t,e,n){"use strict";var r=n(10),i=n(35)(3);r(r.P+r.F*!n(36)([].some,!0),"Array",{some:function(t){return i(this,t,arguments[1])}})},function(t,e,n){var r=n(46),i=n(78),o=n(33),a=n(31),s=n(121);t.exports=function(t,e){var n=1==t,u=2==t,c=3==t,l=4==t,f=6==t,p=5==t||f,h=e||s;return function(e,s,d){for(var v,g,m=o(e),b=i(m),y=r(s,d,3),x=a(b.length),_=0,k=n?h(e,x):u?h(e,0):void 0;x>_;_++)if((p||_ in b)&&(g=y(v=b[_],_,m),t))if(n)k[_]=g;else if(g)switch(t){case 3:return!0;case 5:return v;case 6:return _;case 2:k.push(v)}else if(l)return!1;return f?-1:c||l?l:k}}},function(t,e,n){"use strict";var r=n(14);t.exports=function(t,e){return!!t&&r((function(){e?t.call(null,(function(){}),1):t.call(null)}))}},function(t,e,n){var r=n(38),i=n(152),o=n(153),a=Object.defineProperty;e.f=n(39)?Object.defineProperty:function(t,e,n){if(r(t),e=o(e,!0),r(n),i)try{return a(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[e]=n.value),t}},function(t,e,n){var r=n(55);t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},function(t,e,n){t.exports=!n(91)((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a}))},function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},function(t,e,n){var r=n(11)("unscopables"),i=Array.prototype;null==i[r]&&n(16)(i,r,{}),t.exports=function(t){i[r][t]=!0}},function(t,e,n){var r=n(28),i=n(12),o=i["__core-js_shared__"]||(i["__core-js_shared__"]={});(t.exports=function(t,e){return o[t]||(o[t]=void 0!==e?e:{})})("versions",[]).push({version:r.version,mode:n(74)?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})},function(t,e){var n=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+r).toString(36))}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e){t.exports={}},function(t,e,n){var r=n(113);t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,i){return t.call(e,n,r,i)}}return function(){return t.apply(e,arguments)}}},function(t,e,n){var r=n(79),i=n(50);t.exports=Object.keys||function(t){return r(t,i)}},function(t,e,n){var r=n(29),i=n(31),o=n(117);t.exports=function(t){return function(e,n,a){var s,u=r(e),c=i(u.length),l=o(a,c);if(t&&n!=n){for(;c>l;)if((s=u[l++])!=s)return!0}else for(;c>l;l++)if((t||l in u)&&u[l]===n)return t||l||0;return!t&&-1}}},function(t,e,n){var r=n(42)("keys"),i=n(43);t.exports=function(t){return r[t]||(r[t]=i(t))}},function(t,e){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){var r=n(21),i=n(22),o=n(90),a=n(27),s=n(40),u=function(t,e,n){var c,l,f,p=t&u.F,h=t&u.G,d=t&u.S,v=t&u.P,g=t&u.B,m=t&u.W,b=h?i:i[e]||(i[e]={}),y=b.prototype,x=h?r:d?r[e]:(r[e]||{}).prototype;for(c in h&&(n=e),n)(l=!p&&x&&void 0!==x[c])&&s(b,c)||(f=l?x[c]:n[c],b[c]=h&&"function"!=typeof x[c]?n[c]:g&&l?o(f,r):m&&x[c]==f?function(t){var e=function(e,n,r){if(this instanceof t){switch(arguments.length){case 0:return new t;case 1:return new t(e);case 2:return new t(e,n)}return new t(e,n,r)}return t.apply(this,arguments)};return e.prototype=t.prototype,e}(f):v&&"function"==typeof f?o(Function.call,f):f,v&&((b.virtual||(b.virtual={}))[c]=f,t&u.R&&y&&!y[c]&&a(y,c,f)))};u.F=1,u.G=2,u.S=4,u.P=8,u.B=16,u.W=32,u.U=64,u.R=128,t.exports=u},function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},function(t,e){var n=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:n)(t)}},function(t,e){t.exports=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t}},function(t,e,n){var r=n(164),i=n(59);t.exports=function(t){return r(i(t))}},function(t,e,n){var r=n(97)("keys"),i=n(98);t.exports=function(t){return r[t]||(r[t]=i(t))}},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},,function(t,e,n){for(var r=n(109),i=n(47),o=n(25),a=n(12),s=n(16),u=n(45),c=n(11),l=c("iterator"),f=c("toStringTag"),p=u.Array,h={CSSRuleList:!0,CSSStyleDeclaration:!1,CSSValueList:!1,ClientRectList:!1,DOMRectList:!1,DOMStringList:!1,DOMTokenList:!0,DataTransferItemList:!1,FileList:!1,HTMLAllCollection:!1,HTMLCollection:!1,HTMLFormElement:!1,HTMLSelectElement:!1,MediaList:!0,MimeTypeArray:!1,NamedNodeMap:!1,NodeList:!0,PaintRequestList:!1,Plugin:!1,PluginArray:!1,SVGLengthList:!1,SVGNumberList:!1,SVGPathSegList:!1,SVGPointList:!1,SVGStringList:!1,SVGTransformList:!1,SourceBufferList:!1,StyleSheetList:!0,TextTrackCueList:!1,TextTrackList:!1,TouchList:!1},d=i(h),v=0;vu;)r(s,n=e[u++])&&(~o(c,n)||c.push(n));return c}},function(t,e,n){var r=n(24).f,i=n(26),o=n(11)("toStringTag");t.exports=function(t,e,n){t&&!i(t=n?t:t.prototype,o)&&r(t,o,{configurable:!0,value:e})}},function(t,e,n){var r=n(33),i=n(47);n(120)("keys",(function(){return function(t){return i(r(t))}}))},function(t,e,n){var r=n(30);t.exports=Array.isArray||function(t){return"Array"==r(t)}},function(t,e,n){var r=n(18),i=n(30),o=n(11)("match");t.exports=function(t){var e;return r(t)&&(void 0!==(e=t[o])?!!e:"RegExp"==i(t))}},function(t,e,n){"use strict";var r=n(17),i=n(31),o=n(85),a=n(86);n(87)("match",1,(function(t,e,n,s){return[function(n){var r=t(this),i=null==n?void 0:n[e];return void 0!==i?i.call(n,r):new RegExp(n)[e](String(r))},function(t){var e=s(n,t,this);if(e.done)return e.value;var u=r(t),c=String(this);if(!u.global)return a(u,c);var l=u.unicode;u.lastIndex=0;for(var f,p=[],h=0;null!==(f=a(u,c));){var d=String(f[0]);p[h]=d,""===d&&(u.lastIndex=o(c,i(u.lastIndex),l)),h++}return 0===h?null:p}]}))},function(t,e,n){"use strict";var r=n(130)(!0);t.exports=function(t,e,n){return e+(n?r(t,e).length:1)}},function(t,e,n){"use strict";var r=n(131),i=RegExp.prototype.exec;t.exports=function(t,e){var n=t.exec;if("function"==typeof n){var o=n.call(t,e);if("object"!=typeof o)throw new TypeError("RegExp exec method returned something other than an Object or null");return o}if("RegExp"!==r(t))throw new TypeError("RegExp#exec called on incompatible receiver");return i.call(t,e)}},function(t,e,n){"use strict";n(132);var r=n(25),i=n(16),o=n(14),a=n(20),s=n(11),u=n(88),c=s("species"),l=!o((function(){var t=/./;return t.exec=function(){var t=[];return t.groups={a:"7"},t},"7"!=="".replace(t,"$")})),f=function(){var t=/(?:)/,e=t.exec;t.exec=function(){return e.apply(this,arguments)};var n="ab".split(t);return 2===n.length&&"a"===n[0]&&"b"===n[1]}();t.exports=function(t,e,n){var p=s(t),h=!o((function(){var e={};return e[p]=function(){return 7},7!=""[t](e)})),d=h?!o((function(){var e=!1,n=/a/;return n.exec=function(){return e=!0,null},"split"===t&&(n.constructor={},n.constructor[c]=function(){return n}),n[p](""),!e})):void 0;if(!h||!d||"replace"===t&&!l||"split"===t&&!f){var v=/./[p],g=n(a,p,""[t],(function(t,e,n,r,i){return e.exec===u?h&&!i?{done:!0,value:v.call(e,n,r)}:{done:!0,value:t.call(n,e,r)}:{done:!1}})),m=g[0],b=g[1];r(String.prototype,t,m),i(RegExp.prototype,p,2==e?function(t,e){return b.call(t,this,e)}:function(t){return b.call(t,this)})}}},function(t,e,n){"use strict";var r,i,o=n(89),a=RegExp.prototype.exec,s=String.prototype.replace,u=a,c=(r=/a/,i=/b*/g,a.call(r,"a"),a.call(i,"a"),0!==r.lastIndex||0!==i.lastIndex),l=void 0!==/()??/.exec("")[1];(c||l)&&(u=function(t){var e,n,r,i,u=this;return l&&(n=new RegExp("^"+u.source+"$(?!\\s)",o.call(u))),c&&(e=u.lastIndex),r=a.call(u,t),c&&r&&(u.lastIndex=u.global?r.index+r[0].length:e),l&&r&&r.length>1&&s.call(r[0],n,(function(){for(i=1;i=e.length?{value:void 0,done:!0}:(t=r(e,n),this._i+=t.length,{value:t,done:!1})}))},function(t,e,n){"use strict";var r=n(95),i=n(54),o=n(158),a=n(27),s=n(23),u=n(159),c=n(100),l=n(168),f=n(13)("iterator"),p=!([].keys&&"next"in[].keys()),h=function(){return this};t.exports=function(t,e,n,d,v,g,m){u(n,e,d);var b,y,x,_=function(t){if(!p&&t in O)return O[t];switch(t){case"keys":case"values":return function(){return new n(this,t)}}return function(){return new n(this,t)}},k=e+" Iterator",S="values"==v,L=!1,O=t.prototype,w=O[f]||O["@@iterator"]||v&&O[v],C=w||_(v),$=v?S?_("entries"):C:void 0,j="Array"==e&&O.entries||w;if(j&&(x=l(j.call(new t)))!==Object.prototype&&x.next&&(c(x,k,!0),r||"function"==typeof x[f]||a(x,f,h)),S&&w&&"values"!==w.name&&(L=!0,C=function(){return w.call(this)}),r&&!m||!p&&!L&&O[f]||a(O,f,C),s[e]=C,s[k]=h,v)if(b={values:S?C:_("values"),keys:g?C:_("keys"),entries:$},m)for(y in b)y in O||o(O,y,b[y]);else i(i.P+i.F*(p||L),e,b);return b}},function(t,e){t.exports=!0},function(t,e,n){var r=n(58),i=Math.min;t.exports=function(t){return t>0?i(r(t),9007199254740991):0}},function(t,e,n){var r=n(22),i=n(21),o=i["__core-js_shared__"]||(i["__core-js_shared__"]={});(t.exports=function(t,e){return o[t]||(o[t]=void 0!==e?e:{})})("versions",[]).push({version:r.version,mode:n(95)?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})},function(t,e){var n=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+r).toString(36))}},function(t,e){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,e,n){var r=n(37).f,i=n(40),o=n(13)("toStringTag");t.exports=function(t,e,n){t&&!i(t=n?t:t.prototype,o)&&r(t,o,{configurable:!0,value:e})}},function(t,e,n){var r=n(59);t.exports=function(t){return Object(r(t))}},function(t,e,n){var r=n(57),i=n(13)("toStringTag"),o="Arguments"==r(function(){return arguments}());t.exports=function(t){var e,n,a;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=function(t,e){try{return t[e]}catch(t){}}(e=Object(t),i))?n:o?r(e):"Object"==(a=r(e))&&"function"==typeof e.callee?"Arguments":a}},function(t,e,n){"use strict";var r=n(17),i=n(33),o=n(31),a=n(32),s=n(85),u=n(86),c=Math.max,l=Math.min,f=Math.floor,p=/\$([$&`']|\d\d?|<[^>]*>)/g,h=/\$([$&`']|\d\d?)/g;n(87)("replace",2,(function(t,e,n,d){return[function(r,i){var o=t(this),a=null==r?void 0:r[e];return void 0!==a?a.call(r,o,i):n.call(String(o),r,i)},function(t,e){var i=d(n,t,this,e);if(i.done)return i.value;var f=r(t),p=String(this),h="function"==typeof e;h||(e=String(e));var g=f.global;if(g){var m=f.unicode;f.lastIndex=0}for(var b=[];;){var y=u(f,p);if(null===y)break;if(b.push(y),!g)break;""===String(y[0])&&(f.lastIndex=s(p,o(f.lastIndex),m))}for(var x,_="",k=0,S=0;S=k&&(_+=p.slice(k,O)+A,k=O+L.length)}return _+p.slice(k)}];function v(t,e,r,o,a,s){var u=r+t.length,c=o.length,l=h;return void 0!==a&&(a=i(a),l=p),n.call(s,l,(function(n,i){var s;switch(i.charAt(0)){case"$":return"$";case"&":return t;case"`":return e.slice(0,r);case"'":return e.slice(u);case"<":s=a[i.slice(1,-1)];break;default:var l=+i;if(0===l)return n;if(l>c){var p=f(l/10);return 0===p?n:p<=c?void 0===o[p-1]?i.charAt(1):o[p-1]+i.charAt(1):n}s=o[l-1]}return void 0===s?"":s}))}}))},function(t,e,n){"use strict";var r=n(10),i=n(35)(1);r(r.P+r.F*!n(36)([].map,!0),"Array",{map:function(t){return i(this,t,arguments[1])}})},function(t,e){t.exports=function(t){return null==t}},function(t,e,n){var r=n(190).Symbol;t.exports=r},function(t,e,n){"use strict";n.r(e);n(34);var r=n(15),i={name:"SidebarGroup",components:{DropdownTransition:n(108).a},props:["item","open","collapsable","depth"],beforeCreate:function(){this.$options.components.SidebarLinks=n(107).default},methods:{isActive:r.e}},o=(n(198),n(0)),a=Object(o.a)(i,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("section",{staticClass:"sidebar-group",class:[{collapsable:t.collapsable,"is-sub-group":0!==t.depth},"depth-"+t.depth]},[t.item.path?n("RouterLink",{staticClass:"sidebar-heading clickable",class:{open:t.open,active:t.isActive(t.$route,t.item.path)},attrs:{to:t.item.path},nativeOn:{click:function(e){return t.$emit("toggle")}}},[n("span",[t._v(t._s(t.item.title))]),t._v(" "),t.collapsable?n("span",{staticClass:"arrow",class:t.open?"down":"right"}):t._e()]):n("p",{staticClass:"sidebar-heading",class:{open:t.open},on:{click:function(e){return t.$emit("toggle")}}},[n("span",[t._v(t._s(t.item.title))]),t._v(" "),t.collapsable?n("span",{staticClass:"arrow",class:t.open?"down":"right"}):t._e()]),t._v(" "),n("DropdownTransition",[t.open||!t.collapsable?n("SidebarLinks",{staticClass:"sidebar-group-items",attrs:{items:t.item.children,"sidebar-depth":t.item.sidebarDepth,depth:t.depth+1}}):t._e()],1)],1)}),[],!1,null,null,null).exports;n(104),n(199);function s(t,e,n,r,i){var o={props:{to:e,activeClass:"",exactActiveClass:""},class:{active:r,"sidebar-link":!0}};return i>2&&(o.style={"padding-left":i+"rem"}),t("RouterLink",o,n)}function u(t,e,n,i,o){var a=arguments.length>5&&void 0!==arguments[5]?arguments[5]:1;return!e||a>o?null:t("ul",{class:"sidebar-sub-headers"},e.map((function(e){var c=Object(r.e)(i,n+"#"+e.slug);return t("li",{class:"sidebar-sub-header"},[s(t,n+"#"+e.slug,e.title,c,e.level-1),u(t,e.children,n,i,o,a+1)])})))}var c={functional:!0,props:["item","sidebarDepth"],render:function(t,e){var n=e.parent,i=n.$page,o=(n.$site,n.$route),a=n.$themeConfig,c=n.$themeLocaleConfig,l=e.props,f=l.item,p=l.sidebarDepth,h=Object(r.e)(o,f.path),d="auto"===f.type?h||f.children.some((function(t){return Object(r.e)(o,f.basePath+"#"+t.slug)})):h,v="external"===f.type?function(t,e,n){return t("a",{attrs:{href:e,target:"_blank",rel:"noopener noreferrer"},class:{"sidebar-link":!0}},[n,t("OutboundLink")])}(t,f.path,f.title||f.path):s(t,f.path,f.title||f.path,d),g=[i.frontmatter.sidebarDepth,p,c.sidebarDepth,a.sidebarDepth,1].find((function(t){return void 0!==t})),m=c.displayAllHeaders||a.displayAllHeaders;return"auto"===f.type?[v,u(t,f.children,f.basePath,o,g)]:(d||m)&&f.headers&&!r.d.test(f.path)?[v,u(t,Object(r.c)(f.headers),f.path,o,g)]:v}};n(200);function l(t,e){return"group"===e.type&&e.children.some((function(e){return"group"===e.type?l(t,e):"page"===e.type&&Object(r.e)(t,e.path)}))}var f={name:"SidebarLinks",components:{SidebarGroup:a,SidebarLink:Object(o.a)(c,void 0,void 0,!1,null,null,null).exports},props:["items","depth","sidebarDepth"],data:function(){return{openGroupIndex:0}},watch:{$route:function(){this.refreshIndex()}},created:function(){this.refreshIndex()},methods:{refreshIndex:function(){var t=function(t,e){for(var n=0;n-1&&(this.openGroupIndex=t)},toggleGroup:function(t){this.openGroupIndex=t===this.openGroupIndex?-1:t},isActive:function(t){return Object(r.e)(this.$route,t.regularPath)}}},p=Object(o.a)(f,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return t.items.length?n("ul",{staticClass:"sidebar-links"},t._l(t.items,(function(e,r){return n("li",{key:r},["group"===e.type?n("SidebarGroup",{attrs:{item:e,open:r===t.openGroupIndex,collapsable:e.collapsable||e.collapsible,depth:t.depth},on:{toggle:function(e){return t.toggleGroup(r)}}}):n("SidebarLink",{attrs:{"sidebar-depth":t.sidebarDepth,item:e}})],1)})),0):t._e()}),[],!1,null,null,null);e.default=p.exports},function(t,e,n){"use strict";var r={name:"DropdownTransition",methods:{setHeight:function(t){t.style.height=t.scrollHeight+"px"},unsetHeight:function(t){t.style.height=""}}},i=(n(182),n(0)),o=Object(i.a)(r,(function(){var t=this.$createElement;return(this._self._c||t)("transition",{attrs:{name:"dropdown"},on:{enter:this.setHeight,"after-enter":this.unsetHeight,"before-leave":this.setHeight}},[this._t("default")],2)}),[],!1,null,null,null);e.a=o.exports},function(t,e,n){"use strict";var r=n(41),i=n(110),o=n(45),a=n(29);t.exports=n(111)(Array,"Array",(function(t,e){this._t=a(t),this._i=0,this._k=e}),(function(){var t=this._t,e=this._k,n=this._i++;return!t||n>=t.length?(this._t=void 0,i(1)):i(0,"keys"==e?n:"values"==e?t[n]:[n,t[n]])}),"values"),o.Arguments=o.Array,r("keys"),r("values"),r("entries")},function(t,e){t.exports=function(t,e){return{value:e,done:!!t}}},function(t,e,n){"use strict";var r=n(74),i=n(10),o=n(25),a=n(16),s=n(45),u=n(114),c=n(80),l=n(119),f=n(11)("iterator"),p=!([].keys&&"next"in[].keys()),h=function(){return this};t.exports=function(t,e,n,d,v,g,m){u(n,e,d);var b,y,x,_=function(t){if(!p&&t in O)return O[t];switch(t){case"keys":case"values":return function(){return new n(this,t)}}return function(){return new n(this,t)}},k=e+" Iterator",S="values"==v,L=!1,O=t.prototype,w=O[f]||O["@@iterator"]||v&&O[v],C=w||_(v),$=v?S?_("entries"):C:void 0,j="Array"==e&&O.entries||w;if(j&&(x=l(j.call(new t)))!==Object.prototype&&x.next&&(c(x,k,!0),r||"function"==typeof x[f]||a(x,f,h)),S&&w&&"values"!==w.name&&(L=!0,C=function(){return w.call(this)}),r&&!m||!p&&!L&&O[f]||a(O,f,C),s[e]=C,s[k]=h,v)if(b={values:S?C:_("values"),keys:g?C:_("keys"),entries:$},m)for(y in b)y in O||o(O,y,b[y]);else i(i.P+i.F*(p||L),e,b);return b}},function(t,e,n){t.exports=n(42)("native-function-to-string",Function.toString)},function(t,e){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,e,n){"use strict";var r=n(115),i=n(44),o=n(80),a={};n(16)(a,n(11)("iterator"),(function(){return this})),t.exports=function(t,e,n){t.prototype=r(a,{next:i(1,n)}),o(t,e+" Iterator")}},function(t,e,n){var r=n(17),i=n(116),o=n(50),a=n(49)("IE_PROTO"),s=function(){},u=function(){var t,e=n(76)("iframe"),r=o.length;for(e.style.display="none",n(118).appendChild(e),e.src="javascript:",(t=e.contentWindow.document).open(),t.write(" + + diff --git a/docs/docs/examples.html b/docs/docs/examples.html index 9a1e14fdfe..bdb4aa6cfc 100644 --- a/docs/docs/examples.html +++ b/docs/docs/examples.html @@ -4,12 +4,12 @@ Examples | Caliban - - + + - - + +
- + diff --git a/docs/docs/index.html b/docs/docs/index.html index 023a44b602..7350eccd94 100644 --- a/docs/docs/index.html +++ b/docs/docs/index.html @@ -4,12 +4,12 @@ Getting Started | Caliban - - + + - - + +