├── .circleci ├── config.yml └── install-native-deps.sh ├── .git-blame-ignore-revs ├── .github ├── FUNDING.yml └── workflows │ └── compatibility.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── .sbtopts ├── .scalafix.conf ├── .scalafmt-for-test.conf ├── .scalafmt.conf ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── adapters ├── akka-http │ └── src │ │ ├── main │ │ └── scala │ │ │ └── caliban │ │ │ └── AkkaHttpAdapter.scala │ │ └── test │ │ └── scala │ │ └── caliban │ │ └── AkkaHttpAdapterSpec.scala ├── http4s │ └── src │ │ ├── main │ │ └── scala │ │ │ └── caliban │ │ │ └── Http4sAdapter.scala │ │ └── test │ │ └── scala │ │ └── caliban │ │ └── Http4sAdapterSpec.scala ├── pekko-http │ └── src │ │ ├── main │ │ └── scala │ │ │ └── caliban │ │ │ └── PekkoHttpAdapter.scala │ │ └── test │ │ └── scala │ │ └── caliban │ │ └── PekkoHttpAdapterSpec.scala ├── play │ └── src │ │ ├── main │ │ └── scala │ │ │ └── caliban │ │ │ └── PlayAdapter.scala │ │ └── test │ │ ├── resources │ │ └── application.conf │ │ └── scala │ │ └── caliban │ │ └── PlayAdapterSpec.scala ├── quick │ └── src │ │ ├── main │ │ └── scala │ │ │ └── caliban │ │ │ ├── GraphiQLHandler.scala │ │ │ ├── QuickAdapter.scala │ │ │ ├── QuickHandlers.scala │ │ │ ├── QuickRequestHandler.scala │ │ │ └── quick │ │ │ ├── WebSocketConfig.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── caliban │ │ └── QuickAdapterSpec.scala └── zio-http │ └── src │ └── main │ └── scala │ └── caliban │ └── ZHttpAdapter.scala ├── apollo-compatibility ├── Dockerfile ├── README.md ├── docker-compose.yaml ├── products.graphql └── src │ └── main │ └── scala │ ├── Main.scala │ ├── ProductSchema.scala │ ├── models │ ├── CaseStudy.scala │ ├── CaseStudyArgs.scala │ ├── Custom.scala │ ├── DeprecatedProduct.scala │ ├── DeprecatedProductArgs.scala │ ├── ID.scala │ ├── Inventory.scala │ ├── InventoryArgs.scala │ ├── MyFederation.scala │ ├── Product.scala │ ├── ProductArgs.scala │ ├── ProductDimension.scala │ ├── ProductResearch.scala │ ├── ProductResearchArgs.scala │ ├── ProductVariation.scala │ ├── QueryProductArgs.scala │ ├── User.scala │ ├── UserArgs.scala │ └── package.scala │ └── services │ ├── InventoryService.scala │ ├── ProductService.scala │ └── UserService.scala ├── benchmarks └── src │ ├── main │ └── scala │ │ └── caliban │ │ ├── ComplexQueryBenchmark.scala │ │ ├── Data.scala │ │ ├── FragmentsQueryBenchmark.scala │ │ ├── ParserBenchmark.scala │ │ ├── Schemas.scala │ │ ├── SimpleQueryBenchmark.scala │ │ ├── execution │ │ ├── NestedZQueryBenchmark.scala │ │ └── NestedZQueryBenchmarkSchema.scala │ │ ├── json │ │ └── JsonEncodingBenchmark.scala │ │ └── validation │ │ └── ValidationBenchmark.scala │ └── test │ └── scala │ └── caliban │ ├── CalibanSpec.scala │ ├── GqlSpec.scala │ ├── GrackleSpec.scala │ └── SangriaSpec.scala ├── build.sbt ├── client-laminext ├── index.html ├── package.json ├── src │ ├── main │ │ └── scala │ │ │ ├── caliban │ │ │ └── client │ │ │ │ └── laminext │ │ │ │ ├── Subscription.scala │ │ │ │ └── package.scala │ │ │ └── io │ │ │ └── laminext │ │ │ ├── fetch │ │ │ ├── NonOkayResponse.scala │ │ │ └── jsoniter │ │ │ │ ├── FetchEventStreamBuilderJsoniterOps.scala │ │ │ │ ├── FetchJsoniterSyntax.scala │ │ │ │ ├── JsonToRequestBody.scala │ │ │ │ └── package.scala │ │ │ └── websocket │ │ │ └── jsoniter │ │ │ ├── WebSocketReceiveBuilderJsoniterOps.scala │ │ │ └── package.scala │ └── test │ │ └── scala │ │ └── caliban │ │ └── client │ │ └── laminext │ │ ├── Client.scala │ │ ├── Main.scala │ │ └── Page.scala └── vite.config.js ├── client └── src │ ├── main │ └── scala │ │ └── caliban │ │ └── client │ │ ├── ArgEncoder.scala │ │ ├── Argument.scala │ │ ├── CalibanClientError.scala │ │ ├── FieldBuilder.scala │ │ ├── GraphQLRequest.scala │ │ ├── GraphQLResponse.scala │ │ ├── GraphQLResponseError.scala │ │ ├── IntrospectionClient.scala │ │ ├── Operations.scala │ │ ├── ScalarDecoder.scala │ │ ├── Selection.scala │ │ ├── SelectionBuilder.scala │ │ ├── Zippable.scala │ │ ├── __Value.scala │ │ └── ws │ │ ├── GraphQLWSRequest.scala │ │ └── GraphQLWSResponse.scala │ └── test │ └── scala │ └── caliban │ └── client │ ├── ArgEncoderSpec.scala │ ├── CalibanClientErrorSpec.scala │ ├── GraphQLResponseSpec.scala │ ├── SelectionBuilderSpec.scala │ └── TestData.scala ├── codegen-sbt └── src │ ├── main │ └── scala │ │ └── caliban │ │ └── codegen │ │ ├── CalibanCli.scala │ │ ├── CalibanKeys.scala │ │ ├── CalibanPlugin.scala │ │ ├── CalibanSettings.scala │ │ ├── CalibanSourceGenerator.scala │ │ ├── CompileTimeCalibanPlugin.scala │ │ └── OptionsParser.scala │ ├── sbt-test │ ├── codegen │ │ ├── gen-client-task │ │ │ ├── .scalafmt.conf │ │ │ ├── build.sbt │ │ │ ├── project │ │ │ │ ├── Version.scala │ │ │ │ ├── gitlab-schema.graphql │ │ │ │ ├── plugins.sbt │ │ │ │ └── schema-to-check-name-uniqueness.graphql │ │ │ ├── test │ │ │ └── verify.sh │ │ ├── test-compile-newtype │ │ │ ├── build.sbt │ │ │ ├── modules │ │ │ │ └── base │ │ │ │ │ └── src │ │ │ │ │ └── main │ │ │ │ │ └── scala │ │ │ │ │ └── graphql │ │ │ │ │ └── package.scala │ │ │ ├── project │ │ │ │ ├── Version.scala │ │ │ │ └── plugins.sbt │ │ │ ├── src │ │ │ │ └── main │ │ │ │ │ └── graphql │ │ │ │ │ └── schema.graphql │ │ │ ├── test │ │ │ └── verify.sh │ │ ├── test-compile │ │ │ ├── build.sbt │ │ │ ├── project │ │ │ │ ├── Version.scala │ │ │ │ └── plugins.sbt │ │ │ ├── src │ │ │ │ └── main │ │ │ │ │ └── graphql │ │ │ │ │ ├── genview │ │ │ │ │ └── schema.graphql │ │ │ │ │ └── schema.graphql │ │ │ ├── test │ │ │ └── verify.sh │ │ └── test-split-files-compile │ │ │ ├── build.sbt │ │ │ ├── project │ │ │ ├── Version.scala │ │ │ └── plugins.sbt │ │ │ ├── src │ │ │ └── main │ │ │ │ └── graphql │ │ │ │ └── schema.graphql │ │ │ ├── test │ │ │ └── verify.sh │ └── compiletime-codegen │ │ └── test-compile │ │ ├── README.md │ │ ├── build.sbt │ │ ├── modules │ │ ├── clients │ │ │ └── src │ │ │ │ └── main │ │ │ │ └── scala │ │ │ │ └── poc │ │ │ │ └── caliban │ │ │ │ └── client │ │ │ │ ├── PostClient.scala │ │ │ │ └── PotatoesClient.scala │ │ ├── posts-clients │ │ │ └── .gitkeep │ │ ├── posts │ │ │ └── src │ │ │ │ ├── main │ │ │ │ └── scala │ │ │ │ │ └── poc │ │ │ │ │ └── caliban │ │ │ │ │ └── posts │ │ │ │ │ ├── GraphQLApi.scala │ │ │ │ │ └── PostService.scala │ │ │ │ └── test │ │ │ │ ├── resources │ │ │ │ └── postservice.graphql │ │ │ │ └── scala │ │ │ │ └── ValidateGraphQlSpec.scala │ │ ├── potatoes-clients │ │ │ └── .gitkeep │ │ └── potatoes │ │ │ └── src │ │ │ └── main │ │ │ └── scala │ │ │ └── poc │ │ │ └── caliban │ │ │ └── potatoes │ │ │ ├── PotatoesApi.scala │ │ │ └── PotatoesService.scala │ │ ├── project │ │ ├── Version.scala │ │ ├── build.properties │ │ └── plugins.sbt │ │ └── test │ └── test │ └── scala │ └── caliban │ └── codegen │ └── OptionsParserSpec.scala ├── core └── src │ ├── main │ ├── scala-2 │ │ └── caliban │ │ │ ├── Macros.scala │ │ │ ├── Scala3Annotations.scala │ │ │ ├── introspection │ │ │ └── IntrospectionDerivation.scala │ │ │ ├── schema │ │ │ ├── AnnotationsVersionSpecific.scala │ │ │ ├── ArgBuilderDerivation.scala │ │ │ ├── DerivationUtils.scala │ │ │ ├── SchemaDerivation.scala │ │ │ ├── SchemaVersionSpecific.scala │ │ │ └── SubscriptionSchemaDerivation.scala │ │ │ └── syntax.scala │ ├── scala-3 │ │ └── caliban │ │ │ ├── Macros.scala │ │ │ ├── Scala3Annotations.scala │ │ │ ├── introspection │ │ │ └── IntrospectionDerivation.scala │ │ │ ├── schema │ │ │ ├── AnnotationsVersionSpecific.scala │ │ │ ├── ArgBuilderDerivation.scala │ │ │ ├── DerivationUtils.scala │ │ │ ├── EnumValueSchema.scala │ │ │ ├── ObjectSchema.scala │ │ │ ├── ProductFieldInfo.scala │ │ │ ├── SchemaDerivation.scala │ │ │ ├── SchemaVersionSpecific.scala │ │ │ ├── SubscriptionSchemaDerivation.scala │ │ │ ├── SumSchema.scala │ │ │ ├── TypeUnionDerivation.scala │ │ │ ├── ValueTypeSchema.scala │ │ │ └── macros │ │ │ │ └── Macros.scala │ │ │ └── syntax.scala │ └── scala │ │ └── caliban │ │ ├── CalibanError.scala │ │ ├── Configurator.scala │ │ ├── GraphQL.scala │ │ ├── GraphQLAspect.scala │ │ ├── GraphQLInterpreter.scala │ │ ├── GraphQLRequest.scala │ │ ├── GraphQLResponse.scala │ │ ├── GraphQLWSClose.scala │ │ ├── GraphQLWSInput.scala │ │ ├── GraphQLWSOutput.scala │ │ ├── HttpUtils.scala │ │ ├── Incremental.scala │ │ ├── RootResolver.scala │ │ ├── Value.scala │ │ ├── execution │ │ ├── Deferred.scala │ │ ├── ExecutionRequest.scala │ │ ├── Executor.scala │ │ ├── Feature.scala │ │ ├── Field.scala │ │ ├── FieldInfo.scala │ │ ├── Fragment.scala │ │ └── QueryExecution.scala │ │ ├── implicits.scala │ │ ├── interop │ │ ├── circe │ │ │ └── circe.scala │ │ ├── jsoniter │ │ │ └── jsoniter.scala │ │ ├── play │ │ │ └── play.scala │ │ ├── tapir │ │ │ └── tapir.scala │ │ └── zio │ │ │ └── zio.scala │ │ ├── introspection │ │ ├── Introspector.scala │ │ └── adt │ │ │ ├── __DeprecatedArgs.scala │ │ │ ├── __Directive.scala │ │ │ ├── __DirectiveLocation.scala │ │ │ ├── __EnumValue.scala │ │ │ ├── __Field.scala │ │ │ ├── __InputValue.scala │ │ │ ├── __Introspection.scala │ │ │ ├── __Schema.scala │ │ │ ├── __Type.scala │ │ │ ├── __TypeArgs.scala │ │ │ └── __TypeKind.scala │ │ ├── package.scala │ │ ├── parsing │ │ ├── Parser.scala │ │ ├── SourceMapper.scala │ │ ├── VariablesCoercer.scala │ │ ├── adt │ │ │ ├── Definition.scala │ │ │ ├── Directive.scala │ │ │ ├── Document.scala │ │ │ ├── LocationInfo.scala │ │ │ ├── OperationType.scala │ │ │ ├── Selection.scala │ │ │ ├── Type.scala │ │ │ └── VariableDefinition.scala │ │ └── parsers │ │ │ ├── NumberParsers.scala │ │ │ ├── Parsers.scala │ │ │ ├── SelectionParsers.scala │ │ │ ├── StringParsers.scala │ │ │ └── ValueParsers.scala │ │ ├── relay │ │ ├── Base64Cursor.scala │ │ ├── Connection.scala │ │ ├── Cursor.scala │ │ └── PaginationArgs.scala │ │ ├── rendering │ │ ├── DocumentRenderer.scala │ │ ├── Renderer.scala │ │ └── ValueRenderer.scala │ │ ├── schema │ │ ├── Annotations.scala │ │ ├── ArgBuilder.scala │ │ ├── ObjectFieldResolver.scala │ │ ├── Operation.scala │ │ ├── RootSchema.scala │ │ ├── RootSchemaBuilder.scala │ │ ├── RootType.scala │ │ ├── Schema.scala │ │ ├── SchemaUtils.scala │ │ ├── Step.scala │ │ ├── SubscriptionSchema.scala │ │ └── Types.scala │ │ ├── transformers │ │ └── Transformer.scala │ │ ├── uploads │ │ ├── Upload.scala │ │ └── Uploads.scala │ │ ├── validation │ │ ├── FieldMap.scala │ │ ├── FragmentValidator.scala │ │ ├── SchemaValidator.scala │ │ ├── Utils.scala │ │ ├── ValidationOps.scala │ │ ├── Validator.scala │ │ ├── ValueValidator.scala │ │ └── package.scala │ │ ├── wrappers │ │ ├── ApolloCaching.scala │ │ ├── ApolloPersistedQueries.scala │ │ ├── ApolloTracing.scala │ │ ├── Caching.scala │ │ ├── CostEstimation.scala │ │ ├── DeferSupport.scala │ │ ├── FieldMetrics.scala │ │ ├── IncrementalDelivery.scala │ │ ├── Wrapper.scala │ │ └── Wrappers.scala │ │ └── ws │ │ ├── Protocol.scala │ │ ├── WebSocketHooks.scala │ │ └── package.scala │ └── test │ ├── resources │ └── document-tests │ │ ├── kitchen-sink-compact.graphql │ │ ├── kitchen-sink-query.graphql │ │ ├── kitchen-sink.graphql │ │ └── query-compact.graphql │ ├── scala-2 │ └── caliban │ │ ├── Scala2SpecificSpec.scala │ │ ├── schema │ │ └── ArgBuilderScala2Spec.scala │ │ └── validation │ │ └── InputArgumentSpecInterop.scala │ ├── scala-3 │ └── caliban │ │ ├── Scala3SpecificSpec.scala │ │ ├── schema │ │ ├── ArgBuilderDerivesAutoSpec.scala │ │ ├── EnumFieldAutoDerivation.scala │ │ ├── ExcludedFieldSpec.scala │ │ ├── Scala3DerivesSpec.scala │ │ └── SchemaDerivesAutoSpec.scala │ │ └── validation │ │ └── InputArgumentSpecInterop.scala │ └── scala │ └── caliban │ ├── FragmentSchema.scala │ ├── RenderingSpec.scala │ ├── RenderingSpecSchema.scala │ ├── RenderingSpecSchemaDescriptions.scala │ ├── TestUtils.scala │ ├── TriState.scala │ ├── execution │ ├── DefaultValueSpec.scala │ ├── ExecutionSpec.scala │ ├── FieldArgsSpec.scala │ ├── FieldSpec.scala │ ├── FragmentSpec.scala │ ├── IncrementalDeliverySpec.scala │ └── TestDeferredSchema.scala │ ├── interop │ ├── json │ │ └── JsonAdtSpec.scala │ └── jsoniter │ │ ├── GraphQLRequestJsoniterSpec.scala │ │ ├── GraphQLResponseJsoniterSpec.scala │ │ ├── GraphQLWSInputJsoniterSpec.scala │ │ └── GraphQLWSOutputJsoniterSpec.scala │ ├── introspection │ └── IntrospectionSpec.scala │ ├── parsing │ ├── DocumentSpec.scala │ ├── ParserSpec.scala │ └── SourceMapperSpec.scala │ ├── relay │ └── ConnectionSpec.scala │ ├── schema │ ├── ArgBuilderSpec.scala │ ├── OptionalSpec.scala │ ├── RootTypeSpec.scala │ ├── SchemaDerivationIssuesSpec.scala │ ├── SchemaSpec.scala │ └── SemanticNonNullSchemaSpec.scala │ ├── transformers │ └── TransformerSpec.scala │ ├── validation │ ├── InputArgumentSpec.scala │ ├── InputObjectSpec.scala │ ├── ValidationSchemaSpec.scala │ └── ValidationSpec.scala │ └── wrappers │ ├── CachingSpec.scala │ ├── CostEstimationSpec.scala │ ├── FieldMetricsSpec.scala │ └── WrappersSpec.scala ├── docs ├── .nojekyll ├── 404.html ├── about │ └── index.html ├── assets │ ├── css │ │ └── 0.styles.b5ed6211.css │ ├── img │ │ └── search.83621669.svg │ └── js │ │ ├── 1.1cbc5082.js │ │ ├── 10.3529bcb2.js │ │ ├── 11.d1727d19.js │ │ ├── 12.eaba6033.js │ │ ├── 13.03a72e64.js │ │ ├── 14.a69b86ef.js │ │ ├── 15.1477083d.js │ │ ├── 16.9dd77b55.js │ │ ├── 17.dc3323dd.js │ │ ├── 18.648b10c4.js │ │ ├── 19.2a696a53.js │ │ ├── 2.10987e1d.js │ │ ├── 20.89f58454.js │ │ ├── 21.31e32fb9.js │ │ ├── 22.723716f0.js │ │ ├── 23.59b56e10.js │ │ ├── 24.48e23614.js │ │ ├── 25.c981091f.js │ │ ├── 26.b3b7c765.js │ │ ├── 27.d46b76c4.js │ │ ├── 28.6ac4692a.js │ │ ├── 29.4914bea3.js │ │ ├── 3.f74b7660.js │ │ ├── 30.14aff9e5.js │ │ ├── 31.346dc7bb.js │ │ ├── 32.993f3750.js │ │ ├── 33.3da117f6.js │ │ ├── 34.4cc63b90.js │ │ ├── 35.bb97df3f.js │ │ ├── 36.73fad601.js │ │ ├── 37.eb195eb7.js │ │ ├── 38.5498db05.js │ │ ├── 39.baab6426.js │ │ ├── 4.119e5097.js │ │ ├── 40.2b6878ca.js │ │ ├── 41.cbdf6ad3.js │ │ ├── 42.1d861277.js │ │ ├── 5.635a7794.js │ │ ├── 6.301ea018.js │ │ ├── 7.442dabf9.js │ │ ├── app.7185a066.js │ │ └── vendors~docsearch.d856fa52.js ├── caliban.png ├── caliban.svg ├── docs │ ├── adapters.html │ ├── client-codegen.html │ ├── client.html │ ├── examples.html │ ├── federation.html │ ├── index.html │ ├── interop.html │ ├── laminext.html │ ├── middleware.html │ ├── optimization.html │ ├── relay-connections.html │ ├── schema-comparison.html │ ├── schema-reporting.html │ ├── schema.html │ ├── server-codegen.html │ ├── stitching.html │ └── tools.html ├── faq │ └── index.html ├── index.html └── resources │ └── index.html ├── examples ├── README.md └── src │ └── main │ ├── resources │ ├── application.conf │ ├── gateway │ │ ├── gateway.js │ │ └── package.json │ └── gateway_v2 │ │ ├── gateway.js │ │ └── package.json │ └── scala │ └── example │ ├── ExampleApi.scala │ ├── ExampleData.scala │ ├── ExampleService.scala │ ├── akkahttp │ ├── AuthExampleApp.scala │ └── ExampleApp.scala │ ├── calibantotapir │ ├── MainApp.scala │ ├── README.md │ ├── graphql │ │ └── Book.scala │ └── tapir │ │ └── SampleRestEndpoint.scala │ ├── client │ ├── Client.scala │ └── ExampleApp.scala │ ├── federation │ ├── CharacterService.scala │ ├── EpisodeService.scala │ ├── FederatedApi.scala │ ├── FederatedApp.scala │ ├── FederationData.scala │ └── v2 │ │ ├── CharacterService.scala │ │ ├── EpisodeService.scala │ │ ├── FederatedApi.scala │ │ ├── FederatedApp.scala │ │ └── FederationData.scala │ ├── http4s │ ├── AuthExampleApp.scala │ ├── AuthExampleAppF.scala │ ├── ExampleApp.scala │ └── ExampleAppF.scala │ ├── interop │ ├── cats │ │ ├── ContextualCatsInterop.scala │ │ ├── ContextualCatsInteropIO.scala │ │ └── ExampleCatsInterop.scala │ └── monix │ │ └── ExampleMonixInterop.scala │ ├── optimizations │ ├── CommonData.scala │ ├── NaiveTest.scala │ └── OptimizedTest.scala │ ├── pekkohttp │ ├── AuthExampleApp.scala │ └── ExampleApp.scala │ ├── play │ ├── AuthExampleApp.scala │ └── ExampleApp.scala │ ├── quick │ ├── AuthExampleApp.scala │ └── ExampleApp.scala │ ├── stitching │ └── ExampleApp.scala │ └── tapirtocaliban │ ├── Endpoints.scala │ ├── ExampleApp.scala │ └── README.md ├── federation └── src │ ├── main │ ├── protobuf │ │ └── reports.proto │ └── scala │ │ └── caliban │ │ └── federation │ │ ├── EntityResolver.scala │ │ ├── FederationDirectives.scala │ │ ├── FederationHelpers.scala │ │ ├── FederationSupport.scala │ │ ├── FederationV1.scala │ │ ├── package.scala │ │ ├── tracing │ │ └── ApolloFederatedTracing.scala │ │ └── v2x │ │ ├── FederationDirectivesV2.scala │ │ ├── FederationDirectivesV2_10.scala │ │ ├── FederationDirectivesV2_3.scala │ │ ├── FederationDirectivesV2_5.scala │ │ ├── FederationDirectivesV2_6.scala │ │ ├── FederationDirectivesV2_8.scala │ │ ├── FederationDirectivesV2_9.scala │ │ ├── FederationV2.scala │ │ ├── Link.scala │ │ └── Versions.scala │ └── test │ └── scala │ └── caliban │ └── federation │ ├── FederationV1Spec.scala │ ├── tracing │ └── FederationTracingSpec.scala │ └── v2x │ └── FederationV2Spec.scala ├── interop ├── cats │ └── src │ │ ├── main │ │ └── scala │ │ │ └── caliban │ │ │ └── interop │ │ │ ├── cats │ │ │ ├── CatsInterop.scala │ │ │ ├── FromEffect.scala │ │ │ ├── InjectEnv.scala │ │ │ ├── ToEffect.scala │ │ │ └── implicits │ │ │ │ └── package.scala │ │ │ └── fs2 │ │ │ ├── Fs2Interop.scala │ │ │ └── implicits │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── caliban │ │ └── interop │ │ ├── cats │ │ └── CatsInteropSpec.scala │ │ └── fs2 │ │ └── Fs2InteropSchemaSpec.scala ├── monix │ └── src │ │ └── main │ │ └── scala │ │ └── caliban │ │ └── interop │ │ └── monix │ │ ├── MonixInterop.scala │ │ └── implicits │ │ └── package.scala └── tapir │ └── src │ ├── main │ └── scala │ │ └── caliban │ │ └── interop │ │ └── tapir │ │ ├── HttpInterpreter.scala │ │ ├── HttpUploadInterpreter.scala │ │ ├── JsonCodecs.scala │ │ ├── MaxCharBufSizeJsonJsoniter.scala │ │ ├── StreamConstructor.scala │ │ ├── TapirAdapter.scala │ │ ├── WebSocketHooks.scala │ │ ├── WebSocketInterpreter.scala │ │ └── package.scala │ └── test │ └── scala │ └── caliban │ └── interop │ └── tapir │ ├── FakeAuthorizationInterceptor.scala │ ├── TapirAdapterSpec.scala │ ├── TapirSpec.scala │ ├── TestApi.scala │ ├── TestData.scala │ └── TestService.scala ├── macros └── src │ └── main │ └── scala-2 │ └── caliban │ └── schema │ ├── Derived.scala │ └── DerivedMagnolia.scala ├── project ├── ConsoleHelper.scala ├── Scala3TestHelper.scala ├── ScriptedDependency.scala ├── build.properties └── plugins.sbt ├── reporting └── src │ ├── main │ └── scala │ │ └── caliban │ │ └── reporting │ │ ├── ReportingDaemon.scala │ │ ├── SchemaReporter.scala │ │ ├── SchemaReportingRef.scala │ │ ├── Util.scala │ │ ├── client │ │ ├── Mutation.scala │ │ ├── ReportSchemaError.scala │ │ ├── ReportSchemaErrorCode.scala │ │ ├── ReportSchemaResponse.scala │ │ ├── ReportSchemaResult.scala │ │ ├── SchemaReport.scala │ │ └── package.scala │ │ └── package.scala │ └── test │ └── scala │ └── caliban │ └── reporting │ └── ReportingDaemonSpec.scala ├── tools └── src │ ├── main │ ├── resources │ │ └── default.scalafmt.conf │ └── scala │ │ └── caliban │ │ └── tools │ │ ├── CalibanCommonSettings.scala │ │ ├── ClientWriter.scala │ │ ├── Codegen.scala │ │ ├── Formatter.scala │ │ ├── IntrospectionClient.scala │ │ ├── Options.scala │ │ ├── RemoteSchema.scala │ │ ├── SchemaComparison.scala │ │ ├── SchemaComparisonChange.scala │ │ ├── SchemaLoader.scala │ │ ├── SchemaWriter.scala │ │ ├── compiletime │ │ ├── CompileTime.scala │ │ ├── Config.scala │ │ └── Utils.scala │ │ ├── package.scala │ │ └── stitching │ │ ├── PartialRemoteSchema.scala │ │ ├── RemoteQuery.scala │ │ ├── RemoteResolver.scala │ │ ├── RemoteSchemaResolver.scala │ │ ├── ResolveRequest.scala │ │ └── package.scala │ └── test │ ├── resources │ └── snapshots │ │ ├── ClientWriterSpec │ │ ├── Option types dont conflict with scala.Option.scala │ │ ├── add scalar mappings and additional imports.scala │ │ ├── case-insensitive name uniqueness in 2 basic objects.scala │ │ ├── case-sensitive name uniqueness in enums values.scala │ │ ├── default arguments for optional and list arguments.scala │ │ ├── deprecated field + comment newline.scala │ │ ├── deprecated field + comment.scala │ │ ├── deprecated field argument + comment newline.scala │ │ ├── deprecated field argument + comment.scala │ │ ├── enum with exclude deprecated, only deprecated values.scala │ │ ├── enum with exclude deprecated.scala │ │ ├── enum.scala │ │ ├── extensible enum.scala │ │ ├── input object oneOf.scala │ │ ├── input object with deprecated fields.scala │ │ ├── input object with reserved name.scala │ │ ├── input object.scala │ │ ├── interface with implements.scala │ │ ├── interface with list.scala │ │ ├── interface without implements.scala │ │ ├── interface.scala │ │ ├── nested object type.scala │ │ ├── object type with arguments.scala │ │ ├── object type with reserved name.scala │ │ ├── safe names with underscores.scala │ │ ├── scalar mapped enum.scala │ │ ├── schema with splitFiles.scala │ │ ├── schema.scala │ │ ├── simple object type with exclude deprecated and genView, only deprecated fields.scala │ │ ├── simple object type with exclude deprecated and genView.scala │ │ ├── simple object type.scala │ │ ├── support for Json scalar.scala │ │ └── union.scala │ │ ├── ClientWriterViewSpec │ │ ├── generic view for capital fields.scala │ │ ├── generic view for scala keywords.scala │ │ ├── generic view for scala.Option[List[scala.Option[A]] types.scala │ │ ├── nested object type.scala │ │ ├── recursive object type.scala │ │ ├── root schema optional interface.scala │ │ ├── simple object type.scala │ │ ├── type with more than 22 fields _ function args _ selection args.scala │ │ └── union case.scala │ │ └── SchemaWriterSpec │ │ ├── (empty schema test).scala │ │ ├── GQLDeprecated with multiline reason and escaped quotes.scala │ │ ├── GQLDeprecated with reason.scala │ │ ├── GQLDescription with escaped quotes.scala │ │ ├── add derives also to traits for @oneOf input types.scala │ │ ├── add derives.scala │ │ ├── add scalar mappings and additional imports.scala │ │ ├── args names root level.scala │ │ ├── args unique class names.scala │ │ ├── enum type.scala │ │ ├── final case class reserved field name used.scala │ │ ├── generate nested @lazy fields with abstracted effect type.scala │ │ ├── generate typesafe ids with @newtype directive, for fields on types, input types and arguments.scala │ │ ├── inherits field with args.scala │ │ ├── input type oneOf.scala │ │ ├── input type with preserved input.scala │ │ ├── input type.scala │ │ ├── interface type.scala │ │ ├── interface, abstracted effect and lazy combination.scala │ │ ├── recognize @lazy intention and generate side-effecting field with abstracted effect type.scala │ │ ├── recognize @lazy intention and generate side-effecting field.scala │ │ ├── scala reserved word used.scala │ │ ├── schema test.scala │ │ ├── schema.scala │ │ ├── simple mutation with abstracted effect type.scala │ │ ├── simple mutation.scala │ │ ├── simple queries with abstracted effect type.scala │ │ ├── simple queries.scala │ │ ├── simple subscription.scala │ │ ├── type appears in type union and implements interface.scala │ │ ├── type with field parameter.scala │ │ ├── union type.scala │ │ └── union, abstracted effect type and lazy combination.scala │ └── scala │ └── caliban │ └── tools │ ├── ClientWriterSpec.scala │ ├── ClientWriterViewSpec.scala │ ├── CodegenSpec.scala │ ├── IntrospectionClientSpec.scala │ ├── RemoteSchemaSpec.scala │ ├── SchemaComparisonSpec.scala │ ├── SchemaWriterSpec.scala │ ├── SnapshotTest.scala │ ├── compiletime │ ├── ConfigSpec.scala │ └── UtilsSpec.scala │ └── stitching │ └── RemoteQuerySpec.scala ├── tracing └── src │ ├── main │ └── scala │ │ └── caliban │ │ └── tracing │ │ ├── FieldTracer.scala │ │ ├── SchemaTracer.scala │ │ └── TracingWrapper.scala │ └── test │ └── scala │ └── caliban │ └── tracing │ ├── MockTracer.scala │ └── TracingWrapperSpec.scala └── vuepress ├── docs ├── .vuepress │ ├── config.js │ ├── public │ │ ├── .nojekyll │ │ ├── caliban.png │ │ └── caliban.svg │ └── styles │ │ └── index.styl ├── README.md ├── about │ └── README.md ├── docs │ ├── README.md │ ├── adapters.md │ ├── client-codegen.md │ ├── client.md │ ├── examples.md │ ├── federation.md │ ├── interop.md │ ├── laminext.md │ ├── middleware.md │ ├── optimization.md │ ├── relay-connections.md │ ├── schema-comparison.md │ ├── schema-reporting.md │ ├── schema.md │ ├── server-codegen.md │ ├── stitching.md │ └── tools.md ├── faq │ └── README.md └── resources │ └── README.md └── package.json /.circleci/install-native-deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo apt-get update 4 | 5 | sudo apt-get install -y \ 6 | clang \ 7 | libstdc++-12-dev \ 8 | libgc-dev \ 9 | libuv1-dev \ 10 | openssl 11 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Scala Steward: Reformat with scalafmt 3.8.2 2 | 31f697d44316b1023952d5ab817d770d05f2fa76 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [ghostdogpr, kyri-petrou, paulpdaniels] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .idea/ 3 | .bsp/ 4 | # vim 5 | *.sw? 6 | 7 | # Ignore [ce]tags files 8 | tags 9 | 10 | .bloop 11 | .metals 12 | .vscode 13 | project/metals.sbt 14 | *.class 15 | *.log 16 | 17 | vuepress/node_modules/* 18 | *.lock 19 | vuepress/package-lock.json 20 | 21 | package-lock.json 22 | node_modules/ 23 | .DS_Store 24 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: local 3 | hooks: 4 | - id: scalafmt 5 | name: scalafmt fix formatting 6 | entry: scalafmt 7 | args: [ --exclude=target, --mode=changed, --reportError ] 8 | language: system 9 | stages: [ commit, push ] 10 | always_run: true 11 | pass_filenames: false 12 | minimum_pre_commit_version: '2.8.0' 13 | -------------------------------------------------------------------------------- /.sbtopts: -------------------------------------------------------------------------------- 1 | -J-XX:MaxMetaspaceSize=3G 2 | -J-Xmx3G 3 | -J-XX:+UseG1GC 4 | -------------------------------------------------------------------------------- /.scalafix.conf: -------------------------------------------------------------------------------- 1 | OrganizeImports.preset = INTELLIJ_2020_3 2 | -------------------------------------------------------------------------------- /.scalafmt-for-test.conf: -------------------------------------------------------------------------------- 1 | version = "3.1.2" 2 | 3 | runner.dialect = scala3 4 | maxColumn = 120 5 | align.preset = most 6 | continuationIndent.defnSite = 2 7 | assumeStandardLibraryStripMargin = true 8 | docstrings.style = Asterisk 9 | docstrings.wrap = no 10 | lineEndings = preserve 11 | includeCurlyBraceInSelectChains = false 12 | danglingParentheses.preset = true 13 | spaces { 14 | inImportCurlyBraces = true 15 | } 16 | optIn.annotationNewlines = true 17 | 18 | rewrite.rules = [SortImports, RedundantBraces] 19 | 20 | fileOverride { 21 | "glob:**/scala-3/**" { 22 | runner.dialect = scala3 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = "3.8.2" 2 | runner.dialect = scala213 3 | maxColumn = 120 4 | align.preset = most 5 | continuationIndent.defnSite = 2 6 | assumeStandardLibraryStripMargin = true 7 | docstrings.style = Asterisk 8 | docstrings.wrap = no 9 | lineEndings = preserve 10 | includeCurlyBraceInSelectChains = false 11 | danglingParentheses.preset = true 12 | spaces { 13 | inImportCurlyBraces = true 14 | } 15 | optIn.annotationNewlines = true 16 | 17 | rewrite.rules = [SortImports, RedundantBraces] 18 | 19 | fileOverride { 20 | "glob:**/scala-3/**" { 21 | runner.dialect = scala3 22 | } 23 | } 24 | 25 | project.excludePaths = [ 26 | "glob:**/target/**" 27 | "glob:**/resources/**" 28 | ] -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | We are committed to providing a friendly, safe and welcoming 4 | environment for all, regardless of level of experience, gender, gender 5 | identity and expression, sexual orientation, disability, personal 6 | appearance, body size, race, ethnicity, age, religion, nationality, or 7 | other such characteristics. 8 | 9 | Everyone is expected to follow the [Scala Code of Conduct] when 10 | discussing the project on the available communication channels. If you 11 | are being harassed, please contact us immediately so that we can 12 | support you. 13 | 14 | ## Moderation 15 | 16 | For any questions, concerns, or moderation requests please contact a 17 | member of the project. 18 | 19 | - [Pierre Ricadat](mailto:ghostdogpr@gmail.com) 20 | 21 | [Scala Code of Conduct]: https://www.scala-lang.org/conduct/ 22 | -------------------------------------------------------------------------------- /adapters/play/src/test/resources/application.conf: -------------------------------------------------------------------------------- 1 | http.server.max-content-length = infinite 2 | play.http.parser.maxMemoryBuffer = 100M 3 | -------------------------------------------------------------------------------- /adapters/quick/src/main/scala/caliban/QuickHandlers.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import zio.Trace 4 | import zio.http.{ HandlerAspect, RequestHandler } 5 | import zio.stacktracer.TracingImplicits.disableAutoTrace 6 | 7 | final case class QuickHandlers[-R]( 8 | api: RequestHandler[R, Nothing], 9 | upload: RequestHandler[R, Nothing], 10 | webSocket: RequestHandler[R, Nothing] 11 | ) { 12 | 13 | /** 14 | * Applies a ZIO HTTP `HandlerAspect` to both the api and upload handlers 15 | */ 16 | def @@[R1 <: R](aspect: HandlerAspect[R1, Unit]): QuickHandlers[R1] = { 17 | implicit val trace: Trace = Trace.empty 18 | QuickHandlers( 19 | api = (api @@ aspect).merge, 20 | upload = (upload @@ aspect).merge, 21 | webSocket = (webSocket @@ aspect).merge 22 | ) 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /adapters/quick/src/main/scala/caliban/quick/WebSocketConfig.scala: -------------------------------------------------------------------------------- 1 | package caliban.quick 2 | 3 | import caliban.ws.WebSocketHooks 4 | import zio._ 5 | import zio.http.{ WebSocketConfig => ZWebSocketConfig } 6 | import zio.stacktracer.TracingImplicits.disableAutoTrace 7 | 8 | case class WebSocketConfig[-R]( 9 | keepAliveTime: Option[Duration], 10 | hooks: WebSocketHooks[R, Any], 11 | zHttpConfig: ZWebSocketConfig 12 | ) { 13 | def withHooks[R1](newHooks: WebSocketHooks[R1, Any]): WebSocketConfig[R & R1] = 14 | copy(hooks = hooks ++ newHooks) 15 | 16 | def withKeepAliveTime(time: Duration): WebSocketConfig[R] = 17 | copy(keepAliveTime = Some(time)) 18 | 19 | def withZHttpConfig(newConfig: ZWebSocketConfig): WebSocketConfig[R] = 20 | copy(zHttpConfig = newConfig) 21 | } 22 | 23 | object WebSocketConfig { 24 | def default: WebSocketConfig[Any] = WebSocketConfig(None, WebSocketHooks.empty, ZWebSocketConfig.default) 25 | } 26 | -------------------------------------------------------------------------------- /adapters/zio-http/src/main/scala/caliban/ZHttpAdapter.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import caliban.Configurator.ExecutionConfiguration 4 | import caliban.ws.WebSocketHooks 5 | import zio.Duration 6 | import zio.http.{ WebSocketConfig => ZWebSocketConfig, _ } 7 | 8 | @deprecated( 9 | "The `caliban-zio-http` package is deprecated and scheduled to be removed in a future release. To use Caliban with zio-http, use the `caliban-quick` module instead", 10 | "2.6.0" 11 | ) 12 | object ZHttpAdapter { 13 | 14 | def makeHttpService[R, E]( 15 | interpreter: GraphQLInterpreter[R, E], 16 | config: ExecutionConfiguration = ExecutionConfiguration() 17 | ): RequestHandler[R, Nothing] = 18 | QuickAdapter(interpreter).configure(config).handlers.api 19 | 20 | def makeWebSocketService[R, E]( 21 | interpreter: GraphQLInterpreter[R, E], 22 | keepAliveTime: Option[Duration] = None, 23 | webSocketHooks: WebSocketHooks[R, E] = WebSocketHooks.empty, 24 | zHttpConfig: ZWebSocketConfig = ZWebSocketConfig.default 25 | ): RequestHandler[R, Nothing] = { 26 | val config = quick.WebSocketConfig(keepAliveTime, webSocketHooks, zHttpConfig) 27 | QuickAdapter(interpreter).configureWebSocket(config).handlers.webSocket 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /apollo-compatibility/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM sbtscala/scala-sbt:graalvm-ce-22.3.3-b1-java17_1.9.8_2.13.12 AS build 2 | 3 | WORKDIR /app 4 | COPY build.sbt . 5 | COPY apollo-compatibility/target/apollo-subgraph-compatibility.jar /app/artifact.jar 6 | EXPOSE 4001 7 | CMD java $* -jar artifact.jar -------------------------------------------------------------------------------- /apollo-compatibility/README.md: -------------------------------------------------------------------------------- 1 | # Federated subgraph to test apollo federation spec compatibility 2 | 3 | Implementation of a federated subgraph aligned to the requirements outlined in [apollo-federation-subgraph-compatibility](https://github.com/apollographql/apollo-federation-subgraph-compatibility). 4 | 5 | The subgraph can be used to verify compatibility against [Apollo Federation Subgraph Specification](https://www.apollographql.com/docs/federation/subgraph-spec). 6 | 7 | ### Run compatibility tests 8 | Execute the following command from the root of the repo 9 | 10 | ``` 11 | npx @apollo/federation-subgraph-compatibility docker --compose apollo-compatibility/docker-compose.yml --schema apollo-compatibility/schema.graphql 12 | ``` 13 | 14 | ### Printing the GraphQL Schema (SDL) 15 | 16 | ``` 17 | sbt "apollo-compatibility/run printSchema" 18 | ``` -------------------------------------------------------------------------------- /apollo-compatibility/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | products: 3 | build: 4 | context: . 5 | dockerfile: ./apollo-compatibility/Dockerfile 6 | ports: 7 | - 4001:4001 -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/Main.scala: -------------------------------------------------------------------------------- 1 | import caliban.CalibanError 2 | import zio._ 3 | import caliban.quick._ 4 | import services.{ InventoryService, ProductService, UserService } 5 | import zio.http.{ Response, Routes, Server } 6 | 7 | object Main extends ZIOAppDefault { 8 | 9 | def run = for { 10 | args <- ZIOAppArgs.getArgs 11 | _ <- (args match { 12 | case Chunk("printSchema") => printSchema 13 | case _ => runServer 14 | }) 15 | } yield () 16 | 17 | private val printSchema = Console.printLine(ProductSchema.print) 18 | 19 | private val runServer = { 20 | val routes: Task[Routes[ProductService with UserService with InventoryService, Response]] = 21 | ProductSchema.api.routes("/graphql") 22 | 23 | val server: ZIO[ProductService with UserService with InventoryService with Server, Throwable, Response] = 24 | routes.flatMap(Server.serve(_)) 25 | 26 | server.orDie 27 | .provide( 28 | Server.defaultWithPort(4001), 29 | ProductService.inMemory, 30 | UserService.inMemory, 31 | InventoryService.inMemory 32 | ) 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/CaseStudy.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.schema.Schema 4 | 5 | case class CaseStudy( 6 | caseNumber: ID, 7 | description: Option[String] 8 | ) 9 | 10 | object CaseStudy { 11 | implicit val schema: Schema[Any, CaseStudy] = Schema.gen 12 | } 13 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/CaseStudyArgs.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.schema.{ ArgBuilder, Schema } 4 | 5 | case class CaseStudyArgs(caseNumber: ID) 6 | 7 | object CaseStudyArgs { 8 | implicit val schema: Schema[Any, CaseStudyArgs] = Schema.gen 9 | implicit val argBuilder: ArgBuilder[CaseStudyArgs] = ArgBuilder.gen 10 | } 11 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/Custom.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.parsing.adt.Directive 4 | import caliban.schema.Annotations.GQLDirective 5 | 6 | case class Custom() extends GQLDirective(Directive("custom")) 7 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/DeprecatedProduct.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.schema.{ GenericSchema, Schema } 4 | import services.UserService 5 | import zio.URIO 6 | 7 | @GQLKey("sku package") 8 | case class DeprecatedProduct( 9 | sku: String, 10 | `package`: String, 11 | reason: Option[String], 12 | createdBy: URIO[UserService, Option[User]] 13 | ) 14 | 15 | object DeprecatedProduct { 16 | object apiSchema extends GenericSchema[UserService] 17 | 18 | implicit val schema: Schema[UserService, DeprecatedProduct] = apiSchema.gen[UserService, DeprecatedProduct] 19 | } 20 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/DeprecatedProductArgs.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.schema.{ ArgBuilder, Schema } 4 | 5 | case class DeprecatedProductArgs( 6 | sku: String, 7 | `package`: String 8 | ) 9 | 10 | object DeprecatedProductArgs { 11 | implicit val schema: Schema[Any, DeprecatedProductArgs] = Schema.gen 12 | implicit val argBuilder: ArgBuilder[DeprecatedProductArgs] = ArgBuilder.gen[DeprecatedProductArgs] 13 | } 14 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/ID.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.schema.{ ArgBuilder, Schema } 4 | import caliban.Value.StringValue 5 | 6 | case class ID(id: String) extends AnyVal 7 | 8 | object ID { 9 | implicit val schema: Schema[Any, ID] = Schema.scalarSchema[ID]("ID", None, None, None, id => StringValue(id.id)) 10 | implicit val argBuilder: ArgBuilder[ID] = ArgBuilder.string.map(ID(_)) 11 | } 12 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/Inventory.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.schema.{ GenericSchema, Schema } 4 | import services.{ InventoryService, UserService } 5 | 6 | @GQLInterfaceObject 7 | @GQLKey("email") 8 | case class Inventory( 9 | id: ID, 10 | deprecatedProducts: List[DeprecatedProduct] 11 | ) 12 | 13 | object Inventory { 14 | object genSchema extends GenericSchema[InventoryService with UserService] 15 | implicit val schema: Schema[InventoryService with UserService, Inventory] = genSchema.gen 16 | } 17 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/InventoryArgs.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.schema.{ ArgBuilder, Schema } 4 | 5 | case class InventoryArgs(id: ID) 6 | 7 | object InventoryArgs { 8 | implicit val schema: Schema[Any, InventoryArgs] = Schema.gen 9 | implicit val argBuilder: ArgBuilder[InventoryArgs] = ArgBuilder.gen 10 | } 11 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/MyFederation.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.federation.v2x._ 4 | 5 | abstract class MyFederation 6 | extends caliban.federation.v2x.FederationV2( 7 | Versions.v2_3 :: Link( 8 | "https://myspecs.dev/myCustomDirective/v1.0", 9 | List( 10 | Import("@custom") 11 | ) 12 | ) :: ComposeDirective("@custom") :: Nil 13 | ) 14 | with FederationDirectivesV2_3 15 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/Product.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.schema.Schema 4 | import zio.UIO 5 | 6 | @GQLKey("id") 7 | @GQLKey("sku package") 8 | @GQLKey("sku variation { id }") 9 | @Custom 10 | case class Product( 11 | id: ID, 12 | sku: Option[String], 13 | `package`: Option[String], 14 | variation: Option[ProductVariation], 15 | dimensions: Option[ProductDimension], 16 | @GQLProvides("totalProductsCreated") createdBy: UIO[Option[User]], 17 | @GQLTag("internal") notes: Option[String], 18 | research: List[ProductResearch] 19 | ) 20 | 21 | object Product { 22 | 23 | implicit val schema: Schema[Any, Product] = Schema.gen 24 | 25 | } 26 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/ProductArgs.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.InputValue 4 | import caliban.schema.{ ArgBuilder, Schema } 5 | 6 | sealed trait ProductArgs 7 | 8 | object ProductArgs { 9 | case class IdOnly(id: ID) extends ProductArgs 10 | case class SkuAndPackage(sku: String, `package`: String) extends ProductArgs 11 | case class SkuAndVariationId(sku: String, variation: ProductVariation) extends ProductArgs 12 | 13 | private implicit val variationArgs: ArgBuilder[ProductVariation] = ArgBuilder.gen[ProductVariation] 14 | val idOnlyArgBuilder: ArgBuilder[IdOnly] = ArgBuilder.gen[IdOnly] 15 | val skuAndPackageArgBuilder: ArgBuilder[SkuAndPackage] = ArgBuilder.gen[SkuAndPackage] 16 | val skuAndVariationIdArgBuilder: ArgBuilder[SkuAndVariationId] = ArgBuilder.gen[SkuAndVariationId] 17 | 18 | implicit val argBuilder: ArgBuilder[ProductArgs] = (input: InputValue) => 19 | (for { 20 | error <- skuAndVariationIdArgBuilder.build(input).swap 21 | _ <- skuAndPackageArgBuilder.build(input).swap 22 | _ <- idOnlyArgBuilder.build(input).swap 23 | } yield error).swap 24 | 25 | implicit val idOnlySchema: Schema[Any, ProductArgs.IdOnly] = Schema.gen 26 | implicit val skuAndPackageSchema: Schema[Any, ProductArgs.SkuAndPackage] = Schema.gen 27 | implicit val skuAndVariationIdSchema: Schema[Any, ProductArgs.SkuAndVariationId] = Schema.gen 28 | 29 | } 30 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/ProductDimension.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.schema.Schema 4 | 5 | @GQLShareable 6 | case class ProductDimension( 7 | size: Option[String], 8 | weight: Option[Float], 9 | @GQLInaccessible unit: Option[String] 10 | ) 11 | 12 | object ProductDimension { 13 | implicit val schema: Schema[Any, ProductDimension] = Schema.gen 14 | } 15 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/ProductResearch.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.schema.Schema 4 | 5 | @GQLKey("study { caseNumber }") 6 | case class ProductResearch( 7 | study: CaseStudy, 8 | outcome: Option[String] 9 | ) 10 | 11 | object ProductResearch { 12 | implicit val schema: Schema[Any, ProductResearch] = Schema.gen 13 | } 14 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/ProductResearchArgs.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.schema.{ ArgBuilder, Schema } 4 | 5 | case class ProductResearchArgs(study: CaseStudyArgs) 6 | 7 | object ProductResearchArgs { 8 | implicit val schema: Schema[Any, ProductResearchArgs] = Schema.gen 9 | implicit val argBuilder: ArgBuilder[ProductResearchArgs] = ArgBuilder.gen 10 | } 11 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/ProductVariation.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.schema.Schema 4 | 5 | case class ProductVariation( 6 | id: ID 7 | ) 8 | 9 | object ProductVariation { 10 | implicit val schema: Schema[Any, ProductVariation] = Schema.gen 11 | } 12 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/QueryProductArgs.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.schema.{ ArgBuilder, Schema } 4 | 5 | case class QueryProductArgs(id: ID) 6 | 7 | object QueryProductArgs { 8 | implicit val argBuilder: ArgBuilder[QueryProductArgs] = ArgBuilder.gen 9 | implicit val schema: Schema[Any, QueryProductArgs] = Schema.gen 10 | } 11 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/User.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.schema.Schema 4 | 5 | @GQLKey("email") 6 | @GQLExtend 7 | case class User( 8 | @GQLExternal email: ID, 9 | @GQLExternal totalProductsCreated: Option[Int], 10 | @GQLOverride("users") name: Option[String], 11 | @GQLRequires("totalProductsCreated yearsOfEmployment") averageProductsCreatedPerYear: Option[Int], 12 | @GQLExternal yearsOfEmployment: Int 13 | ) 14 | 15 | object User { 16 | implicit val schema: Schema[Any, User] = Schema.gen 17 | } 18 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/UserArgs.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import caliban.schema.{ ArgBuilder, Schema } 4 | 5 | case class UserArgs(email: ID) 6 | 7 | object UserArgs { 8 | implicit val schema: Schema[Any, UserArgs] = Schema.gen 9 | implicit val argBuilder: ArgBuilder[UserArgs] = ArgBuilder.gen 10 | } 11 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/models/package.scala: -------------------------------------------------------------------------------- 1 | package object models extends MyFederation 2 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/services/InventoryService.scala: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import models.{ DeprecatedProduct, ID, Inventory } 4 | import zio.{ ULayer, ZIO, ZLayer } 5 | 6 | trait InventoryService { 7 | 8 | def getById(id: String): Option[Inventory] 9 | 10 | } 11 | 12 | object InventoryService { 13 | val inventory = List( 14 | Inventory( 15 | id = ID("apollo-oss"), 16 | deprecatedProducts = List( 17 | DeprecatedProduct( 18 | sku = "apollo-federation-v1", 19 | `package` = "@apollo/federation-v1", 20 | reason = Some("Migrate to Federation V2"), 21 | createdBy = ZIO.serviceWithZIO[UserService](_.getUser) 22 | ) 23 | ) 24 | ) 25 | ) 26 | 27 | val inMemory: ULayer[InventoryService] = ZLayer.succeed(new InventoryService { 28 | def getById(id: String): Option[Inventory] = inventory.find(_.id.id == id) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /apollo-compatibility/src/main/scala/services/UserService.scala: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import models.{ ID, User } 4 | import zio.{ UIO, ULayer, ZIO, ZLayer } 5 | 6 | trait UserService { 7 | 8 | def getUser: UIO[Option[User]] 9 | } 10 | 11 | object UserService { 12 | private val theUser = User( 13 | averageProductsCreatedPerYear = Some(1337 / 10), 14 | email = ID("support@apollographql.com"), 15 | name = Some("Jane Smith"), 16 | totalProductsCreated = Some(1337), 17 | yearsOfEmployment = 10 18 | ) 19 | 20 | val inMemory: ULayer[UserService] = ZLayer.succeed(new UserService { 21 | def getUser: zio.UIO[Option[User]] = ZIO.some(theUser) 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /benchmarks/src/main/scala/caliban/Data.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | object Data { 4 | sealed trait Origin 5 | 6 | object Origin { 7 | case object EARTH extends Origin 8 | case object MARS extends Origin 9 | case object BELT extends Origin 10 | } 11 | 12 | sealed trait Role 13 | 14 | object Role { 15 | case class Captain(shipName: String) extends Role 16 | case class Pilot(shipName: String) extends Role 17 | case class Engineer(shipName: String) extends Role 18 | case class Mechanic(shipName: String) extends Role 19 | } 20 | 21 | case class Character(name: String, nicknames: List[String], origin: Origin, role: Option[Role]) 22 | 23 | val characters = List( 24 | Character("James Holden", List("Jim", "Hoss"), Origin.EARTH, Some(Role.Captain("Rocinante"))), 25 | Character("Naomi Nagata", Nil, Origin.BELT, Some(Role.Engineer("Rocinante"))), 26 | Character("Amos Burton", Nil, Origin.EARTH, Some(Role.Mechanic("Rocinante"))), 27 | Character("Alex Kamal", Nil, Origin.MARS, Some(Role.Pilot("Rocinante"))), 28 | Character("Chrisjen Avasarala", Nil, Origin.EARTH, None), 29 | Character("Josephus Miller", List("Joe"), Origin.BELT, None), 30 | Character("Roberta Draper", List("Bobbie", "Gunny"), Origin.MARS, None) 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /benchmarks/src/main/scala/caliban/ParserBenchmark.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import caliban.parsing.Parser 4 | import caliban.parsing.adt.Document 5 | import cats.data.NonEmptyList 6 | import cats.parse.Caret 7 | import gql.parser.QueryAst 8 | import grackle.Operation 9 | import org.openjdk.jmh.annotations._ 10 | import sangria.parser.QueryParser 11 | 12 | import java.util.concurrent.TimeUnit 13 | import scala.concurrent.ExecutionContextExecutor 14 | 15 | @State(Scope.Thread) 16 | @BenchmarkMode(Array(Mode.Throughput)) 17 | @OutputTimeUnit(TimeUnit.SECONDS) 18 | @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 19 | @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 20 | @Fork(1) 21 | class ParserBenchmark { 22 | import ComplexQueryBenchmark._ 23 | 24 | implicit val executionContext: ExecutionContextExecutor = scala.concurrent.ExecutionContext.global 25 | 26 | @Benchmark 27 | def runCaliban(): Document = 28 | Parser.parseQuery(fullIntrospectionQuery).fold(throw _, identity) 29 | 30 | @Benchmark 31 | def runSangria(): sangria.ast.Document = 32 | QueryParser.parse(fullIntrospectionQuery).fold(throw _, identity) 33 | 34 | @Benchmark 35 | def runGrackle(): Operation = 36 | Grackle.compiler.compile(fullIntrospectionQuery).getOrElse(throw new Throwable("Grackle failed to parse query")) 37 | 38 | @Benchmark 39 | def runGql(): NonEmptyList[QueryAst.ExecutableDefinition[Caret]] = 40 | gql.parser.parseQuery(fullIntrospectionQuery).fold(e => throw new Throwable(e.prettyError.value), identity) 41 | } 42 | -------------------------------------------------------------------------------- /benchmarks/src/test/scala/caliban/CalibanSpec.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import caliban.SimpleQueryBenchmark.simpleQuery 4 | import caliban.ComplexQueryBenchmark.fullIntrospectionQuery 5 | import caliban.FragmentsQueryBenchmark.fragmentsQuery 6 | import caliban.parsing.Parser 7 | import zio.test._ 8 | 9 | object CalibanSpec extends ZIOSpecDefault { 10 | override def spec = 11 | suite("Caliban")( 12 | test("Simple") { 13 | val io = Caliban.interpreter.execute(simpleQuery) 14 | assertTrue(Caliban.run(io).errors.isEmpty) 15 | }, 16 | test("Complex") { 17 | val io = Caliban.interpreter.execute(fullIntrospectionQuery) 18 | assertTrue(Caliban.run(io).errors.isEmpty) 19 | }, 20 | test("Fragments") { 21 | val io = Caliban.interpreter.execute(fragmentsQuery) 22 | assertTrue(Caliban.run(io).errors.isEmpty) 23 | }, 24 | test("Parser") { 25 | Parser.parseQuery(fullIntrospectionQuery).map { doc => 26 | assertTrue(doc.definitions.nonEmpty) 27 | } 28 | } 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /benchmarks/src/test/scala/caliban/GrackleSpec.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import caliban.ComplexQueryBenchmark.fullIntrospectionQuery 4 | import caliban.FragmentsQueryBenchmark.fragmentsQuery 5 | import caliban.SimpleQueryBenchmark.simpleQuery 6 | import zio.test._ 7 | 8 | object GrackleSpec extends ZIOSpecDefault { 9 | override def spec = 10 | suite("Grackle")( 11 | test("Simple") { 12 | val io = Grackle.compileAndRun(simpleQuery) 13 | assertTrue(!Grackle.run(io).toString.contains("errors")) 14 | }, 15 | test("Complex") { 16 | val io = Grackle.compileAndRun(fullIntrospectionQuery) 17 | assertTrue(!Grackle.run(io).toString.contains("errors")) 18 | }, 19 | test("Fragments") { 20 | val io = Grackle.compileAndRun(fragmentsQuery) 21 | assertTrue(!Grackle.run(io).toString.contains("errors")) 22 | }, 23 | test("Parser") { 24 | val result = Grackle.compiler.compile(fullIntrospectionQuery) 25 | assertTrue(result.toEither.isRight) 26 | } 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /client-laminext/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Caliban Client 5 | 6 | 7 | 8 |
9 | <%- injectScript %> 10 | -------------------------------------------------------------------------------- /client-laminext/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "caliban-client-demo", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "devDependencies": { 7 | "vite": "^2.1.5", 8 | "vite-plugin-html": "^2.0.6" 9 | } 10 | } -------------------------------------------------------------------------------- /client-laminext/src/main/scala/caliban/client/laminext/Subscription.scala: -------------------------------------------------------------------------------- 1 | package caliban.client.laminext 2 | 3 | import caliban.client.CalibanClientError 4 | import com.raquo.airstream.core.EventStream 5 | 6 | trait Subscription[A] { 7 | def received: EventStream[Either[CalibanClientError, A]] 8 | def unsubscribe(): Unit 9 | } 10 | -------------------------------------------------------------------------------- /client-laminext/src/main/scala/io/laminext/fetch/NonOkayResponse.scala: -------------------------------------------------------------------------------- 1 | package io.laminext.fetch 2 | 3 | import org.scalajs.dom.Response 4 | 5 | class NonOkayResponse(val response: Response) extends Throwable 6 | -------------------------------------------------------------------------------- /client-laminext/src/main/scala/io/laminext/fetch/jsoniter/FetchJsoniterSyntax.scala: -------------------------------------------------------------------------------- 1 | package io.laminext.fetch.jsoniter 2 | 3 | import com.github.plokhotnyuk.jsoniter_scala.core.{ writeToString, JsonValueCodec } 4 | 5 | import scala.language.implicitConversions 6 | 7 | trait FetchJsoniterSyntax { 8 | 9 | implicit def jsonRequestBody[A](value: A)(implicit encoder: JsonValueCodec[A]): ToRequestBody = 10 | new JsonToRequestBody(writeToString(value)) 11 | 12 | implicit def fetchEventStreamBuilderSyntaxJsoniter(b: FetchEventStreamBuilder): FetchEventStreamBuilderJsoniterOps = 13 | new FetchEventStreamBuilderJsoniterOps(b) 14 | } 15 | -------------------------------------------------------------------------------- /client-laminext/src/main/scala/io/laminext/fetch/jsoniter/JsonToRequestBody.scala: -------------------------------------------------------------------------------- 1 | package io.laminext.fetch.jsoniter 2 | 3 | import org.scalajs.dom.BodyInit 4 | 5 | import scala.scalajs.js 6 | import scala.scalajs.js.UndefOr 7 | 8 | class JsonToRequestBody(jsonStr: String) extends ToRequestBody { 9 | 10 | override def apply(): UndefOr[BodyInit] = jsonStr 11 | 12 | override def updateHeaders(headers: js.UndefOr[Map[String, String]]): js.UndefOr[Map[String, String]] = 13 | headers.getOrElse(Map.empty).updated("content-type", "application/json; charset=utf-8") 14 | 15 | } 16 | -------------------------------------------------------------------------------- /client-laminext/src/main/scala/io/laminext/fetch/jsoniter/package.scala: -------------------------------------------------------------------------------- 1 | package io.laminext.fetch 2 | 3 | package object jsoniter extends ReExports with FetchSyntax with FetchJsoniterSyntax 4 | -------------------------------------------------------------------------------- /client-laminext/src/main/scala/io/laminext/websocket/jsoniter/WebSocketReceiveBuilderJsoniterOps.scala: -------------------------------------------------------------------------------- 1 | package io.laminext.websocket.jsoniter 2 | 3 | import com.github.plokhotnyuk.jsoniter_scala.core.{ readFromString, writeToString, JsonValueCodec } 4 | import io.laminext.websocket.{ initialize, receive, send, WebSocketBuilder, WebSocketReceiveBuilder } 5 | 6 | import scala.util.control.NonFatal 7 | 8 | class WebSocketReceiveBuilderJsoniterOps(b: WebSocketReceiveBuilder) { 9 | 10 | @inline def json[Receive, Send](implicit 11 | receiveCodec: JsonValueCodec[Receive], 12 | sendCodec: JsonValueCodec[Send] 13 | ): WebSocketBuilder[Receive, Send] = 14 | new WebSocketBuilder[Receive, Send]( 15 | url = b.url, 16 | protocol = b.protocol, 17 | initializer = initialize.text, 18 | sender = send.text[Send](writeToString(_)), 19 | receiver = receive.text[Receive] { text => 20 | try Right(readFromString[Receive](text)) 21 | catch { 22 | case NonFatal(e) => Left(e) 23 | } 24 | } 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /client-laminext/src/main/scala/io/laminext/websocket/jsoniter/package.scala: -------------------------------------------------------------------------------- 1 | package io.laminext.websocket 2 | 3 | import scala.language.implicitConversions 4 | 5 | package object jsoniter extends ReExports { 6 | 7 | implicit def webSocketReceiveBuilderSyntax(b: WebSocketReceiveBuilder): WebSocketReceiveBuilderJsoniterOps = 8 | new WebSocketReceiveBuilderJsoniterOps(b) 9 | } 10 | -------------------------------------------------------------------------------- /client-laminext/src/test/scala/caliban/client/laminext/Main.scala: -------------------------------------------------------------------------------- 1 | package caliban.client.laminext 2 | 3 | import com.raquo.laminar.api.L._ 4 | import org.scalajs.dom 5 | 6 | object Main { 7 | def main(args: Array[String]): Unit = { 8 | val _ = documentEvents(_.onDomContentLoaded).foreach { _ => 9 | val appContainer = dom.document.querySelector("#app") 10 | appContainer.innerHTML = "" 11 | val _ = render(appContainer, Page.view) 12 | }(unsafeWindowOwner) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client-laminext/vite.config.js: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path' 2 | import { injectHtml } from 'vite-plugin-html' 3 | 4 | const scalaVersion = '2.13' 5 | 6 | export default ({ mode }) => { 7 | const mainJS = `/target/scala-${scalaVersion}/caliban-client-laminext-test-${mode === 'production' ? 'opt' : 'fastopt'}/main.js` 8 | return { 9 | publicDir: './static/public', 10 | plugins: [ 11 | injectHtml({ 12 | injectData: { 13 | injectScript: `` 14 | } 15 | }) 16 | ] 17 | } 18 | } -------------------------------------------------------------------------------- /client/src/main/scala/caliban/client/GraphQLRequest.scala: -------------------------------------------------------------------------------- 1 | package caliban.client 2 | 3 | import com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec 4 | import com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker 5 | 6 | /** 7 | * Represents a GraphQL request, containing a query and a map of variables. 8 | */ 9 | case class GraphQLRequest(query: String, variables: Map[String, __Value]) 10 | 11 | object GraphQLRequest { 12 | 13 | implicit val jsonEncoder: JsonValueCodec[GraphQLRequest] = JsonCodecMaker.makeCirceLike[GraphQLRequest] 14 | 15 | } 16 | -------------------------------------------------------------------------------- /client/src/main/scala/caliban/client/GraphQLResponse.scala: -------------------------------------------------------------------------------- 1 | package caliban.client 2 | 3 | import caliban.client.__Value.__ObjectValue 4 | import com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec 5 | import com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker 6 | 7 | /** 8 | * Represents the result of a GraphQL query, containing a data object and a list of errors. 9 | */ 10 | case class GraphQLResponse( 11 | data: Option[__Value], 12 | errors: List[GraphQLResponseError] = Nil, 13 | extensions: Option[__ObjectValue] = None 14 | ) 15 | 16 | object GraphQLResponse { 17 | 18 | implicit val jsonCodec: JsonValueCodec[GraphQLResponse] = JsonCodecMaker.makeCirceLike[GraphQLResponse] 19 | 20 | } 21 | -------------------------------------------------------------------------------- /client/src/main/scala/caliban/client/Operations.scala: -------------------------------------------------------------------------------- 1 | package caliban.client 2 | 3 | import scala.annotation.implicitNotFound 4 | 5 | object Operations { 6 | type RootQuery 7 | type RootMutation 8 | type RootSubscription 9 | 10 | /** 11 | * Typeclass used to enforce that we can only create a request for one of the root fields. 12 | */ 13 | @implicitNotFound( 14 | """Your selection is not a root type. 15 | 16 | You can only transform a SelectionBuilder into a GraphQL request if it's a root query, mutation or subscription. 17 | """ 18 | ) 19 | trait IsOperation[A] { 20 | def operationName: String 21 | } 22 | 23 | object IsOperation { 24 | implicit val query: IsOperation[RootQuery] = new IsOperation[RootQuery] { 25 | override def operationName: String = "query" 26 | } 27 | implicit val mutation: IsOperation[RootMutation] = new IsOperation[RootMutation] { 28 | override def operationName: String = "mutation" 29 | } 30 | implicit val subscription: IsOperation[RootSubscription] = new IsOperation[RootSubscription] { 31 | override def operationName: String = "subscription" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /client/src/main/scala/caliban/client/Selection.scala: -------------------------------------------------------------------------------- 1 | package caliban.client 2 | 3 | sealed trait Selection 4 | 5 | object Selection { 6 | case class InlineFragment(onType: String, selectionSet: List[Selection]) extends Selection 7 | 8 | case class Field( 9 | alias: Option[String], 10 | name: String, 11 | arguments: List[Argument[_]], 12 | directives: List[Directive], 13 | selectionSet: List[Selection], 14 | code: Int 15 | ) extends Selection 16 | 17 | case class Directive(name: String, arguments: List[Argument[_]] = Nil) { 18 | def toGraphQL( 19 | useVariables: Boolean, 20 | dropNullInputValues: Boolean, 21 | variables: Map[String, (__Value, String)] 22 | ): (String, Map[String, (__Value, String)]) = { 23 | val (newArgs, newVariables) = arguments.foldLeft((List.empty[String], variables)) { 24 | case ((args, variables), arg) => 25 | val (arg2, variables2) = arg.toGraphQL(useVariables, dropNullInputValues, variables) 26 | (arg2 :: args, variables2) 27 | } 28 | (s"@$name(${newArgs.reverse.mkString(",")})", newVariables) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /client/src/main/scala/caliban/client/ws/GraphQLWSRequest.scala: -------------------------------------------------------------------------------- 1 | package caliban.client.ws 2 | 3 | import caliban.client.GraphQLRequest 4 | import com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec 5 | import com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker 6 | 7 | case class GraphQLWSRequest(`type`: String, id: Option[String], payload: Option[GraphQLRequest]) 8 | 9 | object GraphQLWSRequest { 10 | implicit val graphQLWSRequestEncoder: JsonValueCodec[GraphQLWSRequest] = 11 | JsonCodecMaker.makeCirceLike[GraphQLWSRequest] 12 | } 13 | -------------------------------------------------------------------------------- /client/src/main/scala/caliban/client/ws/GraphQLWSResponse.scala: -------------------------------------------------------------------------------- 1 | package caliban.client.ws 2 | 3 | import caliban.client.__Value.__ObjectValue 4 | import com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec 5 | import com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker 6 | 7 | case class GraphQLWSResponse(`type`: String, id: Option[String], payload: Option[__ObjectValue]) 8 | 9 | object GraphQLWSResponse { 10 | private[caliban] implicit val graphQLWSResponseEncoder: JsonValueCodec[GraphQLWSResponse] = 11 | JsonCodecMaker.makeCirceLike[GraphQLWSResponse] 12 | } 13 | -------------------------------------------------------------------------------- /client/src/test/scala/caliban/client/ArgEncoderSpec.scala: -------------------------------------------------------------------------------- 1 | package caliban.client 2 | 3 | import zio.test._ 4 | import java.util.UUID 5 | 6 | object ArgEncoderSpec extends ZIOSpecDefault { 7 | override def spec = 8 | suite("ArgEncoderSpec")( 9 | suite("__StringValue")( 10 | test("regular string") { 11 | assertTrue(ArgEncoder.string.encode("abcde who am i?").toString == """"abcde who am i?"""") 12 | }, 13 | test("string with quotes") { 14 | assertTrue(ArgEncoder.string.encode("abcde \"who am i?\"").toString == """"abcde \"who am i?\""""") 15 | }, 16 | test("string with new line") { 17 | assertTrue(ArgEncoder.string.encode("abcde\n who\n am\n i\n").toString == """"abcde\n who\n am\n i\n"""") 18 | }, 19 | test("string with null characters") { 20 | assertTrue(ArgEncoder.string.encode("abcde who am i\u0000").toString == "\"abcde who am i\\u0000\"") 21 | } 22 | ), 23 | suite("__UUIDValue")( 24 | test("regular uuid") { 25 | assertTrue( 26 | ArgEncoder.uuid 27 | .encode(UUID.fromString("20a69d87-6d68-4779-a4da-601f4c04ebf3")) 28 | .toString == """"20a69d87-6d68-4779-a4da-601f4c04ebf3"""" 29 | ) 30 | } 31 | ) 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /codegen-sbt/src/main/scala/caliban/codegen/CalibanKeys.scala: -------------------------------------------------------------------------------- 1 | package caliban.codegen 2 | 3 | import caliban.tools.CalibanCommonSettings 4 | import sbt._ 5 | 6 | import java.net.URL 7 | 8 | trait CalibanKeys { 9 | lazy val caliban = taskKey[Seq[File]]("Generate GraphQL sources using caliban-codegen-sbt") 10 | lazy val calibanGenerator = taskKey[Seq[File]]("Generate GraphQL sources using caliban-codegen-sbt") 11 | 12 | lazy val calibanSources = settingKey[File]("Where to find .graphql schemas") 13 | lazy val calibanSettings = settingKey[Seq[CalibanSettings]]("Settings that apply to individual GraphQL files") 14 | 15 | def calibanSetting(file: File)(setting: CalibanFileSettings => CalibanFileSettings): CalibanSettings = 16 | setting.apply(CalibanFileSettings(file = file, settings = CalibanCommonSettings.empty)) 17 | def calibanSetting(url: URL)(setting: CalibanUrlSettings => CalibanUrlSettings): CalibanSettings = 18 | setting.apply(CalibanUrlSettings(url = url, settings = CalibanCommonSettings.empty)) 19 | 20 | val calibanVersion = settingKey[String]("Version of the Caliban sbt plugin") 21 | } 22 | 23 | object CalibanKeys extends CalibanKeys 24 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/gen-client-task/.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = "3.1.2" 2 | 3 | runner.dialect = scala213 4 | preset = default 5 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/gen-client-task/build.sbt: -------------------------------------------------------------------------------- 1 | lazy val root = project 2 | .in(file(".")) 3 | .enablePlugins(CalibanPlugin) 4 | .settings( 5 | libraryDependencies ++= Seq( 6 | "com.github.ghostdogpr" %% "caliban-client" % Version.pluginVersion 7 | ) 8 | ) 9 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/gen-client-task/project/Version.scala: -------------------------------------------------------------------------------- 1 | object Version { 2 | def pluginVersion: String = 3 | sys.props.get("plugin.version") match { 4 | case Some(x) => x 5 | case _ => sys.error("""|The system property 'plugin.version' is not defined. 6 | |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/gen-client-task/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | sys.props.get("plugin.version") match { 2 | case Some(x) => addSbtPlugin("com.github.ghostdogpr" % "caliban-codegen-sbt" % x) 3 | case _ => sys.error("""|The system property 'plugin.version' is not defined. 4 | |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) 5 | } 6 | addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.4") 7 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/gen-client-task/verify.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if grep -q "$1" "$2"; then 4 | echo "$1 exists in $2" 5 | exit 0 6 | else 7 | echo "$1 is missing in $2" 8 | exit 1 9 | fi -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/test-compile-newtype/modules/base/src/main/scala/graphql/package.scala: -------------------------------------------------------------------------------- 1 | import zio.query.ZQuery 2 | import javax.sql.DataSource 3 | 4 | package object graphql { 5 | type Env = DataSource 6 | type FID = Int 7 | type MyZQuery[A] = ZQuery[Env, Throwable, A] 8 | } 9 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/test-compile-newtype/project/Version.scala: -------------------------------------------------------------------------------- 1 | object Version { 2 | def pluginVersion: String = 3 | sys.props.get("plugin.version") match { 4 | case Some(x) => x 5 | case _ => sys.error("""|The system property 'plugin.version' is not defined. 6 | |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/test-compile-newtype/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | sys.props.get("plugin.version") match { 2 | case Some(x) => addSbtPlugin("com.github.ghostdogpr" % "caliban-codegen-sbt" % x) 3 | case _ => sys.error("""|The system property 'plugin.version' is not defined. 4 | |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) 5 | } 6 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/test-compile-newtype/src/main/graphql/schema.graphql: -------------------------------------------------------------------------------- 1 | directive @lazy on FIELD_DEFINITION 2 | directive @newtype(name : String) on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION 3 | 4 | scalar FID 5 | 6 | type Query { 7 | getFoo( 8 | id: ID! @newtype(name: "CustomId"), 9 | maybeId : ID @newtype(name: "ACustomIdOpt") 10 | maybeAllIDsOpt: [ID] @newtype(name: "AMaybeInnerIdOpt") 11 | ): Foo 12 | } 13 | 14 | type Mutation { 15 | updateFoo(foo: FooInput!): Foo 16 | } 17 | 18 | type Foo { 19 | id : ID! @newtype(name: "CustomId") 20 | strId : String! @newtype(name: "CustomStrId") 21 | intId : Int! @newtype(name: "CustomIntId") 22 | fid : FID! @newtype(name: "CustomFId") 23 | maybeId : ID @newtype(name: "CustomIdOpt") 24 | IDs : [ID!]! 25 | allIDs : [ID!]! @newtype(name: "InnerId") 26 | allIDsOpt: [ID]! @newtype(name: "InnerOptId") 27 | maybeAllIDs: [ID!] @newtype(name: "MaybeInnerId") 28 | maybeAllIDsOpt: [ID] @newtype(name: "MaybeInnerIdOpt") 29 | getLazyFoo : FooLazy @lazy 30 | } 31 | 32 | type FooLazy { 33 | id : ID! 34 | } 35 | 36 | 37 | input FooInput { 38 | id : ID! @newtype(name: "CustomId") 39 | allIDs : [ID!]! @newtype(name: "IInnerId") 40 | maybeAllIDsOpt: [ID] @newtype(name: "IMaybeInnerIdOpt") 41 | } -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/test-compile-newtype/test: -------------------------------------------------------------------------------- 1 | > compile 2 | 3 | $ exists target/scala-2.12/src_managed/main/caliban-codegen-sbt/graphql/GeneratedAPI.scala 4 | $ exec sh verify.sh Foo target/scala-2.12/src_managed/main/caliban-codegen-sbt/graphql/GeneratedAPI.scala 5 | $ exec sh verify.sh FooInput target/scala-2.12/src_managed/main/caliban-codegen-sbt/graphql/GeneratedAPI.scala 6 | $ exec sh verify.sh CustomId target/scala-2.12/src_managed/main/caliban-codegen-sbt/graphql/GeneratedAPI.scala 7 | $ exec sh verify.sh FooLazy target/scala-2.12/src_managed/main/caliban-codegen-sbt/graphql/GeneratedAPI.scala 8 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/test-compile-newtype/verify.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if grep -q "$1" "$2"; then 4 | echo "$1 exists in $2" 5 | exit 0 6 | else 7 | echo "$1 is missing in $2" 8 | exit 1 9 | fi -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/test-compile/build.sbt: -------------------------------------------------------------------------------- 1 | import _root_.caliban.tools.Codegen 2 | 3 | lazy val root = project 4 | .in(file(".")) 5 | .enablePlugins(CalibanPlugin) 6 | .settings( 7 | libraryDependencies ++= Seq( 8 | "com.github.ghostdogpr" %% "caliban" % Version.pluginVersion, 9 | "com.github.ghostdogpr" %% "caliban-client" % Version.pluginVersion 10 | ), 11 | Compile / caliban / calibanSettings ++= Seq( 12 | calibanSetting(file("src/main/graphql/schema.graphql"))( // Explicitly constrain to disambiguate 13 | _.clientName("Client") 14 | ), 15 | // Another entry for the same file, which will cause another generator to run 16 | calibanSetting(file("src/main/graphql/schema.graphql"))( 17 | _.genType(Codegen.GenType.Schema) 18 | .scalarMapping("Json" -> "String") 19 | .effect("scala.util.Try") 20 | .addDerives(false) 21 | ), 22 | calibanSetting(file("src/main/graphql/schema.graphql"))( 23 | _.genType(Codegen.GenType.Schema) 24 | .scalarMapping("Json" -> "String") 25 | .effect("F") 26 | .abstractEffectType(true) 27 | ), 28 | calibanSetting(file("src/main/graphql/genview/schema.graphql"))( 29 | _.clientName("Client").packageName("genview").genView(true) 30 | ) 31 | ) 32 | ) 33 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/test-compile/project/Version.scala: -------------------------------------------------------------------------------- 1 | object Version { 2 | def pluginVersion: String = 3 | sys.props.get("plugin.version") match { 4 | case Some(x) => x 5 | case _ => sys.error("""|The system property 'plugin.version' is not defined. 6 | |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/test-compile/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | sys.props.get("plugin.version") match { 2 | case Some(x) => addSbtPlugin("com.github.ghostdogpr" % "caliban-codegen-sbt" % x) 3 | case _ => sys.error("""|The system property 'plugin.version' is not defined. 4 | |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) 5 | } 6 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/test-compile/test: -------------------------------------------------------------------------------- 1 | > compile 2 | 3 | $ exists target/scala-2.12/src_managed/main/caliban-codegen-sbt/caliban/Client.scala 4 | $ exists target/scala-2.12/src_managed/main/caliban-codegen-sbt/genview/Client.scala 5 | $ exists target/scala-2.12/src_managed/main/caliban-codegen-sbt/caliban/schema.scala 6 | $ exec sh verify.sh CharacterView target/scala-2.12/src_managed/main/caliban-codegen-sbt/genview/Client.scala 7 | $ exec sh verify.sh OptionView target/scala-2.12/src_managed/main/caliban-codegen-sbt/genview/Client.scala 8 | $ exec sh verify.sh CharacterOneOfInput target/scala-2.12/src_managed/main/caliban-codegen-sbt/genview/Client.scala 9 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/test-compile/verify.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if grep -q "$1" "$2"; then 4 | echo "$1 exists in $2" 5 | exit 0 6 | else 7 | echo "$1 is missing in $2" 8 | exit 1 9 | fi -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/test-split-files-compile/build.sbt: -------------------------------------------------------------------------------- 1 | import _root_.caliban.tools.Codegen 2 | 3 | lazy val root = project 4 | .in(file(".")) 5 | .enablePlugins(CalibanPlugin) 6 | .settings( 7 | libraryDependencies ++= Seq( 8 | "com.github.ghostdogpr" %% "caliban" % Version.pluginVersion, 9 | "com.github.ghostdogpr" %% "caliban-client" % Version.pluginVersion 10 | ), 11 | Compile / caliban / calibanSettings ++= Seq( 12 | calibanSetting(file("src/main/graphql/schema.graphql"))( // Explicitly constrain to disambiguate 13 | _.clientName("Client") 14 | .splitFiles(true) 15 | ) 16 | ) 17 | ) 18 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/test-split-files-compile/project/Version.scala: -------------------------------------------------------------------------------- 1 | object Version { 2 | def pluginVersion: String = 3 | sys.props.get("plugin.version") match { 4 | case Some(x) => x 5 | case _ => sys.error("""|The system property 'plugin.version' is not defined. 6 | |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/test-split-files-compile/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | sys.props.get("plugin.version") match { 2 | case Some(x) => addSbtPlugin("com.github.ghostdogpr" % "caliban-codegen-sbt" % x) 3 | case _ => sys.error("""|The system property 'plugin.version' is not defined. 4 | |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) 5 | } 6 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/test-split-files-compile/test: -------------------------------------------------------------------------------- 1 | $ exec echo compiling for the first time 2 | > compile 3 | 4 | $ exists target/scala-2.12/src_managed/main/caliban-codegen-sbt/caliban/Client/package.scala 5 | $ exists target/scala-2.12/src_managed/main/caliban-codegen-sbt/caliban/Client/Character.scala 6 | $ exists target/scala-2.12/src_managed/main/caliban-codegen-sbt/caliban/Client/Canterbury.scala 7 | 8 | $ exec echo compiling second time 9 | > compile 10 | 11 | $ exists target/scala-2.12/src_managed/main/caliban-codegen-sbt/caliban/Client/package.scala 12 | $ exists target/scala-2.12/src_managed/main/caliban-codegen-sbt/caliban/Client/Character.scala 13 | $ exists target/scala-2.12/src_managed/main/caliban-codegen-sbt/caliban/Client/Canterbury.scala 14 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/codegen/test-split-files-compile/verify.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if grep -q "$1" "$2"; then 4 | echo "$1 exists in $2" 5 | exit 0 6 | else 7 | echo "$1 is missing in $2" 8 | exit 1 9 | fi -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/compiletime-codegen/test-compile/README.md: -------------------------------------------------------------------------------- 1 | # Test doc 2 | 3 | This test project has been copied from: 4 | https://github.com/guizmaii/poc_compile_time_caliban_client_generation 5 | 6 | ### Running locally 7 | You can run these tests using following sbt commandos: 8 | 9 | ```sbt 10 | project codegenSbt 11 | ++2.12 12 | update 13 | scripted compiletime-codegen/test-compile 14 | ``` 15 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/compiletime-codegen/test-compile/modules/clients/src/main/scala/poc/caliban/client/PostClient.scala: -------------------------------------------------------------------------------- 1 | package poc.caliban.client 2 | 3 | import poc.caliban.client.generated.posts.CalibanClient 4 | import poc.caliban.client.generated.posts.CalibanClient._ 5 | import sttp.capabilities.WebSockets 6 | import sttp.capabilities.zio.ZioStreams 7 | import sttp.client3.SttpBackend 8 | import zio.Task 9 | 10 | trait PostClient { 11 | def postById(id: String): Task[Option[(String, String)]] 12 | } 13 | 14 | final class PostClientLive(backend: SttpBackend[Task, ZioStreams with WebSockets]) extends PostClient { 15 | import sttp.client3._ 16 | 17 | private val serverUrl = uri"http://localhost:8088/api/graphql" 18 | 19 | /** 20 | * Jules' comment: 21 | * 22 | * In real-world code, I'd never accept to get a String and return 2 String. I'd use proper types but it's not important in this POC. 23 | */ 24 | override def postById(id: String): Task[Option[(String, String)]] = 25 | CalibanClient.Query 26 | .postById(id)(Post.id(PostId.id) ~ Post.author(AuthorName.name)) 27 | .toRequest(serverUrl) 28 | .send(backend) 29 | .map(_.body) 30 | .absolve 31 | 32 | } 33 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/compiletime-codegen/test-compile/modules/clients/src/main/scala/poc/caliban/client/PotatoesClient.scala: -------------------------------------------------------------------------------- 1 | package poc.caliban.client 2 | 3 | import poc.caliban.client.generated.potatoes._ 4 | import sttp.capabilities.WebSockets 5 | import sttp.capabilities.zio.ZioStreams 6 | import sttp.client3.SttpBackend 7 | import zio.{ Task, ZIO } 8 | 9 | trait PotatoesClient { 10 | def eradicate(name: String): Task[Unit] 11 | } 12 | 13 | final class PotatoesClientLive(backend: SttpBackend[Task, ZioStreams with WebSockets]) extends PotatoesClient { 14 | import sttp.client3._ 15 | 16 | private val serverUrl = uri"http://localhost:8088/api/graphql" 17 | 18 | override def eradicate(name: String): Task[Unit] = 19 | Mutation 20 | .eradicate(name) 21 | .toRequest(serverUrl) 22 | .send(backend) 23 | .foldZIO(ZIO.fail(_), r => ZIO.fromEither(r.body).unit) 24 | 25 | } 26 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/compiletime-codegen/test-compile/modules/posts-clients/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostdogpr/caliban/24a568744f6708a4f400d7f8f5f158b002f29806/codegen-sbt/src/sbt-test/compiletime-codegen/test-compile/modules/posts-clients/.gitkeep -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/compiletime-codegen/test-compile/modules/posts/src/test/resources/postservice.graphql: -------------------------------------------------------------------------------- 1 | schema { 2 | query: Query 3 | mutation: Mutation 4 | subscription: Subscription 5 | } 6 | scalar Unit 7 | 8 | input AuthorNameInput { 9 | name: String! 10 | } 11 | 12 | input PostContentInput { 13 | content: String! 14 | } 15 | 16 | input PostTitleInput { 17 | title: String! 18 | } 19 | 20 | type AuthorName { 21 | name: String! 22 | } 23 | 24 | type Mutation { 25 | createPost(authorName: AuthorNameInput!, title: PostTitleInput!, content: PostContentInput!): Post 26 | deletePost(id: ID!): Unit 27 | } 28 | 29 | type Post { 30 | id: PostId! 31 | author: AuthorName! 32 | title: PostTitle! 33 | content: PostContent! 34 | } 35 | 36 | type PostContent { 37 | content: String! 38 | } 39 | 40 | type PostId { 41 | id: ID! 42 | } 43 | 44 | type PostTitle { 45 | title: String! 46 | } 47 | 48 | type Query { 49 | postById(id: ID!): Post 50 | } 51 | 52 | type Subscription { 53 | allPostsByAuthor(name: String!): Post 54 | } -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/compiletime-codegen/test-compile/modules/posts/src/test/scala/ValidateGraphQlSpec.scala: -------------------------------------------------------------------------------- 1 | import poc.caliban.posts.GraphQLApi 2 | 3 | import scala.io.Source 4 | import zio.test.Assertion._ 5 | import zio.test._ 6 | import caliban.tools._ 7 | import java.nio.file.Path 8 | 9 | object ValidateGraphQlSpec extends SnapshotTest { 10 | override val testName: String = "ValidateGraphQlSpec" 11 | 12 | val graphqlFile= "src/sbt-test/compiletime-codegen/test-compile/modules/posts/src/test/resources/postservice.graphql" 13 | val projectDir = sys.props.get("project.dir").getOrElse("") 14 | 15 | override def spec = 16 | suite("Validate Postservice")( 17 | test("Render postservice as earlier") { 18 | val gqlApi = GraphQLApi.api 19 | val renderContent: String = s"${gqlApi.render}" 20 | 21 | writeAndCompare(Path.of(projectDir).resolve(graphqlFile), renderContent, "Render postservice") 22 | } 23 | ) 24 | } -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/compiletime-codegen/test-compile/modules/potatoes-clients/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostdogpr/caliban/24a568744f6708a4f400d7f8f5f158b002f29806/codegen-sbt/src/sbt-test/compiletime-codegen/test-compile/modules/potatoes-clients/.gitkeep -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/compiletime-codegen/test-compile/project/Version.scala: -------------------------------------------------------------------------------- 1 | object Version { 2 | def pluginVersion: String = 3 | sys.props.get("plugin.version") match { 4 | case Some(x) => x 5 | case _ => sys.error("""|The system property 'plugin.version' is not defined. 6 | |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) 7 | } 8 | def zioTestVersion: String = 9 | sys.props.get("zio.test.version") match { 10 | case Some(x) => x 11 | case _ => sys.error("""|The system property 'zio.test.version' is not defined. 12 | |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) 13 | } 14 | def sttpVersion: String = 15 | sys.props.get("sttp.version") match { 16 | case Some(x) => x 17 | case _ => sys.error("""|The system property 'sttp.version' is not defined. 18 | |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/compiletime-codegen/test-compile/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 1.11.1 2 | -------------------------------------------------------------------------------- /codegen-sbt/src/sbt-test/compiletime-codegen/test-compile/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | import sbt.librarymanagement.Resolver 2 | 3 | resolvers += Resolver.mavenLocal 4 | resolvers += Resolver.sonatypeRepo("snapshots") 5 | 6 | sys.props.get("plugin.version") match { 7 | case Some(x) => addSbtPlugin("com.github.ghostdogpr" % "caliban-codegen-sbt" % x) 8 | case _ => sys.error("""|The system property 'plugin.version' is not defined. 9 | |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/scala-2/caliban/Macros.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import scala.language.experimental.macros 4 | import scala.reflect.macros.blackbox 5 | 6 | import caliban.parsing.Parser 7 | 8 | object Macros { 9 | 10 | /** 11 | * Verifies at compile-time that the given string is a valid GraphQL document. 12 | * @param document a string representing a GraphQL document. 13 | */ 14 | def gqldoc(document: String): String = macro MacrosInternal.queryLiteral 15 | 16 | private class MacrosInternal(val c: blackbox.Context) { 17 | import c.universe._ 18 | def queryLiteral(document: c.Expr[String]): c.Expr[String] = 19 | document.tree match { 20 | case Literal(Constant(s: String)) => 21 | Parser.check(s).fold(document)(e => c.abort(c.enclosingPosition, s"GraphQL document is invalid: $e")) 22 | case _ => 23 | c.abort(c.enclosingPosition, s"This macro can only be used with string literals.") 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/scala-2/caliban/Scala3Annotations.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import scala.annotation.StaticAnnotation 4 | 5 | /** 6 | * Stubs for annotations that exist in Scala 3 but not in Scala 2 7 | */ 8 | private[caliban] object Scala3Annotations { 9 | final class static extends StaticAnnotation 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/scala-2/caliban/introspection/IntrospectionDerivation.scala: -------------------------------------------------------------------------------- 1 | package caliban.introspection 2 | 3 | import caliban.introspection.adt.{ __Introspection, __Type } 4 | import caliban.schema.ArgBuilder.auto._ 5 | import caliban.schema.Schema 6 | import caliban.schema.Schema.auto._ 7 | 8 | trait IntrospectionDerivation { 9 | private implicit lazy val typeSchema: Schema[Any, __Type] = genAll 10 | 11 | val introspectionSchema: Schema[Any, __Introspection] = genAll 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/scala-2/caliban/schema/AnnotationsVersionSpecific.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | import caliban.parsing.adt.Directive 4 | 5 | import scala.annotation.StaticAnnotation 6 | 7 | trait AnnotationsVersionSpecific { 8 | 9 | /** 10 | * Annotation used to provide directives to a schema type 11 | */ 12 | class GQLDirective(val directive: Directive) extends StaticAnnotation 13 | 14 | object GQLDirective { 15 | def unapply(annotation: GQLDirective): Option[Directive] = 16 | Some(annotation.directive) 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/scala-2/caliban/schema/DerivationUtils.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | import caliban.schema.Annotations.GQLValueType 4 | import magnolia1.ReadOnlyCaseClass 5 | 6 | private object DerivationUtils { 7 | 8 | def isValueType[F[_]](ctx: ReadOnlyCaseClass[F, ?]): Boolean = 9 | (ctx.isValueClass || ctx.annotations.exists(_.isInstanceOf[GQLValueType])) && ctx.parameters.nonEmpty 10 | 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/scala-2/caliban/schema/SchemaVersionSpecific.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | trait SchemaVersionSpecific 4 | -------------------------------------------------------------------------------- /core/src/main/scala-2/caliban/schema/SubscriptionSchemaDerivation.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | import magnolia1._ 4 | 5 | import scala.language.experimental.macros 6 | 7 | trait SubscriptionSchemaDerivation { 8 | type Typeclass[T] = SubscriptionSchema[T] 9 | 10 | def join[T](ctx: CaseClass[SubscriptionSchema, T]): Typeclass[T] = new Typeclass[T] {} 11 | 12 | implicit def gen[T]: Typeclass[T] = macro Magnolia.gen[T] 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/scala-2/caliban/syntax.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import scala.collection.mutable 4 | 5 | private[caliban] object syntax { 6 | val NullFn: () => AnyRef = () => null 7 | 8 | implicit class EnrichedImmutableMapOps[K, V <: AnyRef](private val self: Map[K, V]) extends AnyVal { 9 | def getOrElseNull(key: K): V = self.getOrElse(key, NullFn()).asInstanceOf[V] 10 | } 11 | 12 | implicit class EnrichedHashMapOps[K, V <: AnyRef](private val self: mutable.HashMap[K, V]) extends AnyVal { 13 | def getOrElseNull(key: K): V = self.getOrElse(key, NullFn()).asInstanceOf[V] 14 | } 15 | 16 | implicit class EnrichedListOps[A](private val self: List[A]) extends AnyVal { 17 | 18 | /** 19 | * In Scala 3, foreach is not inlined which can lead to performance issues when used in hot paths. 20 | * In Scala 2 this method simply points to foreach, but in Scala 3 it is inlined to a while loop. 21 | */ 22 | @inline def foreachOne(f: A => Unit): Unit = self.foreach(f) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/scala-3/caliban/Macros.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import scala.quoted._ 4 | 5 | import caliban.parsing.Parser 6 | 7 | object Macros { 8 | 9 | /** 10 | * Verifies at compile-time that the given string is a valid GraphQL document. 11 | * @param document a string representing a GraphQL document. 12 | */ 13 | inline def gqldoc(inline document: String): String = ${ gqldocImpl('document) } 14 | 15 | private def gqldocImpl(document: Expr[String])(using Quotes): Expr[String] = { 16 | import quotes.reflect.report 17 | document.value.fold(report.errorAndAbort("This macro can only be used with string literals."))( 18 | Parser.check(_).fold(document)(e => report.errorAndAbort(s"GraphQL document is invalid: $e")) 19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/scala-3/caliban/Scala3Annotations.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import scala.annotation 4 | 5 | /** 6 | * Proxies for annotations that exist in Scala 3 but not in Scala 2 7 | */ 8 | private[caliban] object Scala3Annotations { 9 | type static = annotation.static 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/scala-3/caliban/introspection/IntrospectionDerivation.scala: -------------------------------------------------------------------------------- 1 | package caliban.introspection 2 | 3 | import caliban.InputValue 4 | import caliban.Value.StringValue 5 | import caliban.introspection.adt._ 6 | import caliban.parsing.adt.Directive 7 | import caliban.schema.Schema 8 | import caliban.schema.ArgBuilder 9 | 10 | trait IntrospectionDerivation { 11 | private given Schema[Any, __InputValue] = Schema.derived 12 | private given Schema[Any, __EnumValue] = Schema.derived 13 | private given Schema[Any, __Field] = Schema.derived 14 | private given Schema[Any, __Type] = Schema.derived 15 | private given Schema[Any, __TypeArgs] = Schema.derived 16 | private given Schema[Any, __Schema] = Schema.derived 17 | private given Schema[Any, __Directive] = Schema.derived 18 | private given Schema[Any, __DeprecatedArgs] = Schema.derived 19 | 20 | // Unions, so we can auto-derive them cheaply 21 | private given Schema[Any, __TypeKind] = Schema.Auto.derived 22 | private given Schema[Any, __DirectiveLocation] = Schema.Auto.derived 23 | 24 | private given ArgBuilder[__TypeArgs] = ArgBuilder.derived 25 | private given ArgBuilder[__DeprecatedArgs] = ArgBuilder.derived 26 | 27 | val introspectionSchema: Schema[Any, __Introspection] = Schema.derived 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/scala-3/caliban/schema/AnnotationsVersionSpecific.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | import caliban.parsing.adt.Directive 4 | 5 | import scala.annotation.StaticAnnotation 6 | 7 | trait AnnotationsVersionSpecific { 8 | 9 | /** 10 | * Annotation that can be used on a case class method to mark it as a GraphQL field. 11 | * The method must be public, a `def` (does not work on `val`s / `lazy val`s) and must not take any arguments. 12 | * 13 | * '''NOTE''' This annotation is not safe for use with ahead-of-time compilation (e.g., generating a GraalVM native-image executable) 14 | */ 15 | case class GQLField() extends StaticAnnotation 16 | 17 | /** 18 | * Annotation that can be used on a case class / case object to have all the public methods on it derived as fields. 19 | * 20 | * If you wish to exclude a public method from being derived as a field, you can annotate it with [[GQLExclude]]. 21 | * 22 | * @see [[GQLField]] for a more fine-grained control over which methods are derived as fields 23 | */ 24 | case class GQLFieldsFromMethods() extends StaticAnnotation 25 | 26 | /** 27 | * Annotation used to provide directives to a schema type 28 | */ 29 | open class GQLDirective(val directive: Directive) extends StaticAnnotation 30 | 31 | object GQLDirective { 32 | def unapply(annotation: GQLDirective): Option[Directive] = 33 | Some(annotation.directive) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/scala-3/caliban/schema/EnumValueSchema.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | import caliban.Value.EnumValue 4 | import caliban.introspection.adt.__Type 5 | import caliban.schema.DerivationUtils.* 6 | import magnolia1.TypeInfo 7 | 8 | final private class EnumValueSchema[R, A]( 9 | info: TypeInfo, 10 | anns: List[Any], 11 | enableSemanticNonNull: Boolean 12 | ) extends Schema[R, A] { 13 | 14 | def toType(isInput: Boolean, isSubscription: Boolean): __Type = 15 | if (isInput) mkInputObject[R](anns, Nil, info)(isInput, isSubscription) 16 | else mkObject[R](anns, Nil, info, enableSemanticNonNull)(isInput, isSubscription) 17 | 18 | private val step = PureStep(EnumValue(getName(anns, info))) 19 | def resolve(value: A): Step[R] = step 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/scala-3/caliban/schema/ProductFieldInfo.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | private final case class ProductFieldInfo[R]( 4 | name: String, 5 | schema: Schema[R, Any], 6 | index: Int 7 | ) 8 | -------------------------------------------------------------------------------- /core/src/main/scala-3/caliban/schema/SchemaVersionSpecific.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | import caliban.introspection.adt.__Field 4 | import caliban.parsing.adt.Directive 5 | 6 | transparent trait SchemaVersionSpecific extends GenericSchema[Any] { 7 | 8 | /** 9 | * Scala 3 variant of the `obj` method which improves UX for creating custom object schemas. 10 | * 11 | * {{{ 12 | * case class Author(id: String, firstName: String, lastName: String) 13 | * 14 | * given Schema[Any, Author] = Schema.customObj("Author")( 15 | * field("id")(_.id), 16 | * field("fullName")(author => s"${author.firstName} ${author.lastName}"), 17 | * ) 18 | * }}} 19 | * 20 | * @see [[caliban.schema.GenericSchema.obj]] 21 | */ 22 | def customObj[R1, V]( 23 | name: String, 24 | description: Option[String] = None, 25 | directives: List[Directive] = Nil 26 | )( 27 | fields: FieldAttributes ?=> (__Field, V => Step[R1])* 28 | ): Schema[R1, V] = 29 | obj(name, description, directives) { case given FieldAttributes => 30 | fields.toList.map(identity) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/scala-3/caliban/schema/SubscriptionSchemaDerivation.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | import scala.deriving.Mirror 4 | import scala.compiletime._ 5 | 6 | trait SubscriptionSchemaDerivation { 7 | inline def checkParams[T <: Tuple]: Unit = 8 | inline erasedValue[T] match { 9 | case _: EmptyTuple => () 10 | case _: (t *: ts) => 11 | summonInline[SubscriptionSchema[t]] 12 | checkParams[ts] 13 | } 14 | 15 | inline def derived[A]: SubscriptionSchema[A] = 16 | inline summonInline[Mirror.ProductOf[A]] match { 17 | case m: Mirror.ProductOf[A] => 18 | checkParams[m.MirroredElemTypes] 19 | new ProductSubscriptionSchema[A] 20 | } 21 | 22 | inline given gen[A]: SubscriptionSchema[A] = derived 23 | } 24 | 25 | private final class ProductSubscriptionSchema[A] extends SubscriptionSchema[A] 26 | -------------------------------------------------------------------------------- /core/src/main/scala-3/caliban/schema/ValueTypeSchema.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | import caliban.introspection.adt.__Type 4 | import caliban.schema.Annotations.GQLValueType 5 | import caliban.schema.DerivationUtils.* 6 | import caliban.schema.Types.makeScalar 7 | import magnolia1.TypeInfo 8 | 9 | import scala.annotation.threadUnsafe 10 | 11 | final private class ValueTypeSchema[R, A]( 12 | _schema: => Schema[R, Any], 13 | info: TypeInfo, 14 | anns: List[Any] 15 | ) extends Schema[R, A] { 16 | private val name = getName(anns, info) 17 | 18 | @threadUnsafe 19 | private lazy val schema = _schema 20 | 21 | def toType(isInput: Boolean, isSubscription: Boolean): __Type = { 22 | val _ = schema 23 | if (anns.contains(GQLValueType(true))) makeScalar(name, getDescription(anns)) 24 | else schema.toType_(isInput, isSubscription) 25 | } 26 | 27 | def resolve(value: A): Step[R] = schema.resolve(value.asInstanceOf[Product].productElement(0)) 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/scala-3/caliban/syntax.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import scala.annotation.static 4 | 5 | import scala.collection.mutable 6 | 7 | private[caliban] object syntax { 8 | @static val NullFn: () => AnyRef = () => null 9 | 10 | extension [K, V <: AnyRef](inline map: Map[K, V]) { 11 | transparent inline def getOrElseNull(key: K): V = map.getOrElse(key, NullFn()).asInstanceOf[V] 12 | } 13 | 14 | extension [K, V <: AnyRef](inline map: mutable.HashMap[K, V]) { 15 | transparent inline def getOrElseNull(key: K): V = map.getOrElse(key, NullFn()).asInstanceOf[V] 16 | } 17 | 18 | extension [A](inline list: List[A]) { 19 | 20 | /** 21 | * In Scala 3, foreach is not inlined which can lead to performance issues when used in hot paths. 22 | * In Scala 2 this method simply points to foreach, but in Scala 3 it is inlined to a while loop. 23 | */ 24 | inline def foreachOne(inline f: A => Any): Unit = { 25 | var rem = list 26 | while (rem ne Nil) { 27 | f(rem.head) 28 | rem = rem.tail 29 | } 30 | } 31 | } 32 | } 33 | 34 | // Required for @static fields 35 | private final class syntax private 36 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/GraphQLWSClose.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | case class GraphQLWSClose(code: Int, reason: String) 4 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/GraphQLWSInput.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import caliban.interop.tapir.IsTapirSchema 4 | import com.github.plokhotnyuk.jsoniter_scala.core._ 5 | import com.github.plokhotnyuk.jsoniter_scala.macros._ 6 | 7 | case class GraphQLWSInput(`type`: String, id: Option[String], payload: Option[InputValue]) 8 | 9 | object GraphQLWSInput { 10 | implicit val jsoniterCodec: JsonValueCodec[GraphQLWSInput] = JsonCodecMaker.make 11 | 12 | implicit def tapirSchema[F[_]: IsTapirSchema]: F[GraphQLWSInput] = 13 | caliban.interop.tapir.schema.wsInputSchema.asInstanceOf[F[GraphQLWSInput]] 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/GraphQLWSOutput.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import caliban.interop.tapir.IsTapirSchema 4 | import com.github.plokhotnyuk.jsoniter_scala.core._ 5 | import com.github.plokhotnyuk.jsoniter_scala.macros._ 6 | 7 | case class GraphQLWSOutput(`type`: String, id: Option[String], payload: Option[ResponseValue]) 8 | 9 | object GraphQLWSOutput { 10 | implicit val jsoniterCodec: JsonValueCodec[GraphQLWSOutput] = JsonCodecMaker.make 11 | 12 | implicit def tapirSchema[F[_]: IsTapirSchema]: F[GraphQLWSOutput] = 13 | caliban.interop.tapir.schema.wsOutputSchema.asInstanceOf[F[GraphQLWSOutput]] 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/RootResolver.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | /** 4 | * A `root resolver` contains resolvers for the 3 types of operations allowed in GraphQL: queries, mutations and subscriptions. 5 | * 6 | * A `resolver` is a simple value of the case class describing the API. 7 | */ 8 | case class RootResolver[+Query, +Mutation, +Subscription]( 9 | queryResolver: Option[Query], 10 | mutationResolver: Option[Mutation], 11 | subscriptionResolver: Option[Subscription] 12 | ) 13 | 14 | object RootResolver { 15 | 16 | /** 17 | * Constructs a [[RootResolver]] with only a query resolver. 18 | */ 19 | def apply[Query](queryResolver: Query): RootResolver[Query, Unit, Unit] = 20 | RootResolver(Some(queryResolver), Option.empty[Unit], Option.empty[Unit]) 21 | 22 | /** 23 | * Constructs a [[RootResolver]] with a query resolver and a mutation resolver. 24 | */ 25 | def apply[Query, Mutation](queryResolver: Query, mutationResolver: Mutation): RootResolver[Query, Mutation, Unit] = 26 | RootResolver(Some(queryResolver), Some(mutationResolver), Option.empty[Unit]) 27 | 28 | /** 29 | * Constructs a [[RootResolver]] with a query resolver, a mutation resolver and a subscription resolver. 30 | */ 31 | def apply[Query, Mutation, Subscription]( 32 | queryResolver: Query, 33 | mutationResolver: Mutation, 34 | subscriptionResolver: Subscription 35 | ): RootResolver[Query, Mutation, Subscription] = 36 | RootResolver(Some(queryResolver), Some(mutationResolver), Some(subscriptionResolver)) 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/execution/Deferred.scala: -------------------------------------------------------------------------------- 1 | package caliban.execution 2 | 3 | import caliban.PathValue 4 | import caliban.schema.ReducedStep 5 | 6 | sealed trait Deferred[-R] { 7 | def path: List[PathValue] 8 | def label: Option[String] 9 | } 10 | 11 | case class DeferredFragment[-R]( 12 | path: List[PathValue], 13 | step: ReducedStep[R], 14 | label: Option[String] 15 | ) extends Deferred[R] 16 | 17 | case class DeferredStream[-R]( 18 | path: List[PathValue], 19 | step: ReducedStep.StreamStep[R], 20 | label: Option[String], 21 | startFrom: Int 22 | ) extends Deferred[R] 23 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/execution/ExecutionRequest.scala: -------------------------------------------------------------------------------- 1 | package caliban.execution 2 | 3 | import caliban.parsing.adt.OperationType 4 | 5 | case class ExecutionRequest( 6 | field: Field, 7 | operationType: OperationType, 8 | operationName: Option[String] 9 | ) 10 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/execution/FieldInfo.scala: -------------------------------------------------------------------------------- 1 | package caliban.execution 2 | 3 | import caliban.PathValue 4 | import caliban.introspection.adt.__Type 5 | import caliban.parsing.adt.Directive 6 | 7 | case class FieldInfo( 8 | name: String, 9 | details: Field, 10 | path: List[PathValue], 11 | directives: List[Directive] = Nil, 12 | parent: Option[__Type] 13 | ) 14 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/execution/Fragment.scala: -------------------------------------------------------------------------------- 1 | package caliban.execution 2 | 3 | import caliban.Value.{ BooleanValue, IntValue, StringValue } 4 | import caliban.parsing.adt.{ Directive, Directives } 5 | 6 | case class Fragment(name: Option[String], directives: List[Directive]) {} 7 | 8 | object Fragment { 9 | object IsDeferred { 10 | def unapply(fragment: Fragment): Option[Option[String]] = 11 | fragment.directives.collectFirst { 12 | case Directive(Directives.Defer, args, _, _) if args.get("if").forall { 13 | case BooleanValue(v) => v 14 | case _ => true 15 | } => 16 | args.get("label").collect { case StringValue(v) => v } 17 | } 18 | } 19 | } 20 | 21 | object IsStream { 22 | def unapply(field: Field): Option[(Option[String], Option[Int])] = 23 | field.directives.collectFirst { 24 | case Directive(Directives.Stream, args, _, _) if args.get("if").forall { 25 | case BooleanValue(v) => v 26 | case _ => true 27 | } => 28 | ( 29 | args.get("label").collect { case StringValue(v) => v }, 30 | args.get("initialCount").collect { case v: IntValue => v.toInt } 31 | ) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/execution/QueryExecution.scala: -------------------------------------------------------------------------------- 1 | package caliban.execution 2 | 3 | /** 4 | * Defines which type of parallelism to use when executing queries 5 | */ 6 | sealed trait QueryExecution { 7 | def tag: Int 8 | } 9 | 10 | object QueryExecution { 11 | 12 | /** 13 | * Run effectful fields sequentially. 14 | */ 15 | case object Sequential extends QueryExecution { 16 | final val tag = 0 17 | } 18 | 19 | /** 20 | * Run effectful fields in parallel (default). 21 | */ 22 | case object Parallel extends QueryExecution { 23 | final val tag = 1 24 | } 25 | 26 | /** 27 | * Run effectful fields sequentially but batch effects wrapped with `ZQuery.fromRequest`. 28 | * This mode is recommended when most of your effects are backed by ZQuery DataSources as it avoids forking unnecessary fibers. 29 | */ 30 | case object Batched extends QueryExecution { 31 | final val tag = 2 32 | } 33 | 34 | /** 35 | * Run effectful top-level fields in [[Parallel]] mode and nested fields in [[Batched]] mode. 36 | * This mode is recommended when most of your effects are backed by ZQuery DataSources but want to guarantee that top-level fields are always executed in parallel. 37 | */ 38 | case object Mixed extends QueryExecution { 39 | final val tag = 3 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/implicits.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import zio.Unsafe 4 | 5 | private object implicits { 6 | implicit val unsafe: Unsafe = Unsafe.unsafe(identity) 7 | } 8 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/interop/tapir/tapir.scala: -------------------------------------------------------------------------------- 1 | package caliban.interop.tapir 2 | 3 | import caliban._ 4 | import sttp.tapir.{ Schema, SchemaType } 5 | 6 | /** 7 | * This class is an implementation of the pattern described in https://blog.7mind.io/no-more-orphans.html 8 | * It makes it possible to mark circe dependency as optional and keep Encoders defined in the companion object. 9 | */ 10 | private[caliban] trait IsTapirSchema[F[_]] 11 | private[caliban] object IsTapirSchema { 12 | implicit val isTapirSchema: IsTapirSchema[Schema] = null 13 | } 14 | 15 | object schema { 16 | implicit lazy val requestSchema: Schema[GraphQLRequest] = 17 | sttp.tapir.Schema[GraphQLRequest](SchemaType.SString[GraphQLRequest]()) 18 | implicit def responseSchema[E]: Schema[GraphQLResponse[E]] = 19 | sttp.tapir.Schema[GraphQLResponse[E]](SchemaType.SString[GraphQLResponse[E]]()) 20 | implicit lazy val wsInputSchema: Schema[GraphQLWSInput] = 21 | sttp.tapir.Schema[GraphQLWSInput](SchemaType.SString[GraphQLWSInput]()) 22 | implicit lazy val wsOutputSchema: Schema[GraphQLWSOutput] = 23 | sttp.tapir.Schema[GraphQLWSOutput](SchemaType.SString[GraphQLWSOutput]()) 24 | implicit lazy val responseValueSchema: Schema[ResponseValue] = 25 | sttp.tapir.Schema[ResponseValue](SchemaType.SString[ResponseValue]()) 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/introspection/adt/__DeprecatedArgs.scala: -------------------------------------------------------------------------------- 1 | package caliban.introspection.adt 2 | 3 | case class __DeprecatedArgs(includeDeprecated: Option[Boolean] = None) 4 | 5 | object __DeprecatedArgs { 6 | val include: __DeprecatedArgs = __DeprecatedArgs(Some(true)) 7 | } 8 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/introspection/adt/__Directive.scala: -------------------------------------------------------------------------------- 1 | package caliban.introspection.adt 2 | 3 | import caliban.parsing.adt.Definition.TypeSystemDefinition.DirectiveDefinition 4 | 5 | case class __Directive( 6 | name: String, 7 | description: Option[String], 8 | locations: Set[__DirectiveLocation], 9 | args: __DeprecatedArgs => List[__InputValue], 10 | isRepeatable: Boolean 11 | ) { 12 | def toDirectiveDefinition: DirectiveDefinition = 13 | DirectiveDefinition( 14 | description, 15 | name, 16 | allArgs.map(_.toInputValueDefinition), 17 | isRepeatable, 18 | locations.map(_.toDirectiveLocation) 19 | ) 20 | 21 | lazy val allArgs: List[__InputValue] = 22 | args(__DeprecatedArgs.include) 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/introspection/adt/__EnumValue.scala: -------------------------------------------------------------------------------- 1 | package caliban.introspection.adt 2 | 3 | import caliban.Value.StringValue 4 | import caliban.parsing.adt.Definition.TypeSystemDefinition.TypeDefinition.EnumValueDefinition 5 | import caliban.parsing.adt.Directive 6 | import caliban.schema.Annotations.GQLExcluded 7 | 8 | case class __EnumValue( 9 | name: String, 10 | description: Option[String], 11 | isDeprecated: Boolean, 12 | deprecationReason: Option[String], 13 | @GQLExcluded directives: Option[List[Directive]] 14 | ) { 15 | def toEnumValueDefinition: EnumValueDefinition = 16 | EnumValueDefinition( 17 | description, 18 | name, 19 | (if (isDeprecated) 20 | List( 21 | Directive( 22 | "deprecated", 23 | List(deprecationReason.map(reason => "reason" -> StringValue(reason))).flatten.toMap 24 | ) 25 | ) 26 | else Nil) ++ directives.getOrElse(Nil) 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/introspection/adt/__Introspection.scala: -------------------------------------------------------------------------------- 1 | package caliban.introspection.adt 2 | 3 | case class __Introspection( 4 | __schema: __Schema, 5 | __type: __TypeArgs => Option[__Type] 6 | ) 7 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/introspection/adt/__Schema.scala: -------------------------------------------------------------------------------- 1 | package caliban.introspection.adt 2 | 3 | case class __Schema( 4 | description: Option[String], 5 | queryType: __Type, 6 | mutationType: Option[__Type], 7 | subscriptionType: Option[__Type], 8 | types: List[__Type], 9 | directives: List[__Directive] 10 | ) 11 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/introspection/adt/__TypeArgs.scala: -------------------------------------------------------------------------------- 1 | package caliban.introspection.adt 2 | 3 | case class __TypeArgs(name: String) 4 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/introspection/adt/__TypeKind.scala: -------------------------------------------------------------------------------- 1 | package caliban.introspection.adt 2 | 3 | sealed trait __TypeKind 4 | 5 | object __TypeKind { 6 | case object SCALAR extends __TypeKind 7 | case object OBJECT extends __TypeKind 8 | case object INTERFACE extends __TypeKind 9 | case object UNION extends __TypeKind 10 | case object ENUM extends __TypeKind 11 | case object INPUT_OBJECT extends __TypeKind 12 | case object LIST extends __TypeKind 13 | case object NON_NULL extends __TypeKind 14 | 15 | implicit val kindOrdering: Ordering[__TypeKind] = Ordering 16 | .by[__TypeKind, Int] { 17 | case __TypeKind.SCALAR => 1 18 | case __TypeKind.NON_NULL => 2 19 | case __TypeKind.LIST => 3 20 | case __TypeKind.UNION => 4 21 | case __TypeKind.ENUM => 5 22 | case __TypeKind.INPUT_OBJECT => 6 23 | case __TypeKind.INTERFACE => 7 24 | case __TypeKind.OBJECT => 8 25 | } 26 | 27 | implicit val typeOrdering: Ordering[__Type] = 28 | Ordering.by(o => (o.kind, o.name.getOrElse(""))) 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/parsing/adt/LocationInfo.scala: -------------------------------------------------------------------------------- 1 | package caliban.parsing.adt 2 | 3 | import caliban.ResponseValue 4 | import caliban.ResponseValue.ObjectValue 5 | import caliban.Value.IntValue 6 | 7 | case class LocationInfo(column: Int, line: Int) { 8 | def toResponseValue: ResponseValue = 9 | ObjectValue(List("line" -> IntValue(line), "column" -> IntValue(column))) 10 | } 11 | 12 | object LocationInfo { 13 | val origin: LocationInfo = LocationInfo(0, 0) 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/parsing/adt/OperationType.scala: -------------------------------------------------------------------------------- 1 | package caliban.parsing.adt 2 | 3 | sealed trait OperationType extends Serializable 4 | 5 | object OperationType { 6 | case object Query extends OperationType 7 | case object Mutation extends OperationType 8 | case object Subscription extends OperationType 9 | } 10 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/parsing/adt/Selection.scala: -------------------------------------------------------------------------------- 1 | package caliban.parsing.adt 2 | 3 | import caliban.InputValue 4 | import caliban.parsing.adt.Type.NamedType 5 | 6 | sealed trait Selection extends Serializable { 7 | @transient final override lazy val hashCode: Int = super.hashCode() 8 | } 9 | 10 | object Selection { 11 | 12 | case class Field( 13 | alias: Option[String], 14 | name: String, 15 | arguments: Map[String, InputValue], 16 | directives: List[Directive], 17 | selectionSet: List[Selection], 18 | index: Int 19 | ) extends Selection 20 | 21 | case class FragmentSpread(name: String, directives: List[Directive]) extends Selection 22 | 23 | case class InlineFragment( 24 | typeCondition: Option[NamedType], 25 | dirs: List[Directive], 26 | selectionSet: List[Selection] 27 | ) extends Selection 28 | 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/parsing/adt/Type.scala: -------------------------------------------------------------------------------- 1 | package caliban.parsing.adt 2 | 3 | import scala.annotation.tailrec 4 | 5 | sealed trait Type extends Serializable { self => 6 | val nonNull: Boolean 7 | 8 | final def nullable: Boolean = !nonNull 9 | 10 | override def toString: String = self match { 11 | case Type.NamedType(name, nonNull) => if (nonNull) s"$name!" else name 12 | case Type.ListType(ofType, nonNull) => if (nonNull) s"[$ofType]!" else s"[$ofType]" 13 | } 14 | 15 | final def toNullable: Type = 16 | self match { 17 | case Type.NamedType(name, _) => Type.NamedType(name, nonNull = false) 18 | case Type.ListType(ofType, _) => Type.ListType(ofType, nonNull = false) 19 | } 20 | 21 | final def toNonNullable: Type = 22 | self match { 23 | case Type.NamedType(name, _) => Type.NamedType(name, nonNull = true) 24 | case Type.ListType(ofType, _) => Type.ListType(ofType, nonNull = true) 25 | } 26 | } 27 | 28 | object Type { 29 | 30 | case class NamedType(name: String, nonNull: Boolean) extends Type 31 | case class ListType(ofType: Type, nonNull: Boolean) extends Type 32 | 33 | @tailrec 34 | def innerType(t: Type): String = t match { 35 | case NamedType(name, _) => name 36 | case ListType(ofType, _) => innerType(ofType) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/parsing/adt/VariableDefinition.scala: -------------------------------------------------------------------------------- 1 | package caliban.parsing.adt 2 | 3 | import caliban.InputValue 4 | 5 | case class VariableDefinition( 6 | name: String, 7 | variableType: Type, 8 | defaultValue: Option[InputValue], 9 | directives: List[Directive] 10 | ) 11 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/parsing/parsers/NumberParsers.scala: -------------------------------------------------------------------------------- 1 | package caliban.parsing.parsers 2 | 3 | import caliban.Value._ 4 | import fastparse._ 5 | 6 | private[caliban] trait NumberParsers extends StringParsers { 7 | def negativeSign(implicit ev: P[Any]): P[Unit] = StringIn("-") 8 | def nonZeroDigit(implicit ev: P[Any]): P[Unit] = CharIn("1-9") 9 | def digit(implicit ev: P[Any]): P[Unit] = CharIn("0-9") 10 | def integerPart(implicit ev: P[Any]): P[Unit] = 11 | negativeSign.? ~~ ("0" | (nonZeroDigit ~~ digit.repX)) 12 | 13 | def intValue(implicit ev: P[Any]): P[IntValue] = integerPart.!.map(IntValue.fromStringUnsafe) 14 | 15 | def sign(implicit ev: P[Any]): P[Unit] = StringIn("-", "+") 16 | def exponentIndicator(implicit ev: P[Any]): P[Unit] = CharIn("eE") 17 | def exponentPart(implicit ev: P[Any]): P[Unit] = exponentIndicator ~~ sign.? ~~ digit.repX(1) 18 | def fractionalPart(implicit ev: P[Any]): P[Unit] = "." ~~ digit.repX(1) 19 | def floatValue(implicit ev: P[Any]): P[FloatValue] = 20 | ( 21 | integerPart ~~ (fractionalPart | exponentPart | (fractionalPart ~~ exponentPart)) 22 | ).!.map(FloatValue.fromStringUnsafe) 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/parsing/parsers/ValueParsers.scala: -------------------------------------------------------------------------------- 1 | package caliban.parsing.parsers 2 | 3 | import caliban.InputValue 4 | import caliban.InputValue._ 5 | import caliban.Value._ 6 | import fastparse._ 7 | 8 | import scala.annotation.nowarn 9 | 10 | @nowarn("msg=NoWhitespace") // False positive warning in Scala 2.12.x 11 | private[caliban] trait ValueParsers extends NumberParsers { 12 | def booleanValue(implicit ev: P[Any]): P[BooleanValue] = 13 | StringIn("true", "false").!.map(v => BooleanValue(v.toBoolean)) 14 | 15 | def nullValue(implicit ev: P[Any]): P[InputValue] = LiteralStr("null").map(_ => NullValue) 16 | def enumValue(implicit ev: P[Any]): P[InputValue] = name.map(EnumValue.apply) 17 | def listValue(implicit ev: P[Any]): P[ListValue] = ("[" ~/ value.rep ~ "]").map(values => ListValue(values.toList)) 18 | 19 | def objectField(implicit ev: P[Any]): P[(String, InputValue)] = name ~ ":" ~/ value 20 | def objectValue(implicit ev: P[Any]): P[ObjectValue] = 21 | ("{" ~/ objectField.rep ~ "}").map(values => ObjectValue(values.toMap)) 22 | 23 | def variableValue(implicit ev: P[Any]): P[VariableValue] = ("$" ~/ name).map(VariableValue.apply) 24 | 25 | def value(implicit ev: P[Any]): P[InputValue] = 26 | floatValue | intValue | booleanValue | stringValue | nullValue | enumValue | listValue | objectValue | variableValue 27 | 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/relay/Base64Cursor.scala: -------------------------------------------------------------------------------- 1 | package caliban.relay 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * A cursor implementation that models an index/offset as an 7 | * opaque base64 cursor. 8 | */ 9 | case class Base64Cursor(value: Int) 10 | 11 | object Base64Cursor { 12 | import java.util.Base64 13 | lazy val encoder = Base64.getEncoder() 14 | lazy val decoder = Base64.getDecoder() 15 | 16 | private val prefix = "cursor:" 17 | 18 | implicit val cursor: Cursor[Base64Cursor] = new Cursor[Base64Cursor] { 19 | type T = Int 20 | def encode(a: Base64Cursor): String = 21 | encoder.encodeToString(s"$prefix${a.value}".getBytes("UTF-8")) 22 | 23 | def decode(raw: String): Either[String, Base64Cursor] = 24 | Try({ 25 | val bytes = decoder.decode(raw) 26 | val s = new String(bytes, "UTF-8") 27 | if (s.startsWith(prefix)) { 28 | Base64Cursor(s.replaceFirst(prefix, "").toInt) 29 | } else { 30 | throw new Throwable("invalid cursor") 31 | } 32 | }).toEither.left.map(_.getMessage()) 33 | 34 | def value(cursor: Base64Cursor): Int = cursor.value 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/relay/Cursor.scala: -------------------------------------------------------------------------------- 1 | package caliban.relay 2 | 3 | /** 4 | * A trait representing an abstract Relay Connection cursor. 5 | */ 6 | trait Cursor[A] { 7 | type T 8 | def encode(a: A): String 9 | def decode(s: String): Either[String, A] 10 | def value(cursor: A): T 11 | } 12 | 13 | object Cursor { 14 | def apply[A](implicit c: Cursor[A]): Cursor[A] = c 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/schema/ObjectFieldResolver.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | import caliban.Scala3Annotations.static 4 | import caliban.schema.Step.{ NullStep, ObjectStep } 5 | 6 | import scala.collection.compat._ 7 | import scala.collection.mutable 8 | 9 | final private class ObjectFieldResolver[R, A] private ( 10 | name: String, 11 | fields: mutable.HashMap[String, A => Step[R]] 12 | ) { 13 | import ObjectFieldResolver._ 14 | 15 | private def getFieldStep(value: A): String => Step[R] = 16 | fields.getOrElse(_, NullStepFn0())(value) 17 | 18 | def resolve(value: A): Step[R] = ObjectStep(name, getFieldStep(value)) 19 | } 20 | 21 | private object ObjectFieldResolver { 22 | @static private val NullStepFn: Any => Step[Any] = _ => NullStep 23 | @static private val NullStepFn0: () => Any => Step[Any] = () => NullStepFn 24 | 25 | def apply[R, A](objectName: String, fields: Iterable[(String, A => Step[R])]): ObjectFieldResolver[R, A] = 26 | // NOTE: mutable.HashMap is about twice as fast than immutable.HashMap for .get 27 | new ObjectFieldResolver(objectName, mutable.HashMap.from(fields)) 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/schema/Operation.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | import caliban.introspection.adt.__Type 4 | 5 | case class Operation[-R](opType: __Type, plan: Step[R]) { 6 | def |+|[R1 <: R](that: Operation[R1]): Operation[R1] = 7 | Operation(opType |+| that.opType, Step.mergeRootSteps(plan, that.plan)) 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/schema/RootSchema.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | case class RootSchema[-R](query: Operation[R], mutation: Option[Operation[R]], subscription: Option[Operation[R]]) 4 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/schema/RootType.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | import caliban.introspection.adt.{ __Directive, __Type } 4 | import caliban.schema.Types.collectTypes 5 | 6 | case class RootType( 7 | queryType: __Type, 8 | mutationType: Option[__Type], 9 | subscriptionType: Option[__Type], 10 | additionalTypes: List[__Type] = List.empty, 11 | additionalDirectives: List[__Directive] = List.empty, 12 | description: Option[String] = None 13 | ) { 14 | private val primitiveTypes: List[__Type] = List(Types.boolean, Types.int, Types.float, Types.string) 15 | 16 | val types: Map[String, __Type] = { 17 | val init = additionalTypes.foldLeft(List.empty[__Type]) { case (acc, t) => collectTypes(t, acc) } 18 | (init ++ 19 | primitiveTypes ++ 20 | collectTypes(queryType, init) ++ 21 | mutationType.fold(List.empty[__Type])(collectTypes(_, init)) ++ 22 | subscriptionType.fold(List.empty[__Type])(collectTypes(_, init))) 23 | .groupBy(t => (t.name, t.kind, t.origin)) 24 | .flatMap(_._2.headOption) 25 | .map(t => t.name.getOrElse("") -> t) 26 | .toMap 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/schema/SubscriptionSchema.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | import zio.stream.ZStream 4 | 5 | /** 6 | * Typeclass used to guarantee that the Subscriptions type is either `Unit` or a case class with `zio.stream.ZStream` for fields. 7 | */ 8 | trait SubscriptionSchema[T] 9 | 10 | object SubscriptionSchema extends SubscriptionSchemaDerivation { 11 | 12 | implicit val unitSubscriptionSchema: SubscriptionSchema[Unit] = new SubscriptionSchema[Unit] {} 13 | implicit def streamSubscriptionSchema[R, E, A]: SubscriptionSchema[ZStream[R, E, A]] = 14 | new SubscriptionSchema[ZStream[R, E, A]] {} 15 | implicit def functionSubscriptionSchema[R, E, A, ARG]: SubscriptionSchema[ARG => ZStream[R, E, A]] = 16 | new SubscriptionSchema[ARG => ZStream[R, E, A]] {} 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/uploads/Uploads.scala: -------------------------------------------------------------------------------- 1 | package caliban.uploads 2 | 3 | import zio.stream.ZStream 4 | import zio.{ Chunk, UIO, ULayer, URIO, ZIO, ZLayer } 5 | 6 | trait Uploads { 7 | def stream(name: String): ZStream[Any, Throwable, Byte] 8 | def file(name: String): ZIO[Any, Nothing, Option[FileMeta]] 9 | } 10 | 11 | object Uploads { 12 | val empty: ULayer[Uploads] = 13 | ZLayer.succeed(new Uploads { 14 | def stream(name: String): ZStream[Any, Throwable, Byte] = ZStream.empty 15 | def file(name: String): UIO[Option[FileMeta]] = ZIO.none 16 | }) 17 | 18 | def stream(name: String): ZStream[Uploads, Throwable, Byte] = 19 | ZStream.serviceWithStream(_.stream(name)) 20 | 21 | def fileMeta(name: String): URIO[Uploads, Option[FileMeta]] = 22 | ZIO.serviceWithZIO(_.file(name)) 23 | 24 | def handler(fileHandle: String => UIO[Option[FileMeta]]): UIO[Uploads] = 25 | ZIO 26 | .succeed(new Uploads { 27 | def stream(name: String): ZStream[Any, Throwable, Byte] = 28 | for { 29 | ref <- ZStream.fromZIOOption(fileHandle(name).some) 30 | bytes <- ZStream.fromChunk(Chunk.fromArray(ref.bytes)) 31 | } yield bytes 32 | 33 | def file(name: String): UIO[Option[FileMeta]] = 34 | fileHandle(name) 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/validation/package.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import caliban.parsing.adt.Definition.ExecutableDefinition.{ FragmentDefinition, OperationDefinition } 4 | import caliban.introspection.adt.{ __Field, __Type } 5 | import caliban.parsing.SourceMapper 6 | import caliban.parsing.adt.{ Document, Selection, VariableDefinition } 7 | import caliban.parsing.adt.Selection.Field 8 | import caliban.schema.{ RootType, Types } 9 | 10 | package object validation { 11 | case class SelectedField( 12 | parentType: __Type, 13 | selection: Field, 14 | fieldDef: __Field 15 | ) { 16 | final override lazy val hashCode: Int = super.hashCode() 17 | } 18 | 19 | type FieldMap = Map[String, Set[SelectedField]] 20 | 21 | case class Context( 22 | document: Document, 23 | rootType: RootType, 24 | operations: List[OperationDefinition], 25 | fragments: Map[String, FragmentDefinition], 26 | selectionSets: List[Selection], 27 | variables: Map[String, InputValue] 28 | ) { 29 | lazy val variableDefinitions: Map[String, VariableDefinition] = 30 | operations.flatMap(_.variableDefinitions.map(d => d.name -> d)).toMap 31 | } 32 | 33 | object Context { 34 | val empty: Context = 35 | Context(Document(Nil, SourceMapper.empty), RootType(Types.boolean, None, None), Nil, Map.empty, Nil, Map.empty) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/wrappers/DeferSupport.scala: -------------------------------------------------------------------------------- 1 | package caliban.wrappers 2 | 3 | import caliban.GraphQLAspect 4 | import caliban.execution.Feature 5 | import caliban.introspection.adt.__Directive 6 | 7 | object DeferSupport { 8 | @deprecated("Use Feature.Defer.directives instead", "2.9.0") 9 | private[caliban] val deferDirective: __Directive = Feature.Defer.directives.head 10 | 11 | @deprecated("Use IncrementalDelivery.defer instead", "2.9.0") 12 | val defer: GraphQLAspect[Nothing, Any] = IncrementalDelivery.defer 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/scala/caliban/ws/package.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import zio.stream.Stream 4 | 5 | package object ws { 6 | type Pipe[A, B] = Stream[Throwable, A] => Stream[Throwable, B] 7 | type CalibanPipe = Pipe[GraphQLWSInput, Either[GraphQLWSClose, GraphQLWSOutput]] 8 | } 9 | -------------------------------------------------------------------------------- /core/src/test/resources/document-tests/kitchen-sink-compact.graphql: -------------------------------------------------------------------------------- 1 | directive @mydirective(input:MyDirectiveInput!) on SCHEMA directive @key(fields:String!) on OBJECT|INTERFACE directive @shareable on UNION directive @tag on OBJECT|FIELD_DEFINITION|INTERFACE|UNION|ENUM|INPUT_OBJECT schema@mydirective(input:{name:"test"}){query:Query,mutation:Mutation,subscription:Subscription} union Event@shareable=AgeChanged|NameChanged|RegistrationChanged input MyDirectiveInput{name:String!} input MyInput{name:String! age:Int=18 registered:Boolean friends:[String]!}interface Node{id:ID!@deprecated(reason:"Use `uuid` instead")}interface User@tag{name:String! age:Int! registered:Boolean! friends:[User!]! bestFriend:User friendsCount:Int!}type AgeChanged{age:Int!}type Me implements User & Node@key(fields:"id"){id:ID! name:String! age:Int! registered:Boolean! friends:[User!]! bestFriend:User friendsCount:Int!}type Mutation@tag{goodbye(input:MyInput!={name:"test"}@deprecated(reason:"Bad")):String@tag}type NameChanged{name:String!}type Query{hello(name:String!=" world",after:Int):User!}type RegistrationChanged{registered:Boolean!}type Subscription{event:Event!} -------------------------------------------------------------------------------- /core/src/test/resources/document-tests/kitchen-sink-query.graphql: -------------------------------------------------------------------------------- 1 | query { 2 | id 3 | name 4 | } 5 | 6 | mutation { 7 | thatThing 8 | thatThingWithArgs(input: {id: 1, name: "test"}) { 9 | id 10 | name 11 | } 12 | } 13 | 14 | mutation WithArgs($args: InputType!) { 15 | thatThingWithArgs(input: $args) { 16 | id @include(if: false) 17 | name 18 | ... on SomeType @skip(if: true) { 19 | someField 20 | } 21 | ...SomeTypeFragment 22 | } 23 | } 24 | 25 | subscription { 26 | event { 27 | id 28 | name 29 | } 30 | } 31 | 32 | fragment SomeTypeFragment on SomeType { 33 | someField 34 | } -------------------------------------------------------------------------------- /core/src/test/resources/document-tests/query-compact.graphql: -------------------------------------------------------------------------------- 1 | query Hello($name:String!){hello(name:$name,inline:{age:42})@skip(if:true){message ...on Hello{message} ...FragmentHello}} fragment FragmentHello on Hello{test} -------------------------------------------------------------------------------- /core/src/test/scala-2/caliban/schema/ArgBuilderScala2Spec.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | import caliban.Value.StringValue 4 | import caliban.schema.ArgBuilder.auto._ 5 | import zio.test.Assertion._ 6 | import zio.test._ 7 | 8 | import java.util.UUID 9 | 10 | object ArgBuilderScala2Spec extends ZIOSpecDefault { 11 | def spec = suite("ArgBuilderScala2")( 12 | suite("AnyVal") { 13 | test("ArgBuilder that extends AnyVal") { 14 | val id = UUID.randomUUID() 15 | val value = ArgBuilder[UUIDId].build(StringValue(id.toString)) 16 | assert(value)(isRight(equalTo(UUIDId(id)))) 17 | } 18 | } 19 | ) 20 | 21 | trait Ids[T] extends Any { 22 | self: AnyVal => 23 | def value: T 24 | } 25 | 26 | final case class UUIDId(value: UUID) extends AnyVal with Ids[UUID] 27 | } 28 | -------------------------------------------------------------------------------- /core/src/test/scala-2/caliban/validation/InputArgumentSpecInterop.scala: -------------------------------------------------------------------------------- 1 | package caliban.validation 2 | 3 | object InputArgumentSpecInterop { 4 | import InputArgumentSpec._ 5 | 6 | case class Query( 7 | bool: BoolArg => String = _ => "result", 8 | boolNonNull: BoolArgNonNull => String = _ => "result", 9 | float: FloatArg => String = _ => "result", 10 | int: IntArg => String = _ => "result", 11 | list: ListArg => String = _ => "result", 12 | listInt: ListIntArg => Option[List[Option[Int]]] = _.input, 13 | listListInt: ListListIntArg => Option[List[Option[List[Option[Int]]]]] = _.input, 14 | string: StringArg => String = _ => "result", 15 | `enum`: EnumArg => String = _ => "result", 16 | input: InputArg => String = _ => "result", 17 | exampleObject: ExampleObjectArg => Option[ExampleObject] = _.input 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /core/src/test/scala-3/caliban/schema/ExcludedFieldSpec.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | import caliban.schema.Annotations.GQLExcluded 4 | 5 | /** Compile-time "test", which ensures that we don't need to define a schema for fields annotated with @GQLExcluded */ 6 | object ExcludedFieldSpec { 7 | final case class Foo(value: String) 8 | final case class Bar(value: String, @GQLExcluded foo: Foo, num: Int) 9 | 10 | given Schema[Any, Bar] = Schema.derived 11 | } 12 | -------------------------------------------------------------------------------- /core/src/test/scala-3/caliban/validation/InputArgumentSpecInterop.scala: -------------------------------------------------------------------------------- 1 | package caliban.validation 2 | 3 | object InputArgumentSpecInterop { 4 | import InputArgumentSpec._ 5 | 6 | case class Query( 7 | bool: BoolArg => String = _ => "result", 8 | boolNonNull: BoolArgNonNull => String = _ => "result", 9 | float: FloatArg => String = _ => "result", 10 | int: IntArg => String = _ => "result", 11 | list: ListArg => String = _ => "result", 12 | listInt: ListIntArg => Option[List[Option[Int]]] = _.input, 13 | // does not compile on Scala 3 14 | // listListInt: ListListIntArg => Option[List[Option[List[Option[Int]]]]] = _.input, 15 | string: StringArg => String = _ => "result", 16 | `enum`: EnumArg => String = _ => "result", 17 | input: InputArg => String = _ => "result", 18 | exampleObject: ExampleObjectArg => Option[ExampleObject] = _.input 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /core/src/test/scala/caliban/FragmentSchema.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import caliban.schema.Annotations._ 4 | 5 | object FragmentSchema { 6 | 7 | @GQLDescription("Queries") 8 | case class Query( 9 | bar: Option[Bar], 10 | foo: List[Foo] 11 | ) 12 | 13 | case class Foo( 14 | id: String 15 | ) 16 | case class Bar( 17 | id: String, 18 | baz: Option[Baz] 19 | ) 20 | 21 | case class Baz( 22 | id: String, 23 | foo: Option[Foo] 24 | ) 25 | 26 | val resolverFooBar: RootResolver[Query, Unit, Unit] = RootResolver( 27 | Query( 28 | bar = Some(Bar("1", Some(Baz("2", Some(Foo("3")))))), 29 | foo = List(Foo("1"), Foo("2")) 30 | ) 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /core/src/test/scala/caliban/RenderingSpecSchema.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import caliban.schema.Annotations.GQLDescription 4 | 5 | object RenderingSpecSchema { 6 | 7 | case class UserTest(name: String, @GQLDescription("field-description") age: Int) 8 | case class UserComplex(id: Int, user: UserTest) 9 | 10 | case class UserParams(nameLike: String, @GQLDescription("is user active currently") active: Boolean) 11 | 12 | case class QueryTest(allUsers: () => List[UserTest]) 13 | case class MutationTest( 14 | id: UserComplex => Boolean, 15 | fetch: UserParams => Boolean 16 | ) 17 | 18 | val resolverSchema = RootResolver( 19 | QueryTest(() => List()), 20 | MutationTest(c => true, params => true) 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /core/src/test/scala/caliban/interop/jsoniter/GraphQLWSInputJsoniterSpec.scala: -------------------------------------------------------------------------------- 1 | package caliban.interop.jsoniter 2 | 3 | import caliban.{ GraphQLWSInput, InputValue, TestUtils, Value } 4 | import com.github.plokhotnyuk.jsoniter_scala.core._ 5 | import zio.test.Assertion.equalTo 6 | import zio.test.{ assert, assertTrue, ZIOSpecDefault } 7 | 8 | object GraphQLWSInputJsoniterSpec extends ZIOSpecDefault { 9 | override def spec = 10 | suite("GraphQLWSInputJsoniterSpec")( 11 | test("can be parsed from JSON by jsoniter") { 12 | val request = 13 | """{"id":"id","type":"some type","payload":{"field":"yo"}}""" 14 | 15 | val res = readFromString[GraphQLWSInput](request) 16 | assert(res)( 17 | equalTo( 18 | GraphQLWSInput( 19 | `type` = "some type", 20 | id = Some("id"), 21 | payload = Some(InputValue.ObjectValue(Map("field" -> Value.StringValue("yo")))) 22 | ) 23 | ) 24 | ) 25 | }, 26 | test("can encode to JSON by jsoniter") { 27 | val res = GraphQLWSInput( 28 | `type` = "some type", 29 | id = Some("id"), 30 | payload = Some(InputValue.ObjectValue(Map("field" -> Value.StringValue("yo")))) 31 | ) 32 | 33 | assertTrue(writeToString(res) == """{"type":"some type","id":"id","payload":{"field":"yo"}}""") 34 | } 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /core/src/test/scala/caliban/interop/jsoniter/GraphQLWSOutputJsoniterSpec.scala: -------------------------------------------------------------------------------- 1 | package caliban.interop.jsoniter 2 | 3 | import caliban.{ GraphQLWSOutput, ResponseValue, TestUtils, Value } 4 | import com.github.plokhotnyuk.jsoniter_scala.core._ 5 | import zio.test.Assertion.equalTo 6 | import zio.test.{ assert, assertTrue, ZIOSpecDefault } 7 | 8 | object GraphQLWSOutputJsoniterSpec extends ZIOSpecDefault { 9 | override def spec = 10 | suite("GraphQLWSOutputJsoniter")( 11 | test("can be parsed from JSON by jsoniter") { 12 | val request = 13 | """{"id":"id","type":"some type","payload":{"field":"yo"}}""" 14 | 15 | val res = readFromString[GraphQLWSOutput](request) 16 | assert(res)( 17 | equalTo( 18 | GraphQLWSOutput( 19 | `type` = "some type", 20 | id = Some("id"), 21 | payload = Some(ResponseValue.ObjectValue(List("field" -> Value.StringValue("yo")))) 22 | ) 23 | ) 24 | ) 25 | }, 26 | test("can encode to JSON by jsoniter") { 27 | val res = GraphQLWSOutput( 28 | `type` = "some type", 29 | id = Some("id"), 30 | payload = Some(ResponseValue.ObjectValue(List("field" -> Value.StringValue("yo")))) 31 | ) 32 | 33 | assertTrue(writeToString(res) == """{"type":"some type","id":"id","payload":{"field":"yo"}}""") 34 | } 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /core/src/test/scala/caliban/parsing/DocumentSpec.scala: -------------------------------------------------------------------------------- 1 | package caliban.parsing 2 | 3 | import caliban.TestUtils 4 | import org.apache.commons.lang3.SerializationUtils 5 | import zio.test._ 6 | 7 | object DocumentSpec extends ZIOSpecDefault { 8 | def spec = suite("DocumentSpec")( 9 | test("is serializable") { 10 | for { 11 | doc1 <- Parser.parseQuery(TestUtils.introspectionQuery) 12 | doc2 = SerializationUtils.roundtrip(doc1) 13 | } yield assertTrue(doc1 == doc2) 14 | } 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /core/src/test/scala/caliban/parsing/SourceMapperSpec.scala: -------------------------------------------------------------------------------- 1 | package caliban.parsing 2 | 3 | import caliban.parsing.adt.LocationInfo 4 | import zio.test._ 5 | 6 | object SourceMapperSpec extends ZIOSpecDefault { 7 | 8 | override def spec = 9 | suite("SourceMapper")( 10 | test("should not throw IndexOutOfBounds") { 11 | assertTrue(SourceMapper("").getLocation(100) == LocationInfo(101, 1)) 12 | }, 13 | test("should map correctly to the source location") { 14 | val sm = SourceMapper(""" 15 | |a 16 | |b 17 | |""".stripMargin) 18 | assertTrue(sm.getLocation(3) == LocationInfo(1, 3)) 19 | } 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostdogpr/caliban/24a568744f6708a4f400d7f8f5f158b002f29806/docs/.nojekyll -------------------------------------------------------------------------------- /docs/assets/img/search.83621669.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/assets/js/19.2a696a53.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[19],{271:function(t,e,n){},273:function(t,e,n){"use strict";n.r(e);var s={name:"DropdownTransition",methods:{setHeight(t){t.style.height=t.scrollHeight+"px"},unsetHeight(t){t.style.height=""}}},i=(n(274),n(25)),o=Object(i.a)(s,(function(){return(0,this._self._c)("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.default=o.exports},274:function(t,e,n){"use strict";n(271)}}]); -------------------------------------------------------------------------------- /docs/assets/js/20.89f58454.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[20],{289:function(t,c,n){},301:function(t,c,n){"use strict";n(289)},314:function(t,c,n){"use strict";n.r(c);n(301);var i=n(25),s=Object(i.a)({},(function(){var t=this,c=t._self._c;return c("div",{staticClass:"sidebar-button",on:{click:function(c){return t.$emit("toggle-sidebar")}}},[c("svg",{staticClass:"icon",attrs:{xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",role:"img",viewBox:"0 0 448 512"}},[c("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);c.default=s.exports}}]); -------------------------------------------------------------------------------- /docs/assets/js/22.723716f0.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[22],{333:function(t,n,s){"use strict";s.r(n);var e=s(25),o=Object(e.a)({},(function(){return(0,this._self._c)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);n.default=o.exports}}]); -------------------------------------------------------------------------------- /docs/assets/js/4.119e5097.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[4],{308:function(t,e,n){},321:function(t,e,n){"use strict";n(308)},332:function(t,e,n){"use strict";n.r(e);var i={functional:!0,props:{type:{type:String,default:"tip"},text:String,vertical:{type:String,default:"top"}},render:(t,{props:e,slots:n})=>t("span",{class:["badge",e.type],style:{verticalAlign:e.vertical}},e.text||n().default)},p=(n(321),n(25)),l=Object(p.a)(i,void 0,void 0,!1,null,"15b7b770",null);e.default=l.exports}}]); -------------------------------------------------------------------------------- /docs/assets/js/5.635a7794.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[5],{309:function(t,e,a){},322:function(t,e,a){"use strict";a(309)},328:function(t,e,a){"use strict";a.r(e);var s={name:"CodeBlock",props:{title:{type:String,required:!0},active:{type:Boolean,default:!1}},mounted(){this.$parent&&this.$parent.loadTabs&&this.$parent.loadTabs()}},i=(a(322),a(25)),n=Object(i.a)(s,(function(){return(0,this._self._c)("div",{staticClass:"theme-code-block",class:{"theme-code-block__active":this.active}},[this._t("default")],2)}),[],!1,null,"759a7d02",null);e.default=n.exports}}]); -------------------------------------------------------------------------------- /docs/assets/js/7.442dabf9.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[7],{330:function(t,e,s){"use strict";s.r(e);const o=["There's nothing here.","How did we get here?","That's a Four-Oh-Four.","Looks like we've got some broken links."];var n={methods:{getMsg:()=>o[Math.floor(Math.random()*o.length)]}},h=s(25),i=Object(h.a)(n,(function(){var t=this._self._c;return t("div",{staticClass:"theme-container"},[t("div",{staticClass:"theme-default-content"},[t("h1",[this._v("404")]),this._v(" "),t("blockquote",[this._v(this._s(this.getMsg()))]),this._v(" "),t("RouterLink",{attrs:{to:"/"}},[this._v("\n Take me home.\n ")])],1)])}),[],!1,null,null,null);e.default=i.exports}}]); -------------------------------------------------------------------------------- /docs/caliban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostdogpr/caliban/24a568744f6708a4f400d7f8f5f158b002f29806/docs/caliban.png -------------------------------------------------------------------------------- /examples/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | # Used by the Play example -------------------------------------------------------------------------------- /examples/src/main/resources/gateway/gateway.js: -------------------------------------------------------------------------------- 1 | const {ApolloServer} = require('apollo-server'); 2 | const {ApolloGateway} = require('@apollo/gateway'); 3 | 4 | const gateway = new ApolloGateway({ 5 | serviceList: [ 6 | { name: 'episodes', url: 'http://localhost:8088/api/graphql'}, 7 | { name: 'characters', url: 'http://localhost:8089/api/graphql' } 8 | ], 9 | debug: true 10 | }); 11 | 12 | (async () => { 13 | const server = new ApolloServer({ 14 | gateway, 15 | subscriptions: false 16 | }); 17 | 18 | server.listen().then(({url}) => { 19 | console.log(`🚀 Server ready at ${url}`) 20 | }) 21 | })(); -------------------------------------------------------------------------------- /examples/src/main/resources/gateway/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "federated-gateway", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "gateway.js", 6 | "scripts": { 7 | "start": "node gateway.js" 8 | }, 9 | "keywords": [], 10 | "license": "MIT", 11 | "dependencies": { 12 | "apollo-server": "3.6.7", 13 | "@apollo/gateway": "~0.50.1", 14 | "graphql": "15.8.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/src/main/resources/gateway_v2/gateway.js: -------------------------------------------------------------------------------- 1 | import { ApolloServer } from '@apollo/server'; 2 | import { ApolloServerPluginInlineTrace } from '@apollo/server/plugin/inlineTrace' 3 | import { ApolloGateway, IntrospectAndCompose } from '@apollo/gateway'; 4 | import {startStandaloneServer} from "@apollo/server/standalone"; 5 | 6 | const gateway = new ApolloGateway({ 7 | supergraphSdl: new IntrospectAndCompose({ 8 | subgraphs: [ 9 | { name: 'episodes', url: 'http://localhost:8088/api/graphql'}, 10 | { name: 'characters', url: 'http://localhost:8089/api/graphql' } 11 | ] 12 | }), 13 | debug: true 14 | }); 15 | 16 | (async () => { 17 | const server = new ApolloServer({ 18 | gateway, 19 | subscriptions: false, 20 | plugins: [ 21 | ApolloServerPluginInlineTrace() 22 | ] 23 | }); 24 | 25 | const { url } = await startStandaloneServer(server, { 26 | listen: { port: 4000 } 27 | }) 28 | 29 | console.log(`🚀 Server ready at ${url}`) 30 | })(); -------------------------------------------------------------------------------- /examples/src/main/resources/gateway_v2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "federated-gateway", 3 | "version": "1.0.0", 4 | "description": "", 5 | "type": "module", 6 | "main": "gateway.js", 7 | "scripts": { 8 | "start": "node gateway.js" 9 | }, 10 | "keywords": [], 11 | "license": "MIT", 12 | "dependencies": { 13 | "@apollo/gateway": "2.8.0", 14 | "@apollo/server": "4.10.4", 15 | "graphql": "16.8.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/src/main/scala/example/ExampleData.scala: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import example.ExampleData.Origin.{ BELT, EARTH, MARS } 4 | import example.ExampleData.Role.{ Captain, Engineer, Mechanic, Pilot } 5 | 6 | object ExampleData { 7 | 8 | sealed trait Origin 9 | 10 | object Origin { 11 | case object EARTH extends Origin 12 | case object MARS extends Origin 13 | case object BELT extends Origin 14 | } 15 | 16 | sealed trait Role 17 | 18 | object Role { 19 | case class Captain(shipName: String) extends Role 20 | case class Pilot(shipName: String) extends Role 21 | case class Engineer(shipName: String) extends Role 22 | case class Mechanic(shipName: String) extends Role 23 | } 24 | 25 | case class Character(name: String, nicknames: List[String], origin: Origin, role: Option[Role]) 26 | 27 | case class CharactersArgs(origin: Option[Origin]) 28 | case class CharacterArgs(name: String) 29 | 30 | val sampleCharacters = List( 31 | Character("James Holden", List("Jim", "Hoss"), EARTH, Some(Captain("Rocinante"))), 32 | Character("Naomi Nagata", Nil, BELT, Some(Engineer("Rocinante"))), 33 | Character("Amos Burton", Nil, EARTH, Some(Mechanic("Rocinante"))), 34 | Character("Alex Kamal", Nil, MARS, Some(Pilot("Rocinante"))), 35 | Character("Chrisjen Avasarala", Nil, EARTH, None), 36 | Character("Josephus Miller", List("Joe"), BELT, None), 37 | Character("Roberta Draper", List("Bobbie", "Gunny"), MARS, None) 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /examples/src/main/scala/example/calibantotapir/README.md: -------------------------------------------------------------------------------- 1 | # Caliban to Tapir 2 | 3 | This example shows how to convert the Caliban `GraphQL` datatype and turn them into Tapir `Endpoint`s. 4 | This is useful when you have both REST APIs and GraphQL endpoints and you want to use Tapir to expose a unified API. 5 | 6 | The example also shows how to mount the GraphQL endpoint to `POST /graphql` and `GET /graphql`, the GraphiQL UI (`GET` `/graphiql`), as well as provide `GET /example` for a sample endpoint returning JSON. 7 | All of the endpoints are documented in Swagger. Visiting `localhost:8080/` or `/docs` will show the Swagger UI. 8 | 9 | This example interprets the Tapir endpoints using ZIO HTTP. 10 | 11 | **Note:** If you are only using GraphQL endpoints, then you are much better of using the `quick` adapter instead of this. This example is provided for when you want to use both GraphQL and REST endpoints and have them managed by Tapir to gain the benefits of documentation. -------------------------------------------------------------------------------- /examples/src/main/scala/example/calibantotapir/tapir/SampleRestEndpoint.scala: -------------------------------------------------------------------------------- 1 | package example.calibantotapir.tapir 2 | 3 | import com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec 4 | import com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker 5 | import sttp.capabilities.zio.ZioStreams 6 | import sttp.tapir.Schema 7 | import sttp.tapir.json.jsoniter._ 8 | import sttp.tapir.ztapir._ 9 | import zio._ 10 | 11 | object SampleRestEndpoint { 12 | // NOTE: You can also add the graphiql endpoint in a similar fashion 13 | val endpoint: ZServerEndpoint[Any, ZioStreams] = 14 | infallibleEndpoint.get 15 | .in("example") 16 | .out(jsonBody[SampleResponse]) 17 | .serverLogic(_ => ZIO.right(SampleResponse("Hello", 1, 2))) 18 | 19 | final case class SampleResponse(data: String, x: Int, y: Int) 20 | object SampleResponse { 21 | implicit val sampleResponseCodec: JsonValueCodec[SampleResponse] = JsonCodecMaker.make 22 | implicit val sampleResponseSchema: Schema[SampleResponse] = Schema.derived 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/src/main/scala/example/federation/EpisodeService.scala: -------------------------------------------------------------------------------- 1 | package example.federation 2 | 3 | import example.federation.FederationData.episodes.Episode 4 | 5 | import zio.{ Ref, UIO, URIO, ZIO, ZLayer } 6 | 7 | trait EpisodeService { 8 | def getEpisode(season: Int, episode: Int): UIO[Option[Episode]] 9 | def getEpisodes(season: Option[Int]): UIO[List[Episode]] 10 | } 11 | 12 | object EpisodeService { 13 | def getEpisode(season: Int, episode: Int): URIO[EpisodeService, Option[Episode]] = 14 | ZIO.serviceWithZIO[EpisodeService](_.getEpisode(season, episode)) 15 | 16 | def getEpisodes(season: Option[Int] = None): URIO[EpisodeService, List[Episode]] = 17 | ZIO.serviceWithZIO[EpisodeService](_.getEpisodes(season)) 18 | 19 | def make(initial: List[Episode]): ZLayer[Any, Nothing, EpisodeService] = ZLayer { 20 | Ref 21 | .make(initial) 22 | .map { episodes => 23 | new EpisodeService { 24 | def getEpisode(season: Int, episode: Int): UIO[Option[Episode]] = 25 | episodes.get.map(_.find(e => e.season == season && e.episode == episode)) 26 | 27 | def getEpisodes(season: Option[Int]): UIO[List[Episode]] = 28 | episodes.get.map(_.filter(e => season.forall(_ == e.season))) 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/src/main/scala/example/federation/FederatedApp.scala: -------------------------------------------------------------------------------- 1 | package example.federation 2 | 3 | import caliban.quick._ 4 | import example.federation.FederationData.characters.sampleCharacters 5 | import example.federation.FederationData.episodes.sampleEpisodes 6 | import zio._ 7 | 8 | object FederatedApp extends ZIOAppDefault { 9 | 10 | val episodesApi = FederatedApi.Episodes.api.runServer(8089, "/api/graphql", Some("/graphiql")) 11 | val charactersApi = FederatedApi.Characters.api.runServer(8088, "/api/graphql", Some("/graphiql")) 12 | 13 | override def run = 14 | (episodesApi zipPar charactersApi) 15 | .provide( 16 | EpisodeService.make(sampleEpisodes), 17 | CharacterService.make(sampleCharacters) 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /examples/src/main/scala/example/federation/v2/EpisodeService.scala: -------------------------------------------------------------------------------- 1 | package example.federation.v2 2 | 3 | import example.federation.v2.FederationData.episodes.Episode 4 | import zio.{ Ref, UIO, URIO, ZIO, ZLayer } 5 | 6 | trait EpisodeService { 7 | def getEpisode(season: Int, episode: Int): UIO[Option[Episode]] 8 | def getEpisodes(season: Option[Int]): UIO[List[Episode]] 9 | } 10 | 11 | object EpisodeService { 12 | 13 | def getEpisode(season: Int, episode: Int): URIO[EpisodeService, Option[Episode]] = 14 | ZIO.serviceWithZIO[EpisodeService](_.getEpisode(season, episode)) 15 | 16 | def getEpisodes(season: Option[Int] = None): URIO[EpisodeService, List[Episode]] = 17 | ZIO.serviceWithZIO[EpisodeService](_.getEpisodes(season)) 18 | 19 | def make(initial: List[Episode]): ZLayer[Any, Nothing, EpisodeService] = ZLayer { 20 | Ref 21 | .make(initial) 22 | .map { episodes => 23 | new EpisodeService { 24 | override def getEpisode(season: Int, episode: Int): UIO[Option[Episode]] = 25 | episodes.get.map(_.find(e => e.season == season && e.episode == episode)) 26 | 27 | override def getEpisodes(season: Option[Int]): UIO[List[Episode]] = 28 | episodes.get.map(_.filter(e => season.forall(_ == e.season))) 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/src/main/scala/example/federation/v2/FederatedApp.scala: -------------------------------------------------------------------------------- 1 | package example.federation.v2 2 | 3 | import caliban.quick._ 4 | import example.federation.v2.FederationData.characters.sampleCharacters 5 | import example.federation.v2.FederationData.episodes.sampleEpisodes 6 | import zio._ 7 | 8 | object FederatedApp extends ZIOAppDefault { 9 | 10 | val characterServer = 11 | FederatedApi.Characters.api.runServer(8088, "/api/graphql") 12 | 13 | val episodeServer = 14 | FederatedApi.Episodes.api.runServer(8089, "/api/graphql") 15 | 16 | def run = 17 | (characterServer race episodeServer) 18 | .provide(EpisodeService.make(sampleEpisodes), CharacterService.make(sampleCharacters)) 19 | } 20 | -------------------------------------------------------------------------------- /examples/src/main/scala/example/optimizations/CommonData.scala: -------------------------------------------------------------------------------- 1 | package example.optimizations 2 | 3 | object CommonData { 4 | case class UserArgs(id: Int) 5 | case class FirstArgs(first: Int) 6 | case class SizeArgs(size: Int) 7 | 8 | case class ViewerMetadata(name: String) 9 | case class Tag(name: String) 10 | case class Venue(name: String) 11 | 12 | val query: String = 13 | """ 14 | |query UserEventsScreen { 15 | | user(id: 1) { 16 | | fullName 17 | | username 18 | | picture(size: 50) 19 | | upcomingEvents(first: 5) { 20 | | id 21 | | name 22 | | date 23 | | startTime 24 | | endTime 25 | | viewerRsvp { 26 | | name 27 | | } 28 | | tags { 29 | | name 30 | | } 31 | | venue { 32 | | name 33 | | } 34 | | attendingFriendsOfViewer(first: 4) { 35 | | picture(size: 25) 36 | | } 37 | | } 38 | | } 39 | |}""".stripMargin 40 | } 41 | -------------------------------------------------------------------------------- /examples/src/main/scala/example/quick/ExampleApp.scala: -------------------------------------------------------------------------------- 1 | package example.quick 2 | 3 | import caliban._ 4 | import caliban.quick._ 5 | import example.ExampleData._ 6 | import example.{ ExampleApi, ExampleService } 7 | import zio._ 8 | 9 | object ExampleApp extends ZIOAppDefault { 10 | 11 | // Only as a convenience of shutting down the server via CLI, don't add to production code! 12 | private val cliShutdownSignal = 13 | Console.printLine("Server online at http://localhost:8090/") *> 14 | Console.printLine("Press RETURN to stop...") *> 15 | Console.readLine.unit 16 | 17 | private val serve = 18 | ZIO 19 | .serviceWithZIO[GraphQL[Any]] { 20 | _.runServer( 21 | port = 8090, 22 | apiPath = "/api/graphql", 23 | graphiqlPath = Some("/graphiql"), 24 | webSocketPath = Some("/ws/graphql") 25 | ) 26 | } 27 | .provide( 28 | ExampleService.make(sampleCharacters), 29 | ExampleApi.layer 30 | ) 31 | 32 | override def run: ZIO[Any, Throwable, Unit] = serve race cliShutdownSignal 33 | 34 | } 35 | -------------------------------------------------------------------------------- /examples/src/main/scala/example/tapirtocaliban/README.md: -------------------------------------------------------------------------------- 1 | # Tapir to Caliban 2 | 3 | This example shows how you can take Tapir endpoints and turn them into Caliban GraphQL datatypes. -------------------------------------------------------------------------------- /federation/src/main/scala/caliban/federation/FederationDirectives.scala: -------------------------------------------------------------------------------- 1 | package caliban.federation 2 | 3 | import caliban.InputValue 4 | import caliban.Value.{ BooleanValue, StringValue } 5 | import caliban.parsing.adt.Directive 6 | import caliban.schema.Annotations.GQLDirective 7 | 8 | trait FederationDirectives { 9 | case class GQLProvides(fields: String) extends GQLDirective(Provides(fields)) 10 | 11 | object Provides { 12 | def apply(fields: String): Directive = 13 | Directive("provides", Map("fields" -> StringValue(fields))) 14 | } 15 | 16 | case class GQLRequires(fields: String) extends GQLDirective(Requires(fields)) 17 | 18 | object Requires { 19 | def apply(fields: String): Directive = 20 | Directive("requires", Map("fields" -> StringValue(fields))) 21 | } 22 | 23 | case class GQLExtend() extends GQLDirective(Extend) 24 | 25 | val Extend = Directive("extends") 26 | 27 | case class GQLExternal() extends GQLDirective(External) 28 | 29 | val External = Directive("external") 30 | 31 | case class GQLKey(fields: String, resolvable: Boolean = true) extends GQLDirective(Key(fields, resolvable)) 32 | 33 | object Key { 34 | def apply(fields: String, resolvable: Boolean = true, name: String = "key"): Directive = { 35 | val args: List[(String, InputValue)] = List( 36 | Some("fields" -> StringValue(fields)), 37 | (if (resolvable) None else Some("resolvable" -> BooleanValue(resolvable))) 38 | ).flatten 39 | 40 | Directive(name, args.toMap) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /federation/src/main/scala/caliban/federation/package.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import caliban.federation.v2x.{ 4 | FederationDirectivesV2_10, 5 | FederationDirectivesV2_3, 6 | FederationDirectivesV2_5, 7 | FederationDirectivesV2_6, 8 | FederationDirectivesV2_8, 9 | FederationDirectivesV2_9, 10 | FederationV2, 11 | Versions 12 | } 13 | 14 | package object federation { 15 | 16 | lazy val v1 = new FederationV1 with FederationDirectives 17 | lazy val v2_0 = new FederationV2(List(Versions.v2_0)) 18 | lazy val v2_1 = new FederationV2(List(Versions.v2_1)) 19 | lazy val v2_3 = new FederationV2(List(Versions.v2_3)) with FederationDirectivesV2_3 20 | lazy val v2_4 = new FederationV2(List(Versions.v2_4)) with FederationDirectivesV2_3 21 | lazy val v2_5 = new FederationV2(List(Versions.v2_5)) with FederationDirectivesV2_5 22 | lazy val v2_6 = new FederationV2(List(Versions.v2_6)) with FederationDirectivesV2_6 23 | lazy val v2_7 = new FederationV2(List(Versions.v2_7)) with FederationDirectivesV2_6 24 | lazy val v2_8 = new FederationV2(List(Versions.v2_8)) with FederationDirectivesV2_8 25 | lazy val v2_9 = new FederationV2(List(Versions.v2_9)) with FederationDirectivesV2_9 26 | lazy val v2_10 = new FederationV2(List(Versions.v2_10, FederationV2.connect)) with FederationDirectivesV2_10 27 | 28 | } 29 | -------------------------------------------------------------------------------- /federation/src/main/scala/caliban/federation/v2x/FederationDirectivesV2.scala: -------------------------------------------------------------------------------- 1 | package caliban.federation.v2x 2 | 3 | import caliban.Value.StringValue 4 | import caliban.parsing.adt.Directive 5 | import caliban.schema.Annotations.GQLDirective 6 | 7 | trait FederationDirectivesV2 { 8 | case class GQLShareable() extends GQLDirective(Shareable) 9 | 10 | val Shareable = Directive("shareable") 11 | 12 | case class GQLInaccessible() extends GQLDirective(Inaccessible) 13 | 14 | val Inaccessible = Directive("inaccessible") 15 | 16 | case class GQLOverride(from: String) extends GQLDirective(Override(from)) 17 | 18 | object Override { 19 | def apply(from: String): Directive = 20 | Directive("override", Map("from" -> StringValue(from))) 21 | } 22 | 23 | case class GQLTag(name: String) extends GQLDirective(Tag(name)) 24 | 25 | object Tag { 26 | def apply(name: String): Directive = 27 | Directive("tag", Map("name" -> StringValue(name))) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /federation/src/main/scala/caliban/federation/v2x/FederationDirectivesV2_3.scala: -------------------------------------------------------------------------------- 1 | package caliban.federation.v2x 2 | 3 | import caliban.parsing.adt.Directive 4 | import caliban.schema.Annotations.GQLDirective 5 | 6 | trait FederationDirectivesV2_3 { 7 | case class GQLInterfaceObject() extends GQLDirective(InterfaceObject) 8 | 9 | val InterfaceObject = Directive("interfaceObject") 10 | 11 | } 12 | -------------------------------------------------------------------------------- /federation/src/main/scala/caliban/federation/v2x/FederationDirectivesV2_5.scala: -------------------------------------------------------------------------------- 1 | package caliban.federation.v2x 2 | 3 | import caliban.InputValue.ListValue 4 | import caliban.Value.StringValue 5 | import caliban.parsing.adt.Directive 6 | import caliban.schema.Annotations.GQLDirective 7 | 8 | trait FederationDirectivesV2_5 extends FederationDirectivesV2_3 { 9 | 10 | val Authenticated = Directive("authenticated") 11 | def RequiresScopes(scopes: List[List[String]]) = 12 | Directive("requiresScopes", Map("scopes" -> ListValue(scopes.map(s => ListValue(s.map(s => StringValue(s))))))) 13 | case class GQLAuthenticated() extends GQLDirective(Authenticated) 14 | 15 | case class GQLRequiresScopes(scopes: List[List[String]]) extends GQLDirective(RequiresScopes(scopes)) 16 | 17 | } 18 | -------------------------------------------------------------------------------- /federation/src/main/scala/caliban/federation/v2x/FederationDirectivesV2_6.scala: -------------------------------------------------------------------------------- 1 | package caliban.federation.v2x 2 | 3 | import caliban.InputValue.ListValue 4 | import caliban.Value.StringValue 5 | import caliban.parsing.adt.Directive 6 | import caliban.schema.Annotations.GQLDirective 7 | 8 | trait FederationDirectivesV2_6 extends FederationDirectivesV2_5 { 9 | 10 | def Policy(policies: List[List[String]]) = 11 | Directive("policy", Map("policies" -> ListValue(policies.map(s => ListValue(s.map(s => StringValue(s))))))) 12 | 13 | case class GQLPolicy(policies: List[List[String]]) extends GQLDirective(Policy(policies)) 14 | 15 | } 16 | -------------------------------------------------------------------------------- /federation/src/main/scala/caliban/federation/v2x/FederationDirectivesV2_8.scala: -------------------------------------------------------------------------------- 1 | package caliban.federation.v2x 2 | 3 | import caliban.Value.StringValue 4 | import caliban.parsing.adt.Directive 5 | import caliban.schema.Annotations.GQLDirective 6 | 7 | trait FederationDirectivesV2_8 extends FederationDirectivesV2_6 { 8 | 9 | def Context(context: String) = Directive("context", Map("context" -> StringValue(context))) 10 | 11 | case class GQLContext(context: String) extends GQLDirective(Context(context)) 12 | 13 | def FromContext(context: String) = Directive("fromContext", Map("context" -> StringValue(context))) 14 | 15 | case class GQLFromContext(context: String) extends GQLDirective(FromContext(context)) 16 | } 17 | -------------------------------------------------------------------------------- /federation/src/main/scala/caliban/federation/v2x/Link.scala: -------------------------------------------------------------------------------- 1 | package caliban.federation.v2x 2 | 3 | import caliban.InputValue 4 | import caliban.InputValue.ListValue 5 | import caliban.Value.StringValue 6 | import caliban.parsing.adt.Directive 7 | 8 | trait Extension { 9 | def toDirective: Directive 10 | } 11 | 12 | case class ComposeDirective(name: String) extends Extension { 13 | def toDirective: Directive = Directive("composeDirective", Map("name" -> StringValue(name))) 14 | } 15 | 16 | case class Link(url: String, `import`: List[Import]) extends Extension { 17 | def toDirective: Directive = 18 | Directive("link", Map("url" -> StringValue(url), "import" -> ListValue(`import`.map(_.toInputValue)))) 19 | } 20 | 21 | case class Import(name: String, as: Option[String] = None) { 22 | def toInputValue: InputValue = as.fold[InputValue](StringValue(name))(alias => 23 | InputValue.ObjectValue( 24 | Map("name" -> StringValue(name), "as" -> StringValue(alias)) 25 | ) 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /federation/src/main/scala/caliban/federation/v2x/Versions.scala: -------------------------------------------------------------------------------- 1 | package caliban.federation.v2x 2 | 3 | object Versions { 4 | 5 | val v2_0 = FederationV2.v2_0 6 | val v2_1 = FederationV2.v2_1 7 | val v2_3 = FederationV2.v2_3 8 | val v2_4 = FederationV2.v2_4 9 | val v2_5 = FederationV2.v2_5 10 | val v2_6 = FederationV2.v2_6 11 | val v2_7 = FederationV2.v2_7 12 | val v2_8 = FederationV2.v2_8 13 | val v2_9 = FederationV2.v2_9 14 | val v2_10 = FederationV2.v2_10 15 | 16 | } 17 | -------------------------------------------------------------------------------- /interop/cats/src/main/scala/caliban/interop/fs2/Fs2Interop.scala: -------------------------------------------------------------------------------- 1 | package caliban.interop.fs2 2 | 3 | import caliban.interop.cats.FromEffect 4 | import caliban.introspection.adt.__Type 5 | import caliban.schema.{ Schema, Step } 6 | import fs2.Stream 7 | import zio.stream.ZStream 8 | import zio.stream.interop.fs2z._ 9 | import zio.{ RIO, Task } 10 | 11 | object Fs2Interop { 12 | def schemaStreamRIO[R, A](implicit ev: Schema[R, ZStream[R, Throwable, A]]): Schema[R, Stream[RIO[R, *], A]] = 13 | new Schema[R, Stream[RIO[R, *], A]] { 14 | override def toType(isInput: Boolean, isSubscription: Boolean): __Type = 15 | ev.toType_(isInput, isSubscription) 16 | 17 | override def nullable: Boolean = ev.nullable 18 | override def canFail: Boolean = ev.canFail 19 | 20 | override def resolve(value: Stream[RIO[R, *], A]): Step[R] = 21 | ev.resolve(value.toZStream()) 22 | } 23 | 24 | def schemaStreamF[F[_], R, A](implicit 25 | interop: FromEffect[F, R], 26 | ev: Schema[R, Stream[RIO[R, *], A]] 27 | ): Schema[R, Stream[F, A]] = 28 | new Schema[R, Stream[F, A]] { 29 | override def toType(isInput: Boolean, isSubscription: Boolean): __Type = 30 | ev.toType_(isInput, isSubscription) 31 | 32 | override def nullable: Boolean = ev.nullable 33 | override def canFail: Boolean = ev.canFail 34 | 35 | override def resolve(value: Stream[F, A]): Step[R] = 36 | ev.resolve(value.translate(interop.fromEffectK)) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /interop/cats/src/main/scala/caliban/interop/fs2/implicits/package.scala: -------------------------------------------------------------------------------- 1 | package caliban.interop.fs2 2 | 3 | import caliban.interop.cats.FromEffect 4 | import caliban.schema.{ Schema, SubscriptionSchema } 5 | import fs2.Stream 6 | import zio.RIO 7 | import zio.stream.ZStream 8 | 9 | package object implicits { 10 | 11 | implicit def fs2SchemaStreamRIO[R, A](implicit 12 | ev: Schema[R, ZStream[R, Throwable, A]] 13 | ): Schema[R, Stream[RIO[R, *], A]] = 14 | Fs2Interop.schemaStreamRIO 15 | 16 | implicit def fs2SchemaStreamF[F[_], R, A](implicit 17 | interop: FromEffect[F, R], 18 | ev: Schema[R, Stream[RIO[R, *], A]] 19 | ): Schema[R, Stream[F, A]] = 20 | Fs2Interop.schemaStreamF 21 | 22 | implicit def fs2SubscriptionSchemaStreamF[F[_], A]: SubscriptionSchema[Stream[F, A]] = 23 | new SubscriptionSchema[Stream[F, A]] {} 24 | } 25 | -------------------------------------------------------------------------------- /interop/tapir/src/main/scala/caliban/interop/tapir/JsonCodecs.scala: -------------------------------------------------------------------------------- 1 | package caliban.interop.tapir 2 | 3 | import caliban.{ GraphQLRequest, ResponseValue } 4 | import sttp.tapir.Codec.JsonCodec 5 | import MaxCharBufSizeJsonJsoniter._ 6 | 7 | private object JsonCodecs { 8 | import caliban.interop.jsoniter.ValueJsoniter.stringListCodec 9 | 10 | val listMapCodec: JsonCodec[Map[String, List[String]]] = implicitly 11 | val requestCodec: JsonCodec[GraphQLRequest] = implicitly 12 | val responseCodec: JsonCodec[ResponseValue] = implicitly 13 | } 14 | -------------------------------------------------------------------------------- /interop/tapir/src/main/scala/caliban/interop/tapir/MaxCharBufSizeJsonJsoniter.scala: -------------------------------------------------------------------------------- 1 | package caliban.interop.tapir 2 | 3 | import com.github.plokhotnyuk.jsoniter_scala.core.ReaderConfig 4 | import sttp.tapir.json.jsoniter.TapirJsonJsoniter 5 | 6 | object MaxCharBufSizeJsonJsoniter extends TapirJsonJsoniter { 7 | override lazy val readerConfig: ReaderConfig = 8 | ReaderConfig 9 | .withAppendHexDumpToParseException(false) 10 | .withMaxCharBufSize(Int.MaxValue - 2) 11 | .withMaxBufSize(Int.MaxValue - 2) 12 | } 13 | -------------------------------------------------------------------------------- /interop/tapir/src/main/scala/caliban/interop/tapir/StreamConstructor.scala: -------------------------------------------------------------------------------- 1 | package caliban.interop.tapir 2 | 3 | import zio.stream.ZStream 4 | 5 | trait StreamConstructor[BS] { 6 | def apply(stream: ZStream[Any, Throwable, Byte]): BS 7 | } 8 | 9 | object StreamConstructor { 10 | implicit val zioStreams: StreamConstructor[ZStream[Any, Throwable, Byte]] = 11 | (stream: ZStream[Any, Throwable, Byte]) => stream 12 | 13 | implicit val noStreams: StreamConstructor[Nothing] = 14 | (_: ZStream[Any, Throwable, Byte]) => throw new UnsupportedOperationException("Streams are not supported") 15 | } 16 | -------------------------------------------------------------------------------- /interop/tapir/src/test/scala/caliban/interop/tapir/FakeAuthorizationInterceptor.scala: -------------------------------------------------------------------------------- 1 | package caliban.interop.tapir 2 | 3 | import caliban.interop.tapir.TapirAdapter.TapirResponse 4 | import sttp.model.StatusCode 5 | import sttp.tapir.model.ServerRequest 6 | import zio.{ ZIO, ZLayer } 7 | 8 | object FakeAuthorizationInterceptor { 9 | 10 | def make[R]( 11 | authenticate: ServerRequest => ZIO[R, (Int, String), Unit] 12 | ): ZLayer[ServerRequest with R, TapirResponse, R] = 13 | ZLayer.fromZIOEnvironment { 14 | for { 15 | request <- ZIO.service[ServerRequest] 16 | env <- ZIO.environment[R] 17 | _ <- authenticate(request).mapError { case (status, str) => 18 | TapirResponse(StatusCode(status), body = str) 19 | } 20 | } yield env 21 | } 22 | 23 | def bearer[R]: ZLayer[ServerRequest with R, TapirResponse, R] = 24 | make[R](req => ZIO.fail((401, "You are unauthorized!")).when(req.headers("X-Invalid").nonEmpty).unit) 25 | } 26 | -------------------------------------------------------------------------------- /macros/src/main/scala-2/caliban/schema/Derived.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | import scala.annotation.implicitNotFound 4 | 5 | @implicitNotFound( 6 | """Cannot find a ${T}. 7 | 8 | Caliban derives a Schema automatically for basic Scala types, case classes and sealed traits, but 9 | you need to manually provide an implicit Schema for other types that could be nested inside your type. 10 | If you use a custom type as an argument, you also need to provide an implicit ArgBuilder for that type. 11 | See https://ghostdogpr.github.io/caliban/docs/schema.html for more information. 12 | """ 13 | ) 14 | case class Derived[T](schema: T) extends AnyVal 15 | -------------------------------------------------------------------------------- /macros/src/main/scala-2/caliban/schema/DerivedMagnolia.scala: -------------------------------------------------------------------------------- 1 | package caliban.schema 2 | 3 | object DerivedMagnolia { 4 | import magnolia1.Magnolia 5 | 6 | import scala.reflect.macros.whitebox 7 | 8 | // Wrap the output of Magnolia in a Derived to force it to a lower priority. 9 | // This seems to work, despite magnolia hardcode checks for `macroApplication` symbol 10 | // and relying on getting an diverging implicit expansion error for auto-mode. 11 | // Thankfully at least it doesn't check the output type of its `macroApplication` 12 | def derivedMagnolia[TC[_], A: c.WeakTypeTag](c: whitebox.Context): c.Expr[Derived[TC[A]]] = { 13 | val magnoliaTree = c.Expr[TC[A]](Magnolia.gen[A](c)) 14 | c.universe.reify(Derived(magnoliaTree.splice)) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /project/ScriptedDependency.scala: -------------------------------------------------------------------------------- 1 | import sbt.* 2 | 3 | //noinspection TypeAnnotation 4 | object ScriptedDependency { 5 | // scala steward should understand these version in context of dependencies described here 6 | object Version { 7 | val sttp = "3.10.1" 8 | val zioTest = "2.1.9" 9 | } 10 | 11 | lazy val sttp = Seq( 12 | "com.softwaremill.sttp.client3" %% "core" % Version.sttp, 13 | "com.softwaremill.sttp.client3" %% "zio" % Version.sttp 14 | ) 15 | 16 | lazy val zioTest = Seq( 17 | "dev.zio" %% "zio-test" % Version.zioTest % Test, 18 | "dev.zio" %% "zio-test-sbt" % Version.zioTest % Test 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 1.11.1 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | val sbtcrossProjectVersion = "1.3.2" 2 | 3 | addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.4") 4 | addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.11.1") 5 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.19.0") 6 | addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.7") 7 | addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % sbtcrossProjectVersion) 8 | addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % sbtcrossProjectVersion) 9 | addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.7") 10 | addSbtPlugin("com.thesamet" % "sbt-protoc" % "1.0.7") 11 | addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.13.1") 12 | addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.7.1") 13 | addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.4") 14 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1") 15 | 16 | addSbtPlugin("org.jetbrains.scala" % "sbt-ide-settings" % "1.1.2") 17 | 18 | libraryDependencies += "com.thesamet.scalapb" %% "compilerplugin" % "0.11.17" 19 | addDependencyTreePlugin 20 | -------------------------------------------------------------------------------- /reporting/src/main/scala/caliban/reporting/Util.scala: -------------------------------------------------------------------------------- 1 | package caliban.reporting 2 | 3 | import zio.{ Task, ZIO } 4 | 5 | import java.security.MessageDigest 6 | 7 | object Util { 8 | 9 | private[caliban] def hashSchema(schema: String): Task[String] = 10 | ZIO.attempt( 11 | MessageDigest 12 | .getInstance("SHA-256") 13 | .digest(schema.getBytes("UTF-8")) 14 | .map("%02x".format(_)) 15 | .mkString 16 | ) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /reporting/src/main/scala/caliban/reporting/client/ReportSchemaError.scala: -------------------------------------------------------------------------------- 1 | package caliban.reporting.client 2 | 3 | import caliban.client.FieldBuilder._ 4 | import caliban.client._ 5 | 6 | object ReportSchemaError { 7 | def code: SelectionBuilder[ReportSchemaError, ReportSchemaErrorCode] = 8 | _root_.caliban.client.SelectionBuilder.Field("code", Scalar()) 9 | def inSeconds: SelectionBuilder[ReportSchemaError, Int] = 10 | _root_.caliban.client.SelectionBuilder.Field("inSeconds", Scalar()) 11 | def message: SelectionBuilder[ReportSchemaError, String] = 12 | _root_.caliban.client.SelectionBuilder.Field("message", Scalar()) 13 | def withCoreSchema: SelectionBuilder[ReportSchemaError, Boolean] = 14 | _root_.caliban.client.SelectionBuilder.Field("withCoreSchema", Scalar()) 15 | } 16 | -------------------------------------------------------------------------------- /reporting/src/main/scala/caliban/reporting/client/ReportSchemaResponse.scala: -------------------------------------------------------------------------------- 1 | package caliban.reporting.client 2 | 3 | import caliban.client.FieldBuilder._ 4 | import caliban.client._ 5 | 6 | object ReportSchemaResponse { 7 | def inSeconds: SelectionBuilder[ReportSchemaResponse, Int] = 8 | _root_.caliban.client.SelectionBuilder.Field("inSeconds", Scalar()) 9 | def withCoreSchema: SelectionBuilder[ReportSchemaResponse, Boolean] = 10 | _root_.caliban.client.SelectionBuilder.Field("withCoreSchema", Scalar()) 11 | } 12 | -------------------------------------------------------------------------------- /reporting/src/main/scala/caliban/reporting/client/ReportSchemaResult.scala: -------------------------------------------------------------------------------- 1 | package caliban.reporting.client 2 | 3 | import caliban.client.FieldBuilder._ 4 | import caliban.client._ 5 | 6 | object ReportSchemaResult { 7 | def inSeconds: SelectionBuilder[ReportSchemaResult, Int] = 8 | _root_.caliban.client.SelectionBuilder.Field("inSeconds", Scalar()) 9 | def withCoreSchema: SelectionBuilder[ReportSchemaResult, Boolean] = 10 | _root_.caliban.client.SelectionBuilder.Field("withCoreSchema", Scalar()) 11 | } 12 | -------------------------------------------------------------------------------- /reporting/src/main/scala/caliban/reporting/client/package.scala: -------------------------------------------------------------------------------- 1 | package caliban.reporting 2 | 3 | package object client { 4 | 5 | type ReportSchemaResult 6 | type ReportSchemaError 7 | type ReportSchemaResponse 8 | type Query = _root_.caliban.client.Operations.RootQuery 9 | type Mutation = _root_.caliban.client.Operations.RootMutation 10 | } 11 | -------------------------------------------------------------------------------- /reporting/src/main/scala/caliban/reporting/package.scala: -------------------------------------------------------------------------------- 1 | package caliban 2 | 3 | import sttp.client3.SttpBackend 4 | import zio.Task 5 | 6 | package object reporting { 7 | 8 | type SttpClient = SttpBackend[Task, Any] 9 | } 10 | -------------------------------------------------------------------------------- /tools/src/main/resources/default.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version=3.8.2 2 | runner.dialect=scala3 -------------------------------------------------------------------------------- /tools/src/main/scala/caliban/tools/Options.scala: -------------------------------------------------------------------------------- 1 | package caliban.tools 2 | 3 | final case class Options( 4 | schemaPath: String, 5 | toPath: String, 6 | fmtPath: Option[String], 7 | headers: Option[List[Options.Header]], 8 | packageName: Option[String], 9 | clientName: Option[String], 10 | genView: Option[Boolean], 11 | effect: Option[String], 12 | scalarMappings: Option[Map[String, String]], 13 | imports: Option[List[String]], 14 | abstractEffectType: Option[Boolean], 15 | splitFiles: Option[Boolean], 16 | enableFmt: Option[Boolean], 17 | extensibleEnums: Option[Boolean], 18 | preserveInputNames: Option[Boolean], 19 | supportIsRepeatable: Option[Boolean], 20 | addDerives: Option[Boolean], 21 | envForDerives: Option[String], 22 | excludeDeprecated: Option[Boolean], 23 | supportDeprecatedArgs: Option[Boolean] 24 | ) 25 | 26 | object Options { 27 | final case class Header(name: String, value: String) 28 | } 29 | -------------------------------------------------------------------------------- /tools/src/main/scala/caliban/tools/compiletime/CompileTime.scala: -------------------------------------------------------------------------------- 1 | package caliban.tools.compiletime 2 | 3 | import caliban._ 4 | import caliban.tools.Codegen.GenType 5 | import caliban.tools.compiletime.Config.ClientGenerationSettings 6 | import caliban.tools.{ Codegen, SchemaLoader } 7 | import zio.{ ExitCode, UIO, ZIO } 8 | 9 | object CompileTime { 10 | 11 | def generateClient[R]( 12 | args: List[String] 13 | )(api: GraphQL[R], settings: ClientGenerationSettings): UIO[ExitCode] = 14 | args match { 15 | case baseDir :: Nil => 16 | Codegen 17 | .generate( 18 | SchemaLoader.fromCaliban(api), 19 | settings.toCalibanCommonSettings.toOptions( 20 | schemaPath = "", 21 | toPath = { 22 | val dir = Utils.toPathDir(baseDir, settings.packageName) 23 | 24 | if (settings.splitFiles) dir else s"$dir/${settings.clientName}.scala" 25 | } 26 | ), 27 | GenType.Client 28 | ) 29 | .exitCode 30 | case _ => 31 | ZIO 32 | .fail(new RuntimeException(s"`CompileTime.generateClient` was called with invalid arguments: $args")) 33 | .exitCode 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /tools/src/main/scala/caliban/tools/compiletime/Utils.scala: -------------------------------------------------------------------------------- 1 | package caliban.tools.compiletime 2 | 3 | private[caliban] object Utils { 4 | 5 | def packagePath(packageName: String): String = packageName.split('.').mkString(java.io.File.separator) 6 | def toPathDir(baseDir: String, packageName: String): String = { 7 | val isVersioned = !baseDir.contains("src_managed") 8 | if (isVersioned) s"$baseDir/src/main/scala/${packagePath(packageName)}" 9 | else s"$baseDir/${packagePath(packageName)}" 10 | } 11 | 12 | def toScalaCode[A](l: List[A])(asScalaCode: A => String): String = 13 | if (l.isEmpty) "List.empty" else s"List(${l.map(asScalaCode).mkString(",")})" 14 | 15 | def toScalaCode(l: List[(String, String)]): String = 16 | toScalaCode[(String, String)](l) { case (a, b) => s"""("$a","$b")""" } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /tools/src/main/scala/caliban/tools/stitching/PartialRemoteSchema.scala: -------------------------------------------------------------------------------- 1 | package caliban.tools.stitching 2 | 3 | import zio._ 4 | import zio.query._ 5 | 6 | import caliban.{ CalibanError, ResponseValue } 7 | import caliban.schema._ 8 | import caliban.introspection.adt._ 9 | 10 | trait PartialRemoteSchema[R0, R, A] { self => 11 | def toType(isInput: Boolean, isSubscription: Boolean): __Type 12 | 13 | def resolve(value: A, args: caliban.execution.Field): ZIO[R0, CalibanError, ResponseValue] 14 | 15 | def provideEnvironment[R1 <: R0](env: ZEnvironment[R1]): Schema[R, A] = new Schema[R, A] { 16 | def resolve(value: A): Step[R] = 17 | Step.MetadataFunctionStep { (args: caliban.execution.Field) => 18 | Step.QueryStep(ZQuery.fromZIO(self.resolve(value, args).map(Step.PureStep.apply).provideEnvironment(env))) 19 | } 20 | 21 | def toType(isInput: Boolean, isSubscription: Boolean): __Type = 22 | self.toType(isInput, isSubscription) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tools/src/main/scala/caliban/tools/stitching/ResolveRequest.scala: -------------------------------------------------------------------------------- 1 | package caliban.tools.stitching 2 | 3 | import caliban.execution.Field 4 | 5 | case class ResolveRequest[A]( 6 | args: A, 7 | field: Field 8 | ) 9 | -------------------------------------------------------------------------------- /tools/src/main/scala/caliban/tools/stitching/package.scala: -------------------------------------------------------------------------------- 1 | package caliban.tools 2 | 3 | import caliban.{ CalibanError, ResponseValue } 4 | 5 | import sttp.client3._ 6 | 7 | package object stitching { 8 | type HttpRequest = RequestT[Identity, Either[CalibanError.ExecutionError, ResponseValue], Any] 9 | } 10 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/add scalar mappings and additional imports.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | import java.util.UUID 5 | import a.b._ 6 | 7 | object Client { 8 | 9 | type Order 10 | object Order { 11 | def date: SelectionBuilder[Order, java.time.OffsetDateTime] = 12 | _root_.caliban.client.SelectionBuilder.Field("date", Scalar()) 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/case-insensitive name uniqueness in 2 basic objects.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Character 7 | object Character { 8 | def name: SelectionBuilder[Character, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 9 | def nicknames: SelectionBuilder[Character, List[String]] = 10 | _root_.caliban.client.SelectionBuilder.Field("nicknames", ListOf(Scalar())) 11 | } 12 | 13 | type character_1 14 | object character_1 { 15 | def name: SelectionBuilder[character_1, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 16 | def nicknames: SelectionBuilder[character_1, List[String]] = 17 | _root_.caliban.client.SelectionBuilder.Field("nicknames", ListOf(Scalar())) 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/default arguments for optional and list arguments.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Query = _root_.caliban.client.Operations.RootQuery 7 | object Query { 8 | def characters(first: Int, last: scala.Option[Int] = None, origins: List[scala.Option[String]] = Nil)(implicit 9 | encoder0: ArgEncoder[Int], 10 | encoder1: ArgEncoder[scala.Option[Int]], 11 | encoder2: ArgEncoder[List[scala.Option[String]]] 12 | ): SelectionBuilder[_root_.caliban.client.Operations.RootQuery, scala.Option[String]] = 13 | _root_.caliban.client.SelectionBuilder.Field( 14 | "characters", 15 | OptionOf(Scalar()), 16 | arguments = List( 17 | Argument("first", first, "Int!")(encoder0), 18 | Argument("last", last, "Int")(encoder1), 19 | Argument("origins", origins, "[String]!")(encoder2) 20 | ) 21 | ) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/deprecated field + comment newline.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Character 7 | object Character { 8 | 9 | /** 10 | * name 11 | */ 12 | @deprecated("""foo 13 | bar""") 14 | def name: SelectionBuilder[Character, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/deprecated field + comment.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Character 7 | object Character { 8 | 9 | /** 10 | * name 11 | */ 12 | @deprecated("blah") 13 | def name: SelectionBuilder[Character, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 14 | @deprecated 15 | def nicknames: SelectionBuilder[Character, List[String]] = 16 | _root_.caliban.client.SelectionBuilder.Field("nicknames", ListOf(Scalar())) 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/deprecated field argument + comment newline.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Query = _root_.caliban.client.Operations.RootQuery 7 | object Query { 8 | def characters( 9 | first: Int, 10 | @deprecated("""foo 11 | bar""") 12 | last: scala.Option[Int] = None, 13 | @deprecated 14 | origins: scala.Option[List[scala.Option[String]]] = None 15 | )(implicit 16 | encoder0: ArgEncoder[Int], 17 | encoder1: ArgEncoder[scala.Option[Int]], 18 | encoder2: ArgEncoder[scala.Option[List[scala.Option[String]]]] 19 | ): SelectionBuilder[_root_.caliban.client.Operations.RootQuery, scala.Option[String]] = 20 | _root_.caliban.client.SelectionBuilder.Field( 21 | "characters", 22 | OptionOf(Scalar()), 23 | arguments = List( 24 | Argument("first", first, "Int!")(encoder0), 25 | Argument("last", last, "Int")(encoder1), 26 | Argument("origins", origins, "[String]")(encoder2) 27 | ) 28 | ) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/deprecated field argument + comment.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Query = _root_.caliban.client.Operations.RootQuery 7 | object Query { 8 | def characters( 9 | first: Int, 10 | @deprecated("foo bar") 11 | last: scala.Option[Int] = None, 12 | @deprecated 13 | origins: scala.Option[List[scala.Option[String]]] = None 14 | )(implicit 15 | encoder0: ArgEncoder[Int], 16 | encoder1: ArgEncoder[scala.Option[Int]], 17 | encoder2: ArgEncoder[scala.Option[List[scala.Option[String]]]] 18 | ): SelectionBuilder[_root_.caliban.client.Operations.RootQuery, scala.Option[String]] = 19 | _root_.caliban.client.SelectionBuilder.Field( 20 | "characters", 21 | OptionOf(Scalar()), 22 | arguments = List( 23 | Argument("first", first, "Int!")(encoder0), 24 | Argument("last", last, "Int")(encoder1), 25 | Argument("origins", origins, "[String]")(encoder2) 26 | ) 27 | ) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/enum with exclude deprecated, only deprecated values.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.CalibanClientError.DecodingError 2 | import caliban.client._ 3 | import caliban.client.__Value._ 4 | 5 | object Client { 6 | 7 | sealed trait Origin extends scala.Product with scala.Serializable { def value: String } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/enum with exclude deprecated.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.CalibanClientError.DecodingError 2 | import caliban.client._ 3 | import caliban.client.__Value._ 4 | 5 | object Client { 6 | 7 | sealed trait Origin extends scala.Product with scala.Serializable { def value: String } 8 | object Origin { 9 | case object EARTH extends Origin { val value: String = "EARTH" } 10 | case object BELT extends Origin { val value: String = "BELT" } 11 | 12 | implicit val decoder: ScalarDecoder[Origin] = { 13 | case __StringValue("EARTH") => Right(Origin.EARTH) 14 | case __StringValue("BELT") => Right(Origin.BELT) 15 | case other => Left(DecodingError(s"Can't build Origin from input $other")) 16 | } 17 | implicit val encoder: ArgEncoder[Origin] = { 18 | case Origin.EARTH => __EnumValue("EARTH") 19 | case Origin.BELT => __EnumValue("BELT") 20 | } 21 | 22 | val values: scala.collection.immutable.Vector[Origin] = scala.collection.immutable.Vector(EARTH, BELT) 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/enum.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.CalibanClientError.DecodingError 2 | import caliban.client._ 3 | import caliban.client.__Value._ 4 | 5 | object Client { 6 | 7 | sealed trait Origin extends scala.Product with scala.Serializable { def value: String } 8 | object Origin { 9 | case object EARTH extends Origin { val value: String = "EARTH" } 10 | case object MARS extends Origin { val value: String = "MARS" } 11 | case object BELT extends Origin { val value: String = "BELT" } 12 | 13 | implicit val decoder: ScalarDecoder[Origin] = { 14 | case __StringValue("EARTH") => Right(Origin.EARTH) 15 | case __StringValue("MARS") => Right(Origin.MARS) 16 | case __StringValue("BELT") => Right(Origin.BELT) 17 | case other => Left(DecodingError(s"Can't build Origin from input $other")) 18 | } 19 | implicit val encoder: ArgEncoder[Origin] = { 20 | case Origin.EARTH => __EnumValue("EARTH") 21 | case Origin.MARS => __EnumValue("MARS") 22 | case Origin.BELT => __EnumValue("BELT") 23 | } 24 | 25 | val values: scala.collection.immutable.Vector[Origin] = scala.collection.immutable.Vector(EARTH, MARS, BELT) 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/input object oneOf.scala: -------------------------------------------------------------------------------- 1 | import caliban.client._ 2 | import caliban.client.__Value._ 3 | 4 | object Client { 5 | 6 | sealed trait CharacterInput { 7 | protected def encode: __Value 8 | } 9 | object CharacterInput { 10 | final case class Name(name: String) extends CharacterInput { 11 | protected def encode: __Value = __ObjectValue(List("name" -> implicitly[ArgEncoder[String]].encode(name))) 12 | } 13 | final case class Nicknames(nicknames: List[String] = Nil) extends CharacterInput { 14 | protected def encode: __Value = __ObjectValue( 15 | List("nicknames" -> __ListValue(nicknames.map(value => implicitly[ArgEncoder[String]].encode(value)))) 16 | ) 17 | } 18 | 19 | implicit val encoder: ArgEncoder[CharacterInput] = new ArgEncoder[CharacterInput] { 20 | override def encode(value: CharacterInput): __Value = value.encode 21 | } 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/input object with deprecated fields.scala: -------------------------------------------------------------------------------- 1 | import caliban.client._ 2 | import caliban.client.__Value._ 3 | 4 | object Client { 5 | 6 | final case class CharacterInput( 7 | name: String, 8 | @deprecated 9 | nickname: scala.Option[String] = None, 10 | @deprecated("no longer used") 11 | address: scala.Option[String] = None 12 | ) 13 | object CharacterInput { 14 | implicit val encoder: ArgEncoder[CharacterInput] = new ArgEncoder[CharacterInput] { 15 | override def encode(value: CharacterInput): __Value = 16 | __ObjectValue( 17 | List( 18 | "name" -> implicitly[ArgEncoder[String]].encode(value.name), 19 | "nickname" -> value.nickname.fold(__NullValue: __Value)(value => 20 | implicitly[ArgEncoder[String]].encode(value) 21 | ), 22 | "address" -> value.address.fold(__NullValue: __Value)(value => implicitly[ArgEncoder[String]].encode(value)) 23 | ) 24 | ) 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/input object with reserved name.scala: -------------------------------------------------------------------------------- 1 | import caliban.client._ 2 | import caliban.client.__Value._ 3 | 4 | object Client { 5 | 6 | final case class CharacterInput(wait$ : String) 7 | object CharacterInput { 8 | implicit val encoder: ArgEncoder[CharacterInput] = new ArgEncoder[CharacterInput] { 9 | override def encode(value: CharacterInput): __Value = 10 | __ObjectValue(List("wait" -> implicitly[ArgEncoder[String]].encode(value.wait$))) 11 | } 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/input object.scala: -------------------------------------------------------------------------------- 1 | import caliban.client._ 2 | import caliban.client.__Value._ 3 | 4 | object Client { 5 | 6 | final case class CharacterInput(name: String, nicknames: List[String] = Nil) 7 | object CharacterInput { 8 | implicit val encoder: ArgEncoder[CharacterInput] = new ArgEncoder[CharacterInput] { 9 | override def encode(value: CharacterInput): __Value = 10 | __ObjectValue( 11 | List( 12 | "name" -> implicitly[ArgEncoder[String]].encode(value.name), 13 | "nicknames" -> __ListValue(value.nicknames.map(value => implicitly[ArgEncoder[String]].encode(value))) 14 | ) 15 | ) 16 | } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/interface with implements.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Entity 7 | object Entity { 8 | def name: SelectionBuilder[Entity, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 9 | } 10 | 11 | type Pet 12 | object Pet { 13 | def name: SelectionBuilder[Pet, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 14 | } 15 | 16 | type Cat 17 | object Cat { 18 | def name: SelectionBuilder[Cat, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 19 | } 20 | 21 | type Dog 22 | object Dog { 23 | def name: SelectionBuilder[Dog, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/interface without implements.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.CalibanClientError.DecodingError 2 | import caliban.client.FieldBuilder._ 3 | import caliban.client._ 4 | import caliban.client.__Value._ 5 | 6 | object Client { 7 | 8 | sealed trait Status extends scala.Product with scala.Serializable { def value: String } 9 | object Status { 10 | case object Status1 extends Status { val value: String = "Status1" } 11 | 12 | implicit val decoder: ScalarDecoder[Status] = { 13 | case __StringValue("Status1") => Right(Status.Status1) 14 | case other => Left(DecodingError(s"Can't build Status from input $other")) 15 | } 16 | implicit val encoder: ArgEncoder[Status] = { case Status.Status1 => 17 | __EnumValue("Status1") 18 | } 19 | 20 | val values: scala.collection.immutable.Vector[Status] = scala.collection.immutable.Vector(Status1) 21 | } 22 | 23 | type Entity 24 | object Entity { 25 | def statusInfo[A](innerSelection: SelectionBuilder[StatusInfo, A]): SelectionBuilder[Entity, A] = 26 | _root_.caliban.client.SelectionBuilder.Field("statusInfo", Obj(innerSelection)) 27 | } 28 | 29 | type StatusInfo 30 | object StatusInfo { 31 | def status: SelectionBuilder[StatusInfo, Status] = _root_.caliban.client.SelectionBuilder.Field("status", Scalar()) 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/interface.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Order 7 | object Order { 8 | def name: SelectionBuilder[Order, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 9 | } 10 | 11 | type Ascending 12 | object Ascending { 13 | def name: SelectionBuilder[Ascending, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 14 | } 15 | 16 | type Sort 17 | object Sort { 18 | def `object`[A](onAscending: SelectionBuilder[Ascending, A]): SelectionBuilder[Sort, scala.Option[A]] = 19 | _root_.caliban.client.SelectionBuilder.Field("object", OptionOf(ChoiceOf(Map("Ascending" -> Obj(onAscending))))) 20 | def objectOption[A]( 21 | onAscending: scala.Option[SelectionBuilder[Ascending, A]] = None 22 | ): SelectionBuilder[Sort, scala.Option[scala.Option[A]]] = _root_.caliban.client.SelectionBuilder.Field( 23 | "object", 24 | OptionOf( 25 | ChoiceOf(Map("Ascending" -> onAscending.fold[FieldBuilder[scala.Option[A]]](NullField)(a => OptionOf(Obj(a))))) 26 | ) 27 | ) 28 | def objectInterface[A](`object`: SelectionBuilder[Order, A]): SelectionBuilder[Sort, scala.Option[A]] = 29 | _root_.caliban.client.SelectionBuilder.Field("object", OptionOf(Obj(`object`))) 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/nested object type.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Q 7 | object Q { 8 | def characters[A](innerSelection: SelectionBuilder[Character, A]): SelectionBuilder[Q, List[A]] = 9 | _root_.caliban.client.SelectionBuilder.Field("characters", ListOf(Obj(innerSelection))) 10 | } 11 | 12 | type Character 13 | object Character { 14 | def name: SelectionBuilder[Character, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 15 | def nicknames: SelectionBuilder[Character, List[String]] = 16 | _root_.caliban.client.SelectionBuilder.Field("nicknames", ListOf(Scalar())) 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/object type with arguments.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Q 7 | object Q { 8 | def character[A](name: String)(innerSelection: SelectionBuilder[Character, A])(implicit 9 | encoder0: ArgEncoder[String] 10 | ): SelectionBuilder[Q, scala.Option[A]] = _root_.caliban.client.SelectionBuilder 11 | .Field("character", OptionOf(Obj(innerSelection)), arguments = List(Argument("name", name, "String!")(encoder0))) 12 | } 13 | 14 | type Character 15 | object Character { 16 | def name: SelectionBuilder[Character, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 17 | def nicknames: SelectionBuilder[Character, List[String]] = 18 | _root_.caliban.client.SelectionBuilder.Field("nicknames", ListOf(Scalar())) 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/object type with reserved name.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Character 7 | object Character { 8 | def `type`: SelectionBuilder[Character, String] = _root_.caliban.client.SelectionBuilder.Field("type", Scalar()) 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/safe names with underscores.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Character 7 | object Character { 8 | def _$ : SelectionBuilder[Character, scala.Option[Boolean]] = 9 | _root_.caliban.client.SelectionBuilder.Field("_", OptionOf(Scalar())) 10 | def `_name_` : SelectionBuilder[Character, scala.Option[String]] = 11 | _root_.caliban.client.SelectionBuilder.Field("_name_", OptionOf(Scalar())) 12 | def _nickname: SelectionBuilder[Character, scala.Option[String]] = 13 | _root_.caliban.client.SelectionBuilder.Field("_nickname", OptionOf(Scalar())) 14 | def `age_` : SelectionBuilder[Character, scala.Option[Int]] = 15 | _root_.caliban.client.SelectionBuilder.Field("age_", OptionOf(Scalar())) 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/schema with splitFiles.scala: -------------------------------------------------------------------------------- 1 | --- package --- 2 | package object test { 3 | type Character 4 | type Q = _root_.caliban.client.Operations.RootQuery 5 | } 6 | 7 | --- Character --- 8 | package test 9 | 10 | import caliban.client.FieldBuilder._ 11 | import caliban.client._ 12 | 13 | object Character { 14 | def name: SelectionBuilder[Character, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 15 | def nicknames: SelectionBuilder[Character, List[String]] = 16 | _root_.caliban.client.SelectionBuilder.Field("nicknames", ListOf(Scalar())) 17 | } 18 | 19 | --- Q --- 20 | package test 21 | 22 | import caliban.client.FieldBuilder._ 23 | import caliban.client._ 24 | 25 | object Q { 26 | def characters[A]( 27 | innerSelection: SelectionBuilder[Character, A] 28 | ): SelectionBuilder[_root_.caliban.client.Operations.RootQuery, List[A]] = 29 | _root_.caliban.client.SelectionBuilder.Field("characters", ListOf(Obj(innerSelection))) 30 | } 31 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/schema.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Character 7 | object Character { 8 | def name: SelectionBuilder[Character, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 9 | def nicknames: SelectionBuilder[Character, List[String]] = 10 | _root_.caliban.client.SelectionBuilder.Field("nicknames", ListOf(Scalar())) 11 | } 12 | 13 | type Q = _root_.caliban.client.Operations.RootQuery 14 | object Q { 15 | def characters[A]( 16 | innerSelection: SelectionBuilder[Character, A] 17 | ): SelectionBuilder[_root_.caliban.client.Operations.RootQuery, List[A]] = 18 | _root_.caliban.client.SelectionBuilder.Field("characters", ListOf(Obj(innerSelection))) 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/simple object type with exclude deprecated and genView, only deprecated fields.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Character 7 | object Character {} 8 | 9 | } 10 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/simple object type with exclude deprecated and genView.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Character 7 | object Character { 8 | 9 | final case class CharacterView(name: String) 10 | 11 | type ViewSelection = SelectionBuilder[Character, CharacterView] 12 | 13 | def view: ViewSelection = name.map(name => CharacterView(name)) 14 | 15 | def name: SelectionBuilder[Character, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/simple object type.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Character 7 | object Character { 8 | def name: SelectionBuilder[Character, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 9 | def nicknames: SelectionBuilder[Character, List[String]] = 10 | _root_.caliban.client.SelectionBuilder.Field("nicknames", ListOf(Scalar())) 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterSpec/support for Json scalar.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Query = _root_.caliban.client.Operations.RootQuery 7 | object Query { 8 | def test: SelectionBuilder[_root_.caliban.client.Operations.RootQuery, io.circe.Json] = 9 | _root_.caliban.client.SelectionBuilder.Field("test", Scalar()) 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterViewSpec/generic view for capital fields.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type TypeWithCapitalFields 7 | object TypeWithCapitalFields { 8 | 9 | final case class TypeWithCapitalFieldsView(Name: scala.Option[String], Value: scala.Option[String]) 10 | 11 | type ViewSelection = SelectionBuilder[TypeWithCapitalFields, TypeWithCapitalFieldsView] 12 | 13 | def view: ViewSelection = (Name ~ Value).map { case (name$, value$) => TypeWithCapitalFieldsView(name$, value$) } 14 | 15 | def Name: SelectionBuilder[TypeWithCapitalFields, scala.Option[String]] = 16 | _root_.caliban.client.SelectionBuilder.Field("Name", OptionOf(Scalar())) 17 | def Value: SelectionBuilder[TypeWithCapitalFields, scala.Option[String]] = 18 | _root_.caliban.client.SelectionBuilder.Field("Value", OptionOf(Scalar())) 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterViewSpec/recursive object type.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Character 7 | object Character { 8 | 9 | final case class CharacterView[FriendsSelection](name: String, age: Int, friends: List[FriendsSelection]) 10 | 11 | type ViewSelection[FriendsSelection] = SelectionBuilder[Character, CharacterView[FriendsSelection]] 12 | 13 | def view[FriendsSelection](friendsFilter: scala.Option[String] = None)( 14 | friendsSelection: SelectionBuilder[Character, FriendsSelection] 15 | ): ViewSelection[FriendsSelection] = (name ~ age ~ friends(friendsFilter)(friendsSelection)).map { 16 | case (name, age, friends) => CharacterView(name, age, friends) 17 | } 18 | 19 | def name: SelectionBuilder[Character, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 20 | def age: SelectionBuilder[Character, Int] = _root_.caliban.client.SelectionBuilder.Field("age", Scalar()) 21 | def friends[A](filter: scala.Option[String] = None)(innerSelection: SelectionBuilder[Character, A])(implicit 22 | encoder0: ArgEncoder[scala.Option[String]] 23 | ): SelectionBuilder[Character, List[A]] = _root_.caliban.client.SelectionBuilder 24 | .Field("friends", ListOf(Obj(innerSelection)), arguments = List(Argument("filter", filter, "String")(encoder0))) 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/ClientWriterViewSpec/simple object type.scala: -------------------------------------------------------------------------------- 1 | import caliban.client.FieldBuilder._ 2 | import caliban.client._ 3 | 4 | object Client { 5 | 6 | type Character 7 | object Character { 8 | 9 | final case class CharacterView(name: String, age: Int, nicknames: List[String]) 10 | 11 | type ViewSelection = SelectionBuilder[Character, CharacterView] 12 | 13 | def view: ViewSelection = (name ~ age ~ nicknames).map { case (name, age, nicknames) => 14 | CharacterView(name, age, nicknames) 15 | } 16 | 17 | def name: SelectionBuilder[Character, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) 18 | def age: SelectionBuilder[Character, Int] = _root_.caliban.client.SelectionBuilder.Field("age", Scalar()) 19 | def nicknames: SelectionBuilder[Character, List[String]] = 20 | _root_.caliban.client.SelectionBuilder.Field("nicknames", ListOf(Scalar())) 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/(empty schema test).scala: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/GQLDeprecated with multiline reason and escaped quotes.scala: -------------------------------------------------------------------------------- 1 | import caliban.schema.Annotations._ 2 | 3 | object Types { 4 | 5 | final case class ExampleType( 6 | @GQLDeprecated("""This field is deprecated for the following reasons: 7 | 1. \"Outdated data model\": The field relies on an outdated data model. 8 | 2. \"Performance issues\": Queries using this field have performance issues. 9 | Please use `newField` instead.""") 10 | oldField: scala.Option[String], 11 | newField: scala.Option[String] 12 | ) 13 | 14 | } 15 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/GQLDeprecated with reason.scala: -------------------------------------------------------------------------------- 1 | import caliban.schema.Annotations._ 2 | 3 | object Types { 4 | 5 | final case class Captain( 6 | @GQLDescription("foo") 7 | @GQLDeprecated("bar") 8 | shipName: String 9 | ) 10 | 11 | } 12 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/GQLDescription with escaped quotes.scala: -------------------------------------------------------------------------------- 1 | import caliban.schema.Annotations._ 2 | 3 | object Types { 4 | 5 | final case class Captain( 6 | @GQLDescription("foo \"quotes\" bar") 7 | shipName: String 8 | ) 9 | 10 | } 11 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/add derives also to traits for @oneOf input types.scala: -------------------------------------------------------------------------------- 1 | import caliban.schema.Annotations._ 2 | 3 | object Types { 4 | 5 | @GQLOneOfInput 6 | sealed trait FooInput extends scala.Product with scala.Serializable 7 | derives caliban.schema.Schema.SemiAuto, 8 | caliban.schema.ArgBuilder 9 | object FooInput { 10 | final case class OptionA(optionA: String) extends FooInput 11 | derives caliban.schema.Schema.SemiAuto, 12 | caliban.schema.ArgBuilder 13 | final case class OptionB(optionB: Int) extends FooInput 14 | derives caliban.schema.Schema.SemiAuto, 15 | caliban.schema.ArgBuilder 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/add derives.scala: -------------------------------------------------------------------------------- 1 | import Types._ 2 | 3 | object Types { 4 | final case class HeroNameArgs(pad: Int) derives caliban.schema.Schema.SemiAuto, caliban.schema.ArgBuilder 5 | final case class QueryHeroArgs(episode: scala.Option[Episode]) 6 | derives caliban.schema.Schema.SemiAuto, 7 | caliban.schema.ArgBuilder 8 | final case class Hero(name: HeroNameArgs => String) derives caliban.schema.Schema.SemiAuto 9 | final case class HeroInput(name: String) derives caliban.schema.Schema.SemiAuto, caliban.schema.ArgBuilder 10 | 11 | sealed trait Episode extends scala.Product with scala.Serializable 12 | derives caliban.schema.Schema.SemiAuto, 13 | caliban.schema.ArgBuilder 14 | 15 | object Episode { 16 | case object NEWHOPE extends Episode derives caliban.schema.Schema.SemiAuto, caliban.schema.ArgBuilder 17 | case object EMPIRE extends Episode derives caliban.schema.Schema.SemiAuto, caliban.schema.ArgBuilder 18 | case object JEDI extends Episode derives caliban.schema.Schema.SemiAuto, caliban.schema.ArgBuilder 19 | } 20 | 21 | } 22 | 23 | object Operations { 24 | 25 | final case class Query( 26 | hero: QueryHeroArgs => zio.UIO[scala.Option[Hero]] 27 | ) derives caliban.schema.Schema.SemiAuto 28 | 29 | } 30 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/add scalar mappings and additional imports.scala: -------------------------------------------------------------------------------- 1 | import Types._ 2 | 3 | import zio.stream.ZStream 4 | 5 | import java.util.UUID 6 | import a.b._ 7 | 8 | object Types { 9 | final case class MutationAddPostArgs(author: scala.Option[String], comment: scala.Option[String]) 10 | final case class Post(date: java.time.OffsetDateTime, author: scala.Option[String], comment: scala.Option[String]) 11 | 12 | } 13 | 14 | object Operations { 15 | 16 | final case class Query( 17 | posts: zio.UIO[scala.Option[List[scala.Option[Post]]]] 18 | ) 19 | 20 | final case class Mutation( 21 | addPost: MutationAddPostArgs => zio.UIO[scala.Option[Post]] 22 | ) 23 | 24 | final case class Subscription( 25 | postAdded: ZStream[Any, Nothing, scala.Option[Post]] 26 | ) 27 | 28 | } 29 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/args names root level.scala: -------------------------------------------------------------------------------- 1 | import Types._ 2 | 3 | import zio.stream.ZStream 4 | 5 | object Types { 6 | final case class QueryCharactersArgs(p: Params) 7 | final case class SubscriptionCharactersArgs(p: Params) 8 | final case class Params(p: Int) 9 | 10 | } 11 | 12 | object Operations { 13 | 14 | final case class Query( 15 | characters: QueryCharactersArgs => zio.UIO[Int] 16 | ) 17 | 18 | final case class Subscription( 19 | characters: SubscriptionCharactersArgs => ZStream[Any, Nothing, Int] 20 | ) 21 | 22 | } 23 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/args unique class names.scala: -------------------------------------------------------------------------------- 1 | object Types { 2 | final case class HeroCallAlliesArgs(number: Int) 3 | final case class VillainCallAlliesArgs(number: Int, w: String) 4 | final case class Hero(callAllies: HeroCallAlliesArgs => List[Hero]) 5 | final case class Villain(callAllies: VillainCallAlliesArgs => List[Villain]) 6 | 7 | } 8 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/enum type.scala: -------------------------------------------------------------------------------- 1 | object Types { 2 | 3 | sealed trait Origin extends scala.Product with scala.Serializable 4 | 5 | object Origin { 6 | case object EARTH extends Origin 7 | case object MARS extends Origin 8 | case object BELT extends Origin 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/final case class reserved field name used.scala: -------------------------------------------------------------------------------- 1 | object Types { 2 | 3 | final case class Character(wait$ : String) 4 | 5 | } 6 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/generate nested @lazy fields with abstracted effect type.scala: -------------------------------------------------------------------------------- 1 | import Types._ 2 | 3 | object Types { 4 | 5 | final case class Foo[F[_]](bar: Bar[F]) 6 | final case class Bar[F[_]](baz: F[Baz[F]]) 7 | final case class Baz[F[_]](x: String, y: F[String]) 8 | 9 | } 10 | 11 | object Operations { 12 | 13 | final case class Query[F[_]]( 14 | foo: F[Foo[F]] 15 | ) 16 | 17 | } 18 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/inherits field with args.scala: -------------------------------------------------------------------------------- 1 | import caliban.schema.Annotations._ 2 | 3 | object Types { 4 | final case class CharacterFriendsConnectionArgs(first: scala.Option[Int], after: scala.Option[ID]) 5 | derives caliban.schema.Schema.SemiAuto, 6 | caliban.schema.ArgBuilder 7 | final case class Human(friendsConnection: CharacterFriendsConnectionArgs => FriendsConnection) extends Character 8 | derives caliban.schema.Schema.SemiAuto 9 | final case class Droid(friendsConnection: CharacterFriendsConnectionArgs => FriendsConnection) extends Character 10 | derives caliban.schema.Schema.SemiAuto 11 | 12 | @GQLInterface 13 | sealed trait Character extends scala.Product with scala.Serializable derives caliban.schema.Schema.SemiAuto { 14 | def friendsConnection: CharacterFriendsConnectionArgs => FriendsConnection 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/input type oneOf.scala: -------------------------------------------------------------------------------- 1 | import caliban.schema.Annotations._ 2 | 3 | object Types { 4 | 5 | final case class Character(name: String) 6 | 7 | @GQLOneOfInput 8 | sealed trait CharacterArgs extends scala.Product with scala.Serializable 9 | object CharacterArgs { 10 | final case class Foo(foo: String) extends CharacterArgs 11 | final case class Bar(bar: Int) extends CharacterArgs 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/input type with preserved input.scala: -------------------------------------------------------------------------------- 1 | import caliban.schema.Annotations._ 2 | 3 | object Types { 4 | 5 | final case class Character(name: String) 6 | @GQLInputName("CharacterInput") 7 | final case class CharacterInput(name: String) 8 | 9 | } 10 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/input type.scala: -------------------------------------------------------------------------------- 1 | object Types { 2 | 3 | final case class Character(name: String) 4 | final case class CharacterArgs(name: String) 5 | 6 | } 7 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/interface type.scala: -------------------------------------------------------------------------------- 1 | import caliban.schema.Annotations._ 2 | 3 | object Types { 4 | 5 | final case class Admin( 6 | id: java.util.UUID, 7 | @GQLDescription("firstName") 8 | firstName: String, 9 | lastName: String 10 | ) extends Person 11 | final case class Customer(id: java.util.UUID, firstName: String, lastName: String, email: String) extends Person 12 | 13 | @GQLInterface 14 | @GQLDescription("""person 15 | Admin or Customer""") 16 | sealed trait Person extends scala.Product with scala.Serializable { 17 | def id: java.util.UUID 18 | def firstName: String 19 | def lastName: String 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/interface, abstracted effect and lazy combination.scala: -------------------------------------------------------------------------------- 1 | import Types._ 2 | 3 | import caliban.schema.Annotations._ 4 | 5 | object Types { 6 | 7 | final case class T0[F[_]](i: F[Int]) extends Direct[F] 8 | final case class T1[F[_]](i: scala.Option[T2[F]]) extends Transitive[F] 9 | final case class T2[F[_]](i: F[scala.Option[Int]]) 10 | 11 | @GQLInterface 12 | sealed trait Direct[F[_]] extends scala.Product with scala.Serializable { 13 | def i: F[Int] 14 | } 15 | 16 | @GQLInterface 17 | sealed trait Transitive[F[_]] extends scala.Product with scala.Serializable { 18 | def i: scala.Option[T2[F]] 19 | } 20 | 21 | } 22 | 23 | object Operations { 24 | 25 | final case class Query[F[_]]( 26 | t0: F[T0[F]], 27 | t1: F[T1[F]] 28 | ) 29 | 30 | } 31 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/recognize @lazy intention and generate side-effecting field with abstracted effect type.scala: -------------------------------------------------------------------------------- 1 | object Types { 2 | 3 | final case class Foo[F[_]](bar: String, baz: F[String]) 4 | 5 | } 6 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/recognize @lazy intention and generate side-effecting field.scala: -------------------------------------------------------------------------------- 1 | object Types { 2 | 3 | final case class Foo(bar: String, baz: zio.UIO[String]) 4 | 5 | } 6 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/scala reserved word used.scala: -------------------------------------------------------------------------------- 1 | object Types { 2 | 3 | final case class Character(`private`: String, `object`: String, `type`: String) 4 | 5 | } 6 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/schema test.scala: -------------------------------------------------------------------------------- 1 | import Types._ 2 | 3 | import zio.stream.ZStream 4 | 5 | object Types { 6 | final case class MutationAddPostArgs(author: scala.Option[String], comment: scala.Option[String]) 7 | final case class Post(author: scala.Option[String], comment: scala.Option[String]) 8 | 9 | } 10 | 11 | object Operations { 12 | 13 | final case class Query( 14 | posts: zio.UIO[scala.Option[List[scala.Option[Post]]]] 15 | ) 16 | 17 | final case class Mutation( 18 | addPost: MutationAddPostArgs => zio.UIO[scala.Option[Post]] 19 | ) 20 | 21 | final case class Subscription( 22 | postAdded: ZStream[Any, Nothing, scala.Option[Post]] 23 | ) 24 | 25 | } 26 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/schema.scala: -------------------------------------------------------------------------------- 1 | object Operations { 2 | 3 | final case class Queries( 4 | characters: zio.UIO[Int] 5 | ) 6 | 7 | } 8 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/simple mutation with abstracted effect type.scala: -------------------------------------------------------------------------------- 1 | import Types._ 2 | 3 | object Types { 4 | final case class MutationSetMessageArgs(message: scala.Option[String]) 5 | 6 | } 7 | 8 | object Operations { 9 | 10 | final case class Mutation[F[_]]( 11 | setMessage: MutationSetMessageArgs => F[scala.Option[String]] 12 | ) 13 | 14 | } 15 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/simple mutation.scala: -------------------------------------------------------------------------------- 1 | import Types._ 2 | 3 | object Types { 4 | final case class MutationSetMessageArgs(message: scala.Option[String]) 5 | 6 | } 7 | 8 | object Operations { 9 | 10 | final case class Mutation( 11 | setMessage: MutationSetMessageArgs => zio.UIO[scala.Option[String]] 12 | ) 13 | 14 | } 15 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/simple queries with abstracted effect type.scala: -------------------------------------------------------------------------------- 1 | import Types._ 2 | 3 | object Types { 4 | final case class QueryUserArgs(id: scala.Option[Int]) 5 | final case class User(id: scala.Option[Int], name: scala.Option[String], profilePic: scala.Option[String]) 6 | 7 | } 8 | 9 | object Operations { 10 | 11 | final case class Query[F[_]]( 12 | user: QueryUserArgs => F[scala.Option[User]], 13 | userList: F[List[scala.Option[User]]] 14 | ) 15 | 16 | } 17 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/simple queries.scala: -------------------------------------------------------------------------------- 1 | import Types._ 2 | 3 | object Types { 4 | final case class QueryUserArgs(id: scala.Option[Int]) 5 | final case class User(id: scala.Option[Int], name: scala.Option[String], profilePic: scala.Option[String]) 6 | 7 | } 8 | 9 | object Operations { 10 | 11 | final case class Query( 12 | user: QueryUserArgs => zio.UIO[scala.Option[User]], 13 | userList: zio.UIO[List[scala.Option[User]]] 14 | ) 15 | 16 | } 17 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/simple subscription.scala: -------------------------------------------------------------------------------- 1 | import Types._ 2 | 3 | import zio.stream.ZStream 4 | 5 | object Types { 6 | final case class SubscriptionUserWatchArgs(id: Int) 7 | 8 | } 9 | 10 | object Operations { 11 | 12 | final case class Subscription( 13 | UserWatch: SubscriptionUserWatchArgs => ZStream[Any, Nothing, String] 14 | ) 15 | 16 | } 17 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/type appears in type union and implements interface.scala: -------------------------------------------------------------------------------- 1 | import Types._ 2 | 3 | import caliban.schema.Annotations._ 4 | 5 | object Types { 6 | 7 | final case class Bar(message: String) extends Error with AllErrors derives caliban.schema.Schema.SemiAuto 8 | final case class Foo(message: String) extends Error with AllErrors derives caliban.schema.Schema.SemiAuto 9 | final case class FooBar(message: String) extends Error with AllErrors derives caliban.schema.Schema.SemiAuto 10 | 11 | sealed trait AllErrors extends scala.Product with scala.Serializable derives caliban.schema.Schema.SemiAuto 12 | @GQLInterface 13 | sealed trait Error extends scala.Product with scala.Serializable derives caliban.schema.Schema.SemiAuto { 14 | @GQLDescription("description") 15 | def message: String 16 | } 17 | 18 | } 19 | 20 | object Operations { 21 | 22 | final case class Query( 23 | errorInterface: zio.UIO[Error], 24 | errorUnion: zio.UIO[AllErrors] 25 | ) derives caliban.schema.Schema.SemiAuto 26 | 27 | } 28 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/type with field parameter.scala: -------------------------------------------------------------------------------- 1 | object Types { 2 | final case class HeroNameArgs(pad: Int) 3 | final case class Hero(name: HeroNameArgs => String, nick: String, bday: scala.Option[Int]) 4 | 5 | } 6 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/union type.scala: -------------------------------------------------------------------------------- 1 | import caliban.schema.Annotations._ 2 | 3 | object Types { 4 | 5 | final case class Captain( 6 | @GQLDescription("ship") 7 | shipName: String 8 | ) extends Role 9 | with Role2 10 | final case class Pilot(shipName: String) extends Role with Role2 11 | final case class Stewart(shipName: String) extends Role2 12 | 13 | @GQLDescription("""role 14 | Captain or Pilot""") 15 | sealed trait Role extends scala.Product with scala.Serializable 16 | @GQLDescription("""role2 17 | Captain or Pilot or Stewart""") 18 | sealed trait Role2 extends scala.Product with scala.Serializable 19 | 20 | } 21 | -------------------------------------------------------------------------------- /tools/src/test/resources/snapshots/SchemaWriterSpec/union, abstracted effect type and lazy combination.scala: -------------------------------------------------------------------------------- 1 | import Types._ 2 | 3 | object Types { 4 | final case class BLazyFieldWithArgsArgs(int: scala.Option[Int]) 5 | final case class A[F[_]](lazyField: F[scala.Option[Int]]) extends U0[F] 6 | final case class B[F[_]](lazyFieldWithArgs: BLazyFieldWithArgsArgs => F[scala.Option[Int]]) extends U0[F] 7 | final case class C[F[_]](field: scala.Option[Int]) extends U0[F] with U1 8 | final case class D(field: scala.Option[Int]) extends U1 9 | 10 | sealed trait U0[F[_]] extends scala.Product with scala.Serializable 11 | sealed trait U1 extends scala.Product with scala.Serializable 12 | 13 | } 14 | 15 | object Operations { 16 | 17 | final case class Query[F[_]]( 18 | effectful: F[U0[F]], 19 | pure: F[U1] 20 | ) 21 | 22 | } 23 | -------------------------------------------------------------------------------- /tools/src/test/scala/caliban/tools/compiletime/UtilsSpec.scala: -------------------------------------------------------------------------------- 1 | package caliban.tools.compiletime 2 | 3 | import zio.test._ 4 | 5 | object UtilsSpec extends ZIOSpecDefault { 6 | 7 | private val toPathDirSpec = 8 | suite(".toPathDir")( 9 | test("returns a different path according to the `baseDir` passed") { 10 | val result_0 = Utils.toPathDir("/user/Jules/workspace/awesome-project", "io.example.awesome.project.generated") 11 | val result_1 = Utils.toPathDir( 12 | "/user/Jules/workspace/awesome-project/target/scala-2.13/src_managed/main", 13 | "io.example.awesome.project.generated" 14 | ) 15 | 16 | assertTrue( 17 | result_0 == "/user/Jules/workspace/awesome-project/src/main/scala/io/example/awesome/project/generated" 18 | ) && 19 | assertTrue( 20 | result_1 == "/user/Jules/workspace/awesome-project/target/scala-2.13/src_managed/main/io/example/awesome/project/generated" 21 | ) 22 | } 23 | ) 24 | 25 | override def spec = 26 | suite("Utils spec")( 27 | toPathDirSpec 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /tracing/src/main/scala/caliban/tracing/FieldTracer.scala: -------------------------------------------------------------------------------- 1 | package caliban.tracing 2 | 3 | import caliban.{ CalibanError, ResponseValue } 4 | import caliban.execution.FieldInfo 5 | import caliban.wrappers.Wrapper.FieldWrapper 6 | import io.opentelemetry.api.trace.StatusCode 7 | import zio._ 8 | import zio.query.ZQuery 9 | import zio.telemetry.opentelemetry.tracing.{ StatusMapper, Tracing } 10 | 11 | object FieldTracer { 12 | val wrapper = new FieldWrapper[Tracing] { 13 | def wrap[R <: Tracing]( 14 | query: ZQuery[R, CalibanError.ExecutionError, ResponseValue], 15 | info: FieldInfo 16 | ): ZQuery[R, CalibanError.ExecutionError, ResponseValue] = 17 | ZQuery.acquireReleaseWith( 18 | ZIO.serviceWithZIO[Tracing](_.spanUnsafe(info.name)) 19 | ) { case (_, end) => end } { case (span, _) => 20 | query.foldCauseQuery( 21 | cause => 22 | ZQuery.failCause { 23 | val status = 24 | cause.failureOption.flatMap(StatusMapper.default.failure.lift).fold(StatusCode.ERROR)(_.statusCode) 25 | span.setStatus(status, cause.prettyPrint) 26 | cause 27 | }, 28 | value => ZQuery.succeed(value) 29 | ) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tracing/src/main/scala/caliban/tracing/TracingWrapper.scala: -------------------------------------------------------------------------------- 1 | package caliban.tracing 2 | 3 | object TracingWrapper { 4 | val traced = SchemaTracer.wrapper @@ FieldTracer.wrapper 5 | } 6 | -------------------------------------------------------------------------------- /vuepress/docs/.vuepress/public/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostdogpr/caliban/24a568744f6708a4f400d7f8f5f158b002f29806/vuepress/docs/.vuepress/public/.nojekyll -------------------------------------------------------------------------------- /vuepress/docs/.vuepress/public/caliban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostdogpr/caliban/24a568744f6708a4f400d7f8f5f158b002f29806/vuepress/docs/.vuepress/public/caliban.png -------------------------------------------------------------------------------- /vuepress/docs/.vuepress/styles/index.styl: -------------------------------------------------------------------------------- 1 | .home .hero img { 2 | max-height: 200px !important; 3 | } 4 | -------------------------------------------------------------------------------- /vuepress/docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: /caliban.svg 4 | actionText: Get Started → 5 | actionLink: /docs/ 6 | features: 7 | - title: High performance 8 | details: While all public interfaces are pure and immutable, the library's internals are optimized for speed. 9 | - title: Minimal boilerplate 10 | details: No need to manually define schemas for every type in your API. Let the compiler handle the tedious work. 11 | - title: Excellent interoperability 12 | details: Out-of-the-box support for major HTTP server libraries, effect types, JSON libraries, and more. 13 | --- -------------------------------------------------------------------------------- /vuepress/docs/about/README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | Caliban is a project created by Pierre Ricadat aka [@ghostdogpr](https://github.com/ghostdogpr). 4 | 5 | Current maintainers: 6 | - [@ghostdogpr](https://github.com/ghostdogpr) 7 | - [@paulpdaniels](https://github.com/paulpdaniels) 8 | - [@frekw](https://github.com/frekw) 9 | - [@kyri-petrou](https://github.com/kyri-petrou) 10 | 11 | The name is inspired by the SF novel and tv series [The Expanse](https://en.wikipedia.org/wiki/Caliban%27s_War). 12 | 13 | **Thanks**: 14 | - [@deckerst](https://github.com/deckerst) for the logo 15 | - [@yoohaemin](https://github.com/yoohaemin), [@adamgfraser](https://github.com/adamgfraser) and [@jdegoes](https://github.com/jdegoes) for the early feedback 16 | - the [Morpheus](https://morpheusgraphql.com/) library in Haskell for the inspiration leading to the GraphQL backend implementation 17 | - the [elm-graphql](https://github.com/dillonkearns/elm-graphql) library for the inspiration leading to the GraphQL client implementation 18 | - all Caliban's [contributors](https://github.com/ghostdogpr/caliban/graphs/contributors)! -------------------------------------------------------------------------------- /vuepress/docs/docs/tools.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | Caliban comes with a module called `caliban-tools` that exposes some useful features: 4 | - all the code generation features from `caliban-codegen-sbt`, so that you can use them without sbt: see `caliban.tools.Codegen`. 5 | - a client for GraphQL introspection: see `caliban.tools.IntrospectionClient`. 6 | - utilities for [stitching GraphQL schemas](stitching.md). 7 | - a way to [compare GraphQL schemas](schema-comparison.md), whether they come from Caliban or a remote server. 8 | 9 | ## Dependency 10 | 11 | ```scala 12 | "com.github.ghostdogpr" %% "caliban-tools" % "2.10.0" 13 | ``` 14 | -------------------------------------------------------------------------------- /vuepress/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "vuepress": "^1.9.9" 4 | }, 5 | "scripts": { 6 | "docs:dev": "vuepress dev docs", 7 | "docs:build": "vuepress build docs" 8 | } 9 | } 10 | --------------------------------------------------------------------------------