├── .git-blame-ignore-revs ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md ├── dependabot.yaml ├── labeler.yml ├── release-drafter.yml └── workflows │ ├── ci.yml │ ├── dependency-graph.yml │ ├── rebase-cmd-dispatch.yml │ ├── rebase-cmd.yml │ ├── scala-steward.yml │ └── test-report.yml ├── .gitignore ├── .readthedocs.yaml ├── .sbtopts ├── .scala-steward.conf ├── .scalafix.conf ├── .scalafmt.conf ├── LICENSE ├── README.md ├── banner.png ├── build.sbt ├── client ├── core │ └── src │ │ └── main │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── client │ │ └── ClientOutputParams.scala ├── http4s-client │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── client │ │ │ └── http4s │ │ │ ├── EndpointToHttp4sClient.scala │ │ │ ├── Http4sClientInterpreter.scala │ │ │ └── Http4sClientOptions.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── client │ │ └── http4s │ │ ├── Http4ClientStreamingTests.scala │ │ ├── Http4sClientBasicTests.scala │ │ ├── Http4sClientRequestTests.scala │ │ └── Http4sClientTests.scala ├── play-client │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── client │ │ │ └── play │ │ │ ├── EndpointToPlayClient.scala │ │ │ ├── PlayClientInterpreter.scala │ │ │ └── PlayClientOptions.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── client │ │ └── play │ │ ├── PlayClientBasicTests.scala │ │ ├── PlayClientStreamingTests.scala │ │ └── PlayClientTests.scala ├── play29-client │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── client │ │ │ └── play │ │ │ ├── EndpointToPlayClient.scala │ │ │ ├── PlayClientInterpreter.scala │ │ │ └── PlayClientOptions.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── client │ │ └── play │ │ ├── PlayClientBasicTests.scala │ │ ├── PlayClientStreamingTests.scala │ │ └── PlayClientTests.scala ├── sttp-client │ └── src │ │ ├── main │ │ ├── scala │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── client │ │ │ │ └── sttp │ │ │ │ ├── EndpointToSttpClient.scala │ │ │ │ ├── SttpClientInterpreter.scala │ │ │ │ ├── SttpClientOptions.scala │ │ │ │ └── WebSocketToPipe.scala │ │ ├── scalajs │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── client │ │ │ │ └── sttp │ │ │ │ ├── EndpointToSttpClientExtensions.scala │ │ │ │ ├── SttpClientInterpreterExtensions.scala │ │ │ │ └── ws │ │ │ │ ├── fs2 │ │ │ │ ├── TapirSttpClientFs2WebSockets.scala │ │ │ │ ├── WebSocketToFs2Pipe.scala │ │ │ │ └── package.scala │ │ │ │ └── zio │ │ │ │ ├── TapirSttpClientZioWebSockets.scala │ │ │ │ ├── WebSocketToZioPipe.scala │ │ │ │ └── package.scala │ │ ├── scalajvm-2 │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── client │ │ │ │ └── sttp │ │ │ │ └── ws │ │ │ │ └── akkahttp │ │ │ │ ├── TapirSttpClientAkkaHttpWebSockets.scala │ │ │ │ ├── WebSocketToAkkaPipe.scala │ │ │ │ └── package.scala │ │ ├── scalajvm │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── client │ │ │ │ └── sttp │ │ │ │ ├── EndpointToSttpClientExtensions.scala │ │ │ │ ├── SttpClientInterpreterExtensions.scala │ │ │ │ └── ws │ │ │ │ ├── fs2 │ │ │ │ ├── TapirSttpClientFs2WebSockets.scala │ │ │ │ ├── WebSocketToFs2Pipe.scala │ │ │ │ └── package.scala │ │ │ │ ├── pekkohttp │ │ │ │ ├── TapirSttpClientPekkoHttpWebSockets.scala │ │ │ │ ├── WebSocketToPekkoPipe.scala │ │ │ │ └── package.scala │ │ │ │ └── zio │ │ │ │ ├── TapirSttpClientZioWebSockets.scala │ │ │ │ ├── WebSocketToZioPipe.scala │ │ │ │ └── package.scala │ │ └── scalanative │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── client │ │ │ └── sttp │ │ │ ├── EndpointToSttpClientExtensions.scala │ │ │ └── SttpClientInterpreterExtensions.scala │ │ └── test │ │ ├── scala │ │ └── sttp │ │ │ └── tapir │ │ │ └── client │ │ │ └── sttp │ │ │ ├── SttpClientBasicTests.scala │ │ │ ├── SttpClientMultipartTests.scala │ │ │ └── SttpClientRequestTests.scala │ │ ├── scalajs │ │ └── sttp │ │ │ └── tapir │ │ │ └── client │ │ │ └── sttp │ │ │ ├── SttpClientTests.scala │ │ │ ├── SttpClientWebSocketZioTests.scala │ │ │ └── SttpClientZioTests.scala │ │ ├── scalajvm-2 │ │ └── sttp.tapir.client.sttp │ │ │ ├── SttpAkkaClientTests.scala │ │ │ └── SttpAkkaClientWebSocketTests.scala │ │ ├── scalajvm │ │ └── sttp │ │ │ └── tapir │ │ │ └── client │ │ │ └── sttp │ │ │ ├── SttpClientBasicZioTests.scala │ │ │ ├── SttpClientPekkoTests.scala │ │ │ ├── SttpClientStreamingTests.scala │ │ │ ├── SttpClientStreamingZioTests.scala │ │ │ ├── SttpClientTests.scala │ │ │ ├── SttpClientWebSocketPekkoTests.scala │ │ │ ├── SttpClientWebSocketTests.scala │ │ │ ├── SttpClientWebSocketZioTests.scala │ │ │ └── SttpClientZioTests.scala │ │ └── scalanative │ │ └── sttp │ │ └── tapir │ │ └── client │ │ └── sttp │ │ └── SttpClientTests.scala ├── sttp-client4 │ └── src │ │ ├── main │ │ ├── scala │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── client │ │ │ │ └── sttp4 │ │ │ │ ├── EndpointToSttpClient.scala │ │ │ │ ├── EndpointToSttpClientBase.scala │ │ │ │ ├── SttpClientInterpreter.scala │ │ │ │ ├── SttpClientOptions.scala │ │ │ │ ├── WebSocketToPipe.scala │ │ │ │ ├── package.scala │ │ │ │ ├── stream │ │ │ │ ├── StreamEndpointToSttpClient.scala │ │ │ │ ├── StreamSttpClientInterpreter.scala │ │ │ │ └── StreamsNotWebSockets.scala │ │ │ │ └── ws │ │ │ │ ├── WebSocketEndpointToSttpClient.scala │ │ │ │ └── WebSocketSttpClientInterpreter.scala │ │ ├── scalajs │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── client │ │ │ │ └── sttp4 │ │ │ │ ├── WebSocketEndpointToSttpClientExtensions.scala │ │ │ │ └── ws │ │ │ │ ├── fs2 │ │ │ │ ├── TapirSttpClientFs2WebSockets.scala │ │ │ │ ├── WebSocketToFs2Pipe.scala │ │ │ │ └── package.scala │ │ │ │ └── zio │ │ │ │ ├── TapirSttpClientZioWebSockets.scala │ │ │ │ ├── WebSocketToZioPipe.scala │ │ │ │ └── package.scala │ │ ├── scalajvm-2 │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── client │ │ │ │ └── sttp4 │ │ │ │ └── ws │ │ │ │ └── akkahttp │ │ │ │ ├── TapirSttpClientAkkaHttpWebSockets.scala │ │ │ │ ├── WebSocketToAkkaPipe.scala │ │ │ │ └── package.scala │ │ ├── scalajvm │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── client │ │ │ │ └── sttp4 │ │ │ │ ├── WebSocketEndpointToSttpClientExtensions.scala │ │ │ │ └── ws │ │ │ │ ├── fs2 │ │ │ │ ├── TapirSttpClientFs2WebSockets.scala │ │ │ │ ├── WebSocketToFs2Pipe.scala │ │ │ │ └── package.scala │ │ │ │ ├── pekkohttp │ │ │ │ ├── TapirSttpClientPekkoHttpWebSockets.scala │ │ │ │ ├── WebSocketToPekkoPipe.scala │ │ │ │ └── package.scala │ │ │ │ └── zio │ │ │ │ ├── TapirSttpClientZioWebSockets.scala │ │ │ │ ├── WebSocketToZioPipe.scala │ │ │ │ └── package.scala │ │ └── scalanative │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── client │ │ │ └── sttp4 │ │ │ └── WebSocketEndpointToSttpClientExtensions.scala │ │ └── test │ │ ├── scala │ │ └── sttp │ │ │ └── tapir │ │ │ └── client │ │ │ └── sttp4 │ │ │ ├── SttpClientMultipartTests.scala │ │ │ ├── SttpClientRequestTests.scala │ │ │ └── SttpClientSimpleTests.scala │ │ ├── scalajs │ │ └── sttp │ │ │ └── tapir │ │ │ └── client │ │ │ └── sttp4 │ │ │ ├── SttpClientTestsSender.scala │ │ │ └── ws │ │ │ ├── WebSocketSttpClientZioTests.scala │ │ │ └── WebSocketSttpClientZioTestsSender.scala │ │ ├── scalajvm │ │ └── sttp │ │ │ └── tapir │ │ │ └── client │ │ │ └── sttp4 │ │ │ ├── SttpClientTestsSender.scala │ │ │ ├── SttpClientZioTests.scala │ │ │ ├── SttpClientZioTestsSender.scala │ │ │ ├── stream │ │ │ ├── StreamSttpClientTests.scala │ │ │ ├── StreamSttpClientTestsSender.scala │ │ │ ├── StreamSttpClientZioTests.scala │ │ │ └── StreamSttpClientZioTestsSender.scala │ │ │ └── ws │ │ │ ├── WebSocketSttpClientPekkoTests.scala │ │ │ ├── WebSocketSttpClientPekkoTestsSender.scala │ │ │ ├── WebSocketSttpClientTests.scala │ │ │ ├── WebSocketSttpClientTestsSender.scala │ │ │ ├── WebSocketSttpClientZioTests.scala │ │ │ └── WebSocketSttpClientZioTestsSender.scala │ │ └── scalanative │ │ └── sttp │ │ └── tapir │ │ └── client │ │ └── sttp │ │ └── SttpClientTestsSender.scala ├── tests │ └── src │ │ └── main │ │ ├── scala │ │ └── sttp │ │ │ └── tapir │ │ │ └── client │ │ │ └── tests │ │ │ ├── ClientBasicTests.scala │ │ │ ├── ClientMultipartTests.scala │ │ │ ├── ClientStreamingTests.scala │ │ │ ├── ClientTests.scala │ │ │ └── ClientWebSocketTests.scala │ │ ├── scalajs │ │ └── sttp │ │ │ └── tapir │ │ │ └── client │ │ │ └── tests │ │ │ └── ClientTestsPlatform.scala │ │ ├── scalajvm │ │ └── sttp │ │ │ └── tapir │ │ │ └── client │ │ │ └── tests │ │ │ └── ClientTestsPlatform.scala │ │ └── scalanative │ │ └── sttp │ │ └── tapir │ │ └── client │ │ └── tests │ │ └── ClientTestsPlatform.scala └── testserver │ └── src │ └── main │ ├── resources │ └── logback.xml │ └── scala │ └── sttp │ └── tapir │ └── client │ └── tests │ └── HttpServer.scala ├── core └── src │ ├── main │ ├── boilerplate-gen │ │ └── sttp │ │ │ └── tapir │ │ │ ├── internal │ │ │ ├── ParamsToSeq.scala │ │ │ └── SeqToParams.scala │ │ │ └── typelevel │ │ │ ├── TupleArity.scala │ │ │ └── TupleOps.scala │ ├── boilerplate │ │ └── sttp │ │ │ └── tapir │ │ │ ├── internal │ │ │ ├── ParamsToSeq.scala.template │ │ │ └── SeqToParams.scala.template │ │ │ └── typelevel │ │ │ ├── TupleArity.scala.template │ │ │ └── TupleOps.scala.template │ ├── resources │ │ └── mimeByExtensions.txt │ ├── scala-2.13+ │ │ └── sttp │ │ │ └── tapir │ │ │ └── macros │ │ │ └── ModifyMacroSupport.scala │ ├── scala-2.13- │ │ └── sttp │ │ │ └── tapir │ │ │ ├── CodecExtensions2.scala │ │ │ └── macros │ │ │ └── ModifyMacroSupport.scala │ ├── scala-2 │ │ └── sttp │ │ │ └── tapir │ │ │ ├── generic │ │ │ ├── auto │ │ │ │ ├── SchemaDerivation.scala │ │ │ │ ├── SchemaMagnoliaDerivation.scala │ │ │ │ └── package.scala │ │ │ └── internal │ │ │ │ └── MagnoliaDerivedMacro.scala │ │ │ ├── internal │ │ │ ├── AttributeKeyMacro.scala │ │ │ ├── CaseClassUtil.scala │ │ │ ├── CodecEnumerationMacro.scala │ │ │ ├── CodecValueClassMacro.scala │ │ │ ├── Debug.scala │ │ │ ├── EndpointAnnotationsMacro.scala │ │ │ ├── EndpointInputAnnotationsMacro.scala │ │ │ ├── EndpointOutputAnnotationsMacro.scala │ │ │ ├── ErasureSameAsTypeMacro.scala │ │ │ ├── FormCodecMacro.scala │ │ │ ├── MapToMacro.scala │ │ │ ├── ModifySchemaMacro.scala │ │ │ ├── MultipartCodecMacro.scala │ │ │ ├── OneOfMacro.scala │ │ │ ├── SchemaAnnotationsMacro.scala │ │ │ ├── SchemaEnumerationMacro.scala │ │ │ ├── SchemaMapMacro.scala │ │ │ └── ValidatorEnumerationMacro.scala │ │ │ ├── macros │ │ │ ├── AttributeKeyMacros.scala │ │ │ ├── CodecMacros.scala │ │ │ ├── EndpointInputMacros.scala │ │ │ ├── EndpointMacros.scala │ │ │ ├── EndpointOutputMacros.scala │ │ │ ├── EndpointTransputMacros.scala │ │ │ ├── ErasureSameAsTypeMacros.scala │ │ │ ├── FormCodecMacros.scala │ │ │ ├── MultipartCodecMacros.scala │ │ │ ├── SchemaAnnotationsMacros.scala │ │ │ ├── SchemaMacros.scala │ │ │ └── ValidatorMacros.scala │ │ │ └── typelevel │ │ │ └── MatchTypeMacros.scala │ ├── scala-3-2.13+ │ │ └── sttp │ │ │ └── tapir │ │ │ ├── CodecExtensions2.scala │ │ │ └── model │ │ │ └── package.scala │ ├── scala-3 │ │ └── sttp │ │ │ └── tapir │ │ │ ├── generic │ │ │ └── auto │ │ │ │ ├── SchemaDerivation.scala │ │ │ │ ├── SchemaMagnoliaDerivation.scala │ │ │ │ └── package.scala │ │ │ ├── internal │ │ │ ├── AnnotationsMacros.scala │ │ │ ├── CaseClass.scala │ │ │ ├── CodecValueClassMacro.scala │ │ │ ├── CompileTimeErrors.scala │ │ │ ├── EnumerationMacros.scala │ │ │ ├── MappingMacros.scala │ │ │ ├── SNameMacros.scala │ │ │ └── SchemaAnnotationsMacro.scala │ │ │ ├── macros │ │ │ ├── AttributeKeyMacros.scala │ │ │ ├── CodecMacros.scala │ │ │ ├── EndpointInputMacros.scala │ │ │ ├── EndpointMacros.scala │ │ │ ├── EndpointOutputMacros.scala │ │ │ ├── EndpointTransputMacros.scala │ │ │ ├── ErasureSameAsTypeMacros.scala │ │ │ ├── FormCodecMacros.scala │ │ │ ├── ModifyMacroSupport.scala │ │ │ ├── MultipartCodecMacros.scala │ │ │ ├── SchemaAnnotationsMacros.scala │ │ │ ├── SchemaMacros.scala │ │ │ ├── ValidatorMacros.scala │ │ │ └── union_derivation.scala │ │ │ └── typelevel │ │ │ └── MatchTypeMacros.scala │ ├── scala │ │ └── sttp │ │ │ └── tapir │ │ │ ├── Codec.scala │ │ │ ├── CodecFormat.scala │ │ │ ├── DecodeResult.scala │ │ │ ├── Endpoint.scala │ │ │ ├── EndpointIO.scala │ │ │ ├── FieldName.scala │ │ │ ├── FileRange.scala │ │ │ ├── InputStreamRange.scala │ │ │ ├── Mapping.scala │ │ │ ├── Schema.scala │ │ │ ├── SchemaAnnotations.scala │ │ │ ├── SchemaType.scala │ │ │ ├── Tapir.scala │ │ │ ├── TapirAliases.scala │ │ │ ├── TapirAuth.scala │ │ │ ├── Validator.scala │ │ │ ├── attribute.scala │ │ │ ├── capabilities │ │ │ └── NoStreams.scala │ │ │ ├── generic │ │ │ ├── Configuration.scala │ │ │ └── Derived.scala │ │ │ ├── internal │ │ │ ├── ShowPathTemplate.scala │ │ │ └── package.scala │ │ │ ├── macros │ │ │ ├── CreateDerivedEnumerationCodec.scala │ │ │ ├── CreateDerivedEnumerationSchema.scala │ │ │ └── ModifyMacroFunctorSupport.scala │ │ │ ├── model │ │ │ ├── Delimited.scala │ │ │ ├── ServerRequest.scala │ │ │ ├── StatusCodeRange.scala │ │ │ ├── UsernamePassword.scala │ │ │ └── ws.scala │ │ │ ├── package.scala │ │ │ ├── server │ │ │ ├── PartialServerEndpoint.scala │ │ │ ├── PartialServerEndpointSync.scala │ │ │ ├── PartialServerEndpointWithSecurityOutput.scala │ │ │ ├── PartialServerEndpointWithSecurityOutputSync.scala │ │ │ └── ServerEndpoint.scala │ │ │ └── typelevel │ │ │ ├── BinaryTupleOp.scala │ │ │ ├── ErasureSameAsType.scala │ │ │ ├── MatchType.scala │ │ │ └── ParamConcat.scala │ ├── scalajs │ │ └── sttp │ │ │ └── tapir │ │ │ ├── CodecExtensions.scala │ │ │ ├── Defaults.scala │ │ │ ├── TapirExtensions.scala │ │ │ ├── internal │ │ │ └── UrlencodedData.scala │ │ │ └── static │ │ │ └── TapirStaticContentEndpoints.scala │ ├── scalajvm │ │ └── sttp │ │ │ └── tapir │ │ │ ├── CodecExtensions.scala │ │ │ ├── Defaults.scala │ │ │ ├── TapirExtensions.scala │ │ │ ├── internal │ │ │ ├── MimeByExtensionDB.scala │ │ │ └── UrlencodedData.scala │ │ │ └── static │ │ │ ├── Files.scala │ │ │ ├── Resources.scala │ │ │ ├── TapirStaticContentEndpoints.scala │ │ │ ├── model.scala │ │ │ └── package.scala │ └── scalanative │ │ └── sttp │ │ └── tapir │ │ ├── CodecExtensions.scala │ │ ├── Defaults.scala │ │ ├── TapirExtensions.scala │ │ ├── internal │ │ └── UrlencodedData.scala │ │ └── static │ │ └── TapirStaticContentEndpoints.scala │ └── test │ ├── scala-2 │ └── sttp │ │ └── tapir │ │ ├── LegacySchemaApplyValidationTest.scala │ │ ├── SchemaMacroTest2.scala │ │ ├── SchemaMacroTestData2.scala │ │ └── generic │ │ └── LegacySchemaGenericAutoTest.scala │ ├── scala-3-2.13+ │ └── sttp │ │ └── tapir │ │ └── CodecDelimitedTest.scala │ ├── scala-3 │ └── sttp │ │ └── tapir │ │ ├── CodecScala3Test.scala │ │ ├── EndpointScala3Test.scala │ │ ├── SchemaMacroScala3Test.scala │ │ └── ValidatorScala3EnumTest.scala │ ├── scala │ └── sttp │ │ └── tapir │ │ ├── AttributeTest.scala │ │ ├── CodecTest.scala │ │ ├── EndpointTest.scala │ │ ├── SchemaAnnotationsTest.scala │ │ ├── SchemaAnnotationsTestData.scala │ │ ├── SchemaApplyValidationTest.scala │ │ ├── SchemaApplyValidationTestData.scala │ │ ├── SchemaMacroTest.scala │ │ ├── SchemaMacroTestData.scala │ │ ├── SchemaTest.scala │ │ ├── TestUtil.scala │ │ ├── ValidatorTest.scala │ │ ├── annotations │ │ └── DeriveEndpointIOTest.scala │ │ ├── generic │ │ ├── EnumsCodecDerivationTest.scala │ │ ├── FormCodecDerivationTest.scala │ │ ├── MultipartCodecDerivationTest.scala │ │ ├── SchemaGenericAutoTest.scala │ │ └── ValueClassCodecDerivationTest.scala │ │ ├── internal │ │ ├── RichEndpointInputTest.scala │ │ ├── RichEndpointOutputTest.scala │ │ └── UrlencodedDataTest.scala │ │ ├── namespacing │ │ └── SchemaMacroNamespaceTest.scala │ │ ├── testdata │ │ ├── BuiltinTypenameCollisionCaseClass.scala │ │ └── BuiltinTypenameCollisionEnum.scala │ │ └── typelevel │ │ ├── MatchTypeTest.scala │ │ └── ParamConcatTest.scala │ ├── scalajs │ └── sttp │ │ └── tapir │ │ ├── EndpointTestExtensions.scala │ │ ├── generic │ │ ├── FormCodecDerivationTestExtensions.scala │ │ └── MultipartCodecDerivationTestExtensions.scala │ │ └── typelevel │ │ └── MatchTypeTestExtensions.scala │ ├── scalajvm │ └── sttp │ │ └── tapir │ │ ├── CodecTestDateTime.scala │ │ ├── EndpointTestExtensions.scala │ │ ├── SchemaSerialisationTest.scala │ │ ├── generic │ │ ├── FormCodecDerivationTestExtensions.scala │ │ ├── MultipartCodecDerivationTestExtensions.scala │ │ └── SchemaGenericSemiautoTest.scala │ │ ├── internal │ │ └── MimeByExtensionDBTest.scala │ │ └── typelevel │ │ └── MatchTypeTestExtensions.scala │ └── scalanative │ └── sttp │ └── tapir │ ├── EndpointTestExtensions.scala │ ├── generic │ ├── FormCodecDerivationTestExtensions.scala │ └── MultipartCodecDerivationTestExtensions.scala │ └── typelevel │ └── MatchTypeTestExtensions.scala ├── doc ├── .gitignore ├── .python-version ├── Makefile ├── _static │ └── css │ │ └── custom.css ├── adopters.md ├── adopters │ ├── adobe.png │ ├── broad.png │ ├── budgetbakers.svg │ ├── carvana.svg │ ├── colisweb.png │ ├── dpg-recruitment.svg │ ├── ematiq.png │ ├── flo.svg │ ├── fugo.png │ ├── hootsuite.png │ ├── hunters.png │ ├── iceo.png │ ├── kaizo.png │ ├── kelkoogroup.png │ ├── kensu.png │ ├── moia.png │ ├── moneyfarm.png │ ├── ocado.png │ ├── pits.svg │ ├── process_street.png │ ├── softwaremill.png │ ├── swissborg.png │ ├── swisscom.svg │ ├── tranzzo.svg │ └── wegtam.svg ├── adr │ ├── 0001-explicit-encode-function-on-validator-enum.md │ ├── 0002-codecs-schemas-validators.md │ ├── 0003-shape-of-ios.md │ ├── 0004-codecs-parametrised-by-raw-values.md │ ├── 0005-rethinking-codecs.md │ ├── 0006-partial-server-logic.md │ ├── 0007-codecs-schemas-configuration.md │ ├── 0008-effectful-maps.md │ ├── 0009-lists-are-optional.md │ ├── 0010-security-refactoring.md │ ├── 0011-dedicated-schema-macros.md │ └── 0012-extracting-schema-as-a-module.md ├── client │ ├── http4s.md │ ├── play.md │ ├── sttp.md │ └── sttp4.md ├── conf.py ├── docs │ ├── asyncapi.md │ ├── json-schema.md │ └── openapi.md ├── endpoint │ ├── basics.md │ ├── codecs.md │ ├── contenttype.md │ ├── customtypes.md │ ├── enumerations.md │ ├── forms.md │ ├── integrations.md │ ├── ios.md │ ├── json.md │ ├── oneof.md │ ├── pickler.md │ ├── schemas.md │ ├── security.md │ ├── static.md │ ├── streaming.md │ ├── validation.md │ ├── websockets.md │ └── xml.md ├── examples.md ├── external.md ├── flake.lock ├── flake.nix ├── generate.md ├── generator │ └── sbt-openapi-codegen.md ├── how-tos │ └── delimited-path-parameters.md ├── index.md ├── logo.png ├── make.bat ├── other │ ├── adr.md │ ├── contributing.md │ ├── goals.md │ ├── grpc.md │ ├── migrating.md │ ├── mytapir.md │ ├── other_interpreters.md │ ├── stability.md │ └── troubleshooting.md ├── press │ └── tapir-1-0-released.md ├── quickstart.md ├── requirements.txt ├── scala_2_3_platforms.md ├── server │ ├── akkahttp.md │ ├── armeria.md │ ├── aws.md │ ├── debugging.md │ ├── errors.md │ ├── finatra.md │ ├── http4s.md │ ├── interceptors.md │ ├── jdkhttp.md │ ├── logic.md │ ├── netty.md │ ├── nima.md │ ├── observability.md │ ├── options.md │ ├── overview.md │ ├── path.md │ ├── pekkohttp.md │ ├── play.md │ ├── vertx.md │ ├── zio-http4s.md │ └── ziohttp.md ├── support.md ├── testing.md ├── tutorials │ ├── 01_hello_world.md │ ├── 02_openapi_docs.md │ ├── 03_json.md │ ├── 04_errors.md │ ├── 05_multiple_inputs_outputs.md │ ├── 06_error_variants.md │ └── 07_cats_effect.md └── watch.sh ├── docs ├── apispec-docs │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── docs │ │ │ └── apispec │ │ │ ├── DocsExtension.scala │ │ │ ├── DocsExtensions.scala │ │ │ ├── EndpointInputMapper.scala │ │ │ ├── SecurityRequirementsForEndpoints.scala │ │ │ ├── SecuritySchemesForEndpoints.scala │ │ │ ├── package.scala │ │ │ └── schema │ │ │ ├── MetaSchema.scala │ │ │ ├── SchemaKey.scala │ │ │ ├── SchemasForEndpoints.scala │ │ │ ├── TSchemaToASchema.scala │ │ │ ├── TapirSchemaToJsonSchema.scala │ │ │ ├── ToKeyedSchemas.scala │ │ │ ├── ToSchemaReference.scala │ │ │ └── schema.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── docs │ │ └── apispec │ │ └── schema │ │ └── TapirSchemaToJsonSchemaTest.scala ├── asyncapi-docs │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── docs │ │ │ └── asyncapi │ │ │ ├── AsyncAPIDocsOptions.scala │ │ │ ├── AsyncAPIInterpreter.scala │ │ │ ├── EndpointToAsyncAPIComponents.scala │ │ │ ├── EndpointToAsyncAPIDocs.scala │ │ │ ├── EndpointToAsyncAPIWebSocketChannel.scala │ │ │ ├── ExampleConverter.scala │ │ │ ├── MessagesForEndpoints.scala │ │ │ └── package.scala │ │ └── test │ │ ├── resources │ │ ├── expected_binding.yml │ │ ├── expected_coproduct_with_discriminator.yml │ │ ├── expected_description_header.yml │ │ ├── expected_description_query.yml │ │ ├── expected_extensions.yml │ │ ├── expected_flags_header.yml │ │ ├── expected_flags_query.yml │ │ ├── expected_json_custom_schema_name.yml │ │ ├── expected_json_example_name_summary.yml │ │ ├── expected_json_examples.yml │ │ ├── expected_json_json.yml │ │ ├── expected_required_parameters.yml │ │ ├── expected_security.yml │ │ ├── expected_string.yml │ │ ├── expected_string_json.yml │ │ └── expected_two_endpoints.yml │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── docs │ │ └── asyncapi │ │ └── VerifyAsyncAPIYamlTest.scala ├── openapi-docs │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── docs │ │ │ └── openapi │ │ │ ├── CodecToMediaType.scala │ │ │ ├── EndpointInputToDecodeFailureOutput.scala │ │ │ ├── EndpointInputToParameterConverter.scala │ │ │ ├── EndpointToOpenAPIComponents.scala │ │ │ ├── EndpointToOpenAPIDocs.scala │ │ │ ├── EndpointToOpenAPIPaths.scala │ │ │ ├── EndpointToOperationResponse.scala │ │ │ ├── ExampleConverter.scala │ │ │ ├── OpenAPIDocsInterpreter.scala │ │ │ └── OpenAPIDocsOptions.scala │ │ └── test │ │ ├── resources │ │ ├── coproduct │ │ │ ├── expected_coproduct.yml │ │ │ ├── expected_coproduct_discriminator.yml │ │ │ ├── expected_coproduct_discriminator_enum.yml │ │ │ ├── expected_coproduct_discriminator_nested.yml │ │ │ ├── expected_coproduct_discriminator_with_enum_circe.yml │ │ │ ├── expected_coproduct_independent.yml │ │ │ ├── expected_coproduct_nested.yml │ │ │ ├── expected_coproduct_wrapped.yml │ │ │ ├── expected_flat_either.yml │ │ │ ├── expected_generic_coproduct.yml │ │ │ ├── expected_recursive_coproducts.yml │ │ │ └── expected_unfolded_coproduct_unfolded_array.yml │ │ ├── decode_failure_output │ │ │ ├── expected_default_400_response.yml │ │ │ ├── expected_json_response_defined_in_options.yml │ │ │ ├── expected_no_400_response.yml │ │ │ ├── expected_response_defined_in_options.yml │ │ │ └── expected_user_defined_response.yml │ │ ├── enum │ │ │ ├── expected_enum_collections.yml │ │ │ ├── expected_enum_in_delimited_query.yml │ │ │ ├── expected_enum_in_delimited_query_with_default.yml │ │ │ ├── expected_enumeratum_enum_adding_default_when_encoded_value_specified.yml │ │ │ ├── expected_enumeratum_enum_collection_component.yml │ │ │ ├── expected_enumeratum_enum_component.yml │ │ │ ├── expected_enumeratum_enum_default.yml │ │ │ ├── expected_enumeratum_enum_not_adding_default_when_no_encoded_value_specified.yml │ │ │ ├── expected_enumeratum_enum_using_first_specified_default_value.yml │ │ │ ├── expected_enumeratum_int_enum_component.yml │ │ │ └── expected_trait_enum_component.yml │ │ ├── example │ │ │ ├── expected_byte_buffer_example.yml │ │ │ ├── expected_examples_of_list_and_not_list_types.yml │ │ │ ├── expected_fixed_header_example.yml │ │ │ ├── expected_multiple_examples.yml │ │ │ ├── expected_multiple_examples_with_default_names.yml │ │ │ ├── expected_multiple_examples_with_explicit_and_default_names.yml │ │ │ ├── expected_multiple_examples_with_names.yml │ │ │ ├── expected_schema_example.yml │ │ │ ├── expected_schema_example_multiple_value.yml │ │ │ ├── expected_single_example_with_description.yml │ │ │ ├── expected_single_example_with_name.yml │ │ │ ├── expected_single_example_with_summary.yml │ │ │ └── expected_stream_example.yml │ │ ├── expected.json │ │ ├── expected.yml │ │ ├── expected_additional_properties.yml │ │ ├── expected_arbitrary_json_output.yml │ │ ├── expected_array_no_format.yml │ │ ├── expected_callbacks.yml │ │ ├── expected_custom_operation_id.yml │ │ ├── expected_custom_schema_name.yml │ │ ├── expected_date_time.yml │ │ ├── expected_default_and_example_on_nested_option_field.yml │ │ ├── expected_default_query_param.yml │ │ ├── expected_default_request_body.yml │ │ ├── expected_deprecated.yml │ │ ├── expected_descriptions_in_nested_custom_schemas.yml │ │ ├── expected_double_quoted.yml │ │ ├── expected_empty.yml │ │ ├── expected_enumeration_values.yml │ │ ├── expected_exclusive_bounds.yml │ │ ├── expected_explicit_content_type_header.yml │ │ ├── expected_extensions.yml │ │ ├── expected_extensions_schema.yml │ │ ├── expected_extensions_security_scheme.yml │ │ ├── expected_external.yml │ │ ├── expected_fields_with_additional_properties.yml │ │ ├── expected_fixed_header_input_request.yml │ │ ├── expected_fixed_header_output_response.yml │ │ ├── expected_fixed_status_code.yml │ │ ├── expected_fixed_status_code_2.yml │ │ ├── expected_flag_query.yml │ │ ├── expected_full_schema_names.yml │ │ ├── expected_general_info.yml │ │ ├── expected_generic.yml │ │ ├── expected_json_query_param.yml │ │ ├── expected_literal.yml │ │ ├── expected_localDateTime.yml │ │ ├── expected_map_with_plain_values.yml │ │ ├── expected_multi_empty_outputs.yml │ │ ├── expected_multipart.yml │ │ ├── expected_multiple.yml │ │ ├── expected_multiple_servers.yml │ │ ├── expected_nullable_option_class_field.yml │ │ ├── expected_nullable_option_class_field_303.yml │ │ ├── expected_nullable_option_field.yml │ │ ├── expected_nullable_option_field_303.yml │ │ ├── expected_optional_header.yml │ │ ├── expected_prepended_input.yml │ │ ├── expected_recursive.yml │ │ ├── expected_same_fullnames.yml │ │ ├── expected_single_server_with_variables.yml │ │ ├── expected_streaming.yml │ │ ├── expected_string_body_response.yml │ │ ├── expected_tags.yml │ │ ├── expected_type_and_description_for_circe_json.yml │ │ ├── expected_unfolded_array.yml │ │ ├── expected_unfolded_array_unfolded_object.yml │ │ ├── expected_unfolded_array_with_unique_items.yml │ │ ├── expected_unfolded_hierarchy.yml │ │ ├── expected_unfolded_object_unfolded_array.yml │ │ ├── expected_unfolded_option.yml │ │ ├── expected_unfolded_option_description.yml │ │ ├── expected_unique_open_api_parameters.yml │ │ ├── hide_in_docs.yml │ │ ├── multi_customise_schema │ │ │ ├── expected_deprecated_array_field.yml │ │ │ ├── expected_deprecated_optional_field.yml │ │ │ ├── inlined.yml │ │ │ ├── nested_body.yml │ │ │ └── top_level_body.yml │ │ ├── oneOf │ │ │ ├── expected_multiple_media_types_common_schema.yml │ │ │ ├── expected_multiple_media_types_different_schema.yml │ │ │ ├── expected_one_of_status_code_range.yml │ │ │ ├── expected_one_of_status_codes.yml │ │ │ ├── expected_status_codes.yml │ │ │ ├── expected_status_codes_with_empty_output.yaml │ │ │ ├── expected_the_same_status_codes.yml │ │ │ └── expected_the_same_status_codes_and_content_types.yml │ │ ├── oneOfBody │ │ │ ├── expected_in_json_text_range.yml │ │ │ ├── expected_in_json_xml_hidden.yml │ │ │ ├── expected_in_json_xml_text.yml │ │ │ └── expected_out_json_xml_text.yml │ │ ├── security │ │ │ ├── expected_auth.yml │ │ │ ├── expected_auth_with_bearer_format.yml │ │ │ ├── expected_auth_with_description.yml │ │ │ ├── expected_auth_with_named_schemes.yml │ │ │ ├── expected_hide_security_body.yml │ │ │ ├── expected_multiple_optional_auth.yml │ │ │ ├── expected_multiple_optional_auth_oauth2.yml │ │ │ ├── expected_multiple_optional_grouped_auth.yml │ │ │ ├── expected_oauth2.yml │ │ │ └── expected_optional_auth.yml │ │ └── validator │ │ │ ├── expected_valid_additional_properties.yml │ │ │ ├── expected_valid_body_collection.yml │ │ │ ├── expected_valid_body_enum.yml │ │ │ ├── expected_valid_body_wrapped.yml │ │ │ ├── expected_valid_coproduct.yml │ │ │ ├── expected_valid_enum_cats_nel.yml │ │ │ ├── expected_valid_enum_class.yml │ │ │ ├── expected_valid_enum_class_wrapped_in_option.yml │ │ │ ├── expected_valid_enum_object.yml │ │ │ ├── expected_valid_enum_values.yml │ │ │ ├── expected_valid_enumeratum.yml │ │ │ ├── expected_valid_enumeratum_with_metadata.yml │ │ │ ├── expected_valid_int_array.yml │ │ │ ├── expected_valid_modified_array_objects.yml │ │ │ ├── expected_valid_modified_array_strings.yml │ │ │ ├── expected_valid_optional_body_wrapped.yml │ │ │ ├── expected_valid_optional_coproduct.yml │ │ │ ├── expected_valid_query_tagged.yml │ │ │ ├── expected_valid_query_wrapped.yml │ │ │ ├── expected_validator_encode_from_codec.yml │ │ │ ├── expected_validator_infer_encode_to_raw.yml │ │ │ ├── expected_validator_with_custom_naming.yml │ │ │ └── expected_whole_numbers_in_examples_of_string_schemas.yml │ │ ├── scalajvm-2 │ │ └── sttp │ │ │ └── tapir │ │ │ └── docs │ │ │ └── openapi │ │ │ ├── VerifyYamlEnumeratumTest.scala │ │ │ └── dtos │ │ │ ├── VerifyYamlCoproductTestData2.scala │ │ │ └── VerifyYamlTestData2.scala │ │ ├── scalajvm-3-2.13+ │ │ └── sttp │ │ │ └── tapir │ │ │ └── docs │ │ │ └── openapi │ │ │ └── VerifyYamlEnumerationTest.scala │ │ ├── scalajvm-3 │ │ └── sttp │ │ │ └── tapir │ │ │ └── docs │ │ │ └── openapi │ │ │ └── dtos │ │ │ ├── VerifyYamlCoproductTestData2.scala │ │ │ └── VerifyYamlTestData2.scala │ │ └── scalajvm │ │ └── sttp │ │ └── tapir │ │ └── docs │ │ └── openapi │ │ ├── EndpointToOpenAPIDocsTest.scala │ │ ├── VerifyJsonTest.scala │ │ ├── VerifyYamlCoproductTest.scala │ │ ├── VerifyYamlDecodeFailureOutputTest.scala │ │ ├── VerifyYamlExampleTest.scala │ │ ├── VerifyYamlMultiCustomiseSchemaTest.scala │ │ ├── VerifyYamlOneOfBodyTest.scala │ │ ├── VerifyYamlOneOfTest.scala │ │ ├── VerifyYamlSecurityTest.scala │ │ ├── VerifyYamlTest.scala │ │ ├── VerifyYamlValidatorTest.scala │ │ ├── dtos │ │ ├── Book.scala │ │ ├── VerifyYamlCoproductTestData.scala │ │ ├── VerifyYamlTestData.scala │ │ ├── VerifyYamlValidatorTestData.scala │ │ ├── a │ │ │ └── Pet.scala │ │ └── b │ │ │ └── Pet.scala │ │ └── package.scala ├── openapi-verifier │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── docs │ │ │ └── openapi │ │ │ └── OpenAPIVerifier.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── docs │ │ └── openapi │ │ └── OpenApiVerifierTest.scala ├── redoc-bundle │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── redoc │ │ │ └── bundle │ │ │ └── RedocInterpreter.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── redoc │ │ └── bundle │ │ └── RedocInterpreterTest.scala ├── redoc │ └── src │ │ └── main │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── redoc │ │ ├── Redoc.scala │ │ └── RedocUIOptions.scala ├── swagger-ui-bundle │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── swagger │ │ │ └── bundle │ │ │ └── SwaggerInterpreter.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── swagger │ │ └── bundle │ │ └── SwaggerInterpreterTest.scala └── swagger-ui │ └── src │ ├── main │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── swagger │ │ ├── SwaggerUI.scala │ │ └── SwaggerUIOptions.scala │ └── test │ └── scala │ └── sttp │ └── tapir │ └── swagger │ └── SwaggerUITest.scala ├── examples └── src │ └── main │ ├── resources │ ├── logback.xml │ └── webapp │ │ ├── img │ │ └── logo.png │ │ └── index.html │ └── scala │ └── sttp │ └── tapir │ └── examples │ ├── HelloWorldHttp4sServer.scala │ ├── HelloWorldNettyCatsServer.scala │ ├── HelloWorldZioHttpServer.scala │ ├── ZioEnvExampleHttp4sServer.scala │ ├── ZioExampleHttp4sServer.scala │ ├── ZioExampleZioHttpServer.scala │ ├── ZioPartialServerLogicHttp4s.scala │ ├── booksExample.scala │ ├── booksPicklerExample.scala │ ├── client │ └── Http4sClientExample.scala │ ├── custom_types │ ├── EndpointWithCustomTypes.scala │ ├── booksExampleSemiauto.scala │ ├── commaSeparatedQueryParameter.scala │ ├── enumQueryParameter.scala │ └── sealedTraitWithDiscriminator.scala │ ├── errors │ ├── ErrorUnionTypesHttp4sServer.scala │ ├── customErrorsOnDecodeFailurePekkoServer.scala │ ├── errorAsJson.scala │ ├── errorOutputsPekkoServer.scala │ └── optionalValueExample.scala │ ├── helloWorldArmeriaServer.scala │ ├── helloWorldJdkHttpServer.scala │ ├── helloWorldNettyFutureServer.scala │ ├── helloWorldNettySyncServer.scala │ ├── helloWorldPekkoServer.scala │ ├── json │ ├── circeAutoDerivationNettySyncServer.scala │ ├── circeNullBody.scala │ └── jsoniterNettySyncServer.scala │ ├── logging │ └── ZioLoggingWithCorrelationIdNettyServer.scala │ ├── multipart │ └── multipartFormUploadPekkoServer.scala │ ├── observability │ ├── OpenTelemetryTracingExample.scala │ ├── Otel4sTracingExample.scala │ ├── ZioMetricsExample.scala │ ├── datadogMetricsExample.scala │ ├── openTelemetryMetricsExample.scala │ └── prometheusMetricsExample.scala │ ├── openapi │ ├── MultipleEndpointsDocumentationHttp4sServer.scala │ ├── RedocContextPathHttp4sServer.scala │ ├── RedocZioHttpServer.scala │ ├── multipleEndpointsDocumentationPekkoServer.scala │ ├── openapiExtensions.scala │ └── swaggerUIOAuth2PekkoServer.scala │ ├── schema │ └── customisingSchemas.scala │ ├── security │ ├── OAuth2GithubHttp4sServer.scala │ ├── ServerSecurityLogicZio.scala │ ├── basicAuthenticationPekkoServer.scala │ ├── corsInterceptorPekkoServer.scala │ ├── corsInterceptorVertxServer.scala │ ├── csrfTokens.scala │ ├── externalSecurityInterceptor.scala │ ├── serverSecurityLogicPekko.scala │ └── serverSecurityLogicRefreshCookiesPekko.scala │ ├── sse │ └── sseNettySyncServer.scala │ ├── static_content │ ├── staticContentFromFilesNettyServer.scala │ ├── staticContentFromFilesPekkoServer.scala │ ├── staticContentFromResourcesPekkoServer.scala │ └── staticContentSecurePekkoServer.scala │ ├── status_code │ └── statusCodeNettyServer.scala │ ├── streaming │ ├── ProxyHttp4sFs2Server.scala │ ├── StreamingHttp4sFs2Server.scala │ ├── StreamingHttp4sFs2ServerOrError.scala │ ├── StreamingNettyFs2Server.scala │ ├── StreamingNettyZioServer.scala │ ├── StreamingZioHttpServer.scala │ └── streamingPekkoServer.scala │ ├── testing │ ├── CatsServerStubInterpreter.scala │ ├── PekkoServerStubInterpreter.scala │ └── SttpMockServerClientExample.scala │ └── websocket │ ├── WebSocketChatNettySyncServer.scala │ ├── WebSocketHttp4sServer.scala │ ├── WebSocketNettySyncServer.scala │ ├── WebSocketZioHttpCustomCodecServer.scala │ ├── WebSocketZioHttpJsonServer.scala │ └── webSocketPekkoServer.scala ├── files └── src │ ├── main │ ├── scalajs │ │ └── sttp │ │ │ └── tapir │ │ │ └── files │ │ │ └── TapirStaticContentEndpoints.scala │ ├── scalajvm │ │ └── sttp │ │ │ └── tapir │ │ │ └── files │ │ │ ├── Files.scala │ │ │ ├── Resources.scala │ │ │ ├── TapirStaticContentEndpoints.scala │ │ │ ├── model.scala │ │ │ └── package.scala │ └── scalanative │ │ └── sttp │ │ └── tapir │ │ └── files │ │ └── TapirStaticContentEndpoints.scala │ └── test │ └── scalajvm │ └── sttp │ └── tapir │ └── FilesSpec.scala ├── generated-doc └── out │ ├── .gitignore │ ├── .python-version │ ├── Makefile │ ├── _static │ └── css │ │ └── custom.css │ ├── adopters.md │ ├── adopters │ ├── adobe.png │ ├── broad.png │ ├── budgetbakers.svg │ ├── carvana.svg │ ├── colisweb.png │ ├── dpg-recruitment.svg │ ├── ematiq.png │ ├── flo.svg │ ├── fugo.png │ ├── hootsuite.png │ ├── hunters.png │ ├── iceo.png │ ├── kaizo.png │ ├── kelkoogroup.png │ ├── kensu.png │ ├── moia.png │ ├── moneyfarm.png │ ├── ocado.png │ ├── pits.svg │ ├── process_street.png │ ├── softwaremill.png │ ├── swissborg.png │ ├── swisscom.svg │ ├── tranzzo.svg │ └── wegtam.svg │ ├── adr │ ├── 0001-explicit-encode-function-on-validator-enum.md │ ├── 0002-codecs-schemas-validators.md │ ├── 0003-shape-of-ios.md │ ├── 0004-codecs-parametrised-by-raw-values.md │ ├── 0005-rethinking-codecs.md │ ├── 0006-partial-server-logic.md │ ├── 0007-codecs-schemas-configuration.md │ ├── 0008-effectful-maps.md │ ├── 0009-lists-are-optional.md │ ├── 0010-security-refactoring.md │ ├── 0011-dedicated-schema-macros.md │ └── 0012-extracting-schema-as-a-module.md │ ├── client │ ├── http4s.md │ ├── play.md │ ├── sttp.md │ └── sttp4.md │ ├── conf.py │ ├── docs │ ├── asyncapi.md │ ├── json-schema.md │ └── openapi.md │ ├── endpoint │ ├── basics.md │ ├── codecs.md │ ├── contenttype.md │ ├── customtypes.md │ ├── enumerations.md │ ├── forms.md │ ├── integrations.md │ ├── ios.md │ ├── json.md │ ├── oneof.md │ ├── pickler.md │ ├── schemas.md │ ├── security.md │ ├── static.md │ ├── streaming.md │ ├── validation.md │ ├── websockets.md │ └── xml.md │ ├── examples.md │ ├── external.md │ ├── flake.lock │ ├── flake.nix │ ├── generate.md │ ├── generator │ └── sbt-openapi-codegen.md │ ├── how-tos │ └── delimited-path-parameters.md │ ├── includes │ └── examples_list.md │ ├── index.md │ ├── logo.png │ ├── make.bat │ ├── other │ ├── adr.md │ ├── contributing.md │ ├── goals.md │ ├── grpc.md │ ├── migrating.md │ ├── mytapir.md │ ├── other_interpreters.md │ ├── stability.md │ └── troubleshooting.md │ ├── press │ └── tapir-1-0-released.md │ ├── quickstart.md │ ├── requirements.txt │ ├── scala_2_3_platforms.md │ ├── server │ ├── akkahttp.md │ ├── armeria.md │ ├── aws.md │ ├── debugging.md │ ├── errors.md │ ├── finatra.md │ ├── http4s.md │ ├── interceptors.md │ ├── jdkhttp.md │ ├── logic.md │ ├── netty.md │ ├── nima.md │ ├── observability.md │ ├── options.md │ ├── overview.md │ ├── path.md │ ├── pekkohttp.md │ ├── play.md │ ├── vertx.md │ ├── zio-http4s.md │ └── ziohttp.md │ ├── support.md │ ├── testing.md │ ├── tutorials │ ├── 01_hello_world.md │ ├── 02_openapi_docs.md │ ├── 03_json.md │ ├── 04_errors.md │ ├── 05_multiple_inputs_outputs.md │ ├── 06_error_variants.md │ └── 07_cats_effect.md │ └── watch.sh ├── grpc ├── examples │ └── src │ │ └── main │ │ ├── protobuf │ │ └── simple_books_example.proto │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── grpc │ │ └── examples │ │ └── GrpcSimpleBooksExample.scala ├── pbdirect │ └── src │ │ └── main │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── grpc │ │ └── protobuf │ │ └── pbdirect │ │ ├── TapirProtobufPbDirect.scala │ │ └── pbdirect.scala ├── pekko-examples │ └── src │ │ └── main │ │ ├── protobuf │ │ └── simple_books_example.proto │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── grpc │ │ └── examples │ │ └── PekkoGrpcSimpleBooksExample.scala └── protobuf │ └── src │ ├── main │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── grpc │ │ └── protobuf │ │ ├── EndpointToProtobufMessage.scala │ │ ├── EndpointToProtobufService.scala │ │ ├── ProtoRenderer.scala │ │ ├── ProtoSchemaGenerator.scala │ │ ├── ProtobufAttributes.scala │ │ ├── ProtobufInterpreter.scala │ │ ├── ProtobufScalarType.scala │ │ ├── model │ │ ├── Protobuf.scala │ │ ├── ProtobufMessage.scala │ │ ├── ProtobufMessageField.scala │ │ ├── ProtobufOptions.scala │ │ ├── ProtobufService.scala │ │ ├── ProtobufServiceMethod.scala │ │ └── package.scala │ │ └── package.scala │ └── test │ └── scala │ └── sttp │ └── tapir │ └── grpc │ └── protobuf │ ├── ProtoRendererTest.scala │ ├── ProtobufInterpreterTest.scala │ └── ProtobufMatchers.scala ├── integrations ├── cats-effect │ └── src │ │ └── main │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── integ │ │ └── cats │ │ └── effect │ │ └── CatsMonadError.scala ├── cats │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── integ │ │ │ └── cats │ │ │ ├── EndpointIOInstances.scala │ │ │ ├── ExampleInstances.scala │ │ │ ├── ModifyFunctorInstances.scala │ │ │ ├── MonadErrorSyntax.scala │ │ │ ├── ServerEndpointSyntax.scala │ │ │ ├── TapirCodecCats.scala │ │ │ ├── ValidatorCats.scala │ │ │ ├── codec.scala │ │ │ ├── instances.scala │ │ │ └── syntax.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── integ │ │ └── cats │ │ ├── EndpointIOInstancesSpec.scala │ │ ├── ExampleFunctorLawSpec.scala │ │ ├── ModifyFunctorInstancesTest.scala │ │ ├── ModifyFunctorInstancesTestData.scala │ │ ├── TapirCodecCatsTest.scala │ │ └── ValidatorCatsTest.scala ├── derevo │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── derevo │ │ │ └── schema.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── derevo │ │ └── DerevoSchemaDerivationSpec.scala ├── enumeratum │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── codec │ │ │ └── enumeratum │ │ │ ├── TapirCodecEnumeratum.scala │ │ │ └── enumeratum.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── codec │ │ └── enumeratum │ │ └── TapirCodecEnumeratumTest.scala ├── iron │ ├── examples │ │ └── src │ │ │ └── main │ │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── examples │ │ │ └── errors │ │ │ └── IronRefinementErrorsNettyServer.scala │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── iron │ │ │ └── codec │ │ │ └── iron │ │ │ ├── IntersectionTypeMirror.scala │ │ │ ├── TapirCodecIron.scala │ │ │ ├── UnionTypeMirror.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala-3 │ │ ├── com │ │ └── example │ │ │ └── RefinedString.scala │ │ └── sttp │ │ └── iron │ │ └── codec │ │ └── iron │ │ ├── RefinedInt.scala │ │ └── TapirCodecIronTestScala3.scala ├── monix-newtype │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── codec │ │ │ └── monix │ │ │ └── newtype │ │ │ ├── TapirCodecMonixNewType.scala │ │ │ └── newtype.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── codec │ │ └── monix │ │ └── newtype │ │ └── TapirCodecMonixNewTypeTest.scala ├── newtype │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── codec │ │ │ └── newtype │ │ │ ├── TapirCodecNewType.scala │ │ │ └── newtype.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── codec │ │ └── newtype │ │ └── TapirCodecNewTypeTest.scala ├── refined │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── codec │ │ │ └── refined │ │ │ ├── TapirCodecRefined.scala │ │ │ └── package.scala │ │ └── test │ │ ├── scala-2 │ │ └── sttp │ │ │ └── tapir │ │ │ └── codec │ │ │ └── refined │ │ │ └── TapirCodecRefinedTestScala2.scala │ │ ├── scala-3 │ │ └── sttp │ │ │ └── tapir │ │ │ └── codec │ │ │ └── refined │ │ │ └── TapirCodecRefinedTestScala3.scala │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── codec │ │ └── refined │ │ └── TapirCodecRefinedTest.scala ├── zio-prelude │ └── src │ │ ├── main │ │ ├── scala-2 │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── codec │ │ │ │ └── zio │ │ │ │ └── prelude │ │ │ │ └── newtype │ │ │ │ ├── TapirNewtype.scala │ │ │ │ └── TapirNewtypeSupport.scala │ │ └── scala-3 │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── codec │ │ │ └── zio │ │ │ └── prelude │ │ │ └── newtype │ │ │ ├── TapirNewtype.scala │ │ │ └── TapirNewtypeSupport.scala │ │ └── test │ │ ├── scala-2 │ │ └── sttp │ │ │ └── tapir │ │ │ └── codec │ │ │ └── zio │ │ │ └── prelude │ │ │ └── newtype │ │ │ ├── TapirNewtypeSupportTestFixture.scala │ │ │ └── TapirNewtypeTestFixture.scala │ │ ├── scala-3 │ │ └── sttp │ │ │ └── tapir │ │ │ └── codec │ │ │ └── zio │ │ │ └── prelude │ │ │ └── newtype │ │ │ ├── PalindromeValidator.scala │ │ │ ├── TapirNewtypeCustomSupportTest.scala │ │ │ ├── TapirNewtypeCustomTest.scala │ │ │ ├── TapirNewtypeSupportTestFixture.scala │ │ │ └── TapirNewtypeTestFixture.scala │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── codec │ │ └── zio │ │ └── prelude │ │ └── newtype │ │ ├── TapirNewtypeSupportTest.scala │ │ └── TapirNewtypeTest.scala └── zio │ └── src │ ├── main │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── ztapir │ │ ├── RIOMonadError.scala │ │ ├── ZPartialServerEndpoint.scala │ │ ├── ZTapir.scala │ │ ├── ZioServerSentEvents.scala │ │ └── package.scala │ └── test │ └── scala │ └── sttp │ └── tapir │ └── ztapir │ ├── TestError.scala │ ├── ZTapirTest.scala │ ├── ZioServerSentEventsTest.scala │ └── instances │ └── TestMonadError.scala ├── json ├── circe │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── json │ │ │ └── circe │ │ │ ├── TapirJsonCirce.scala │ │ │ └── package.scala │ │ └── test │ │ └── scalajvm │ │ └── sttp │ │ └── tapir │ │ └── json │ │ └── circe │ │ └── TapirJsonCirceTests.scala ├── json4s │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── json │ │ │ └── json4s │ │ │ ├── TapirJson4s.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala-2 │ │ └── sttp │ │ └── tapir │ │ └── json │ │ └── json4s │ │ └── TapirJson4sTests.scala ├── jsoniter │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── json │ │ │ └── jsoniter │ │ │ ├── TapirJsonJsoniter.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── json │ │ └── jsoniter │ │ └── TapirJsonJsoniterTests.scala ├── pickler │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── json │ │ │ └── pickler │ │ │ ├── CreateDerivedEnumerationPickler.scala │ │ │ ├── Pickler.scala │ │ │ ├── PicklerConfiguration.scala │ │ │ ├── Readers.scala │ │ │ ├── SchemaDerivation.scala │ │ │ ├── SubtypeDiscriminator.scala │ │ │ ├── TapirPickle.scala │ │ │ ├── UpickleHelpers.scala │ │ │ ├── Writers.scala │ │ │ ├── generic.scala │ │ │ ├── macros.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── json │ │ └── pickler │ │ ├── Fixtures.scala │ │ ├── PicklerBasicTest.scala │ │ ├── PicklerCoproductTest.scala │ │ ├── PicklerCustomizationTest.scala │ │ ├── PicklerEnumTest.scala │ │ └── SchemaDerivationTest.scala ├── playjson │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── json │ │ │ └── play │ │ │ ├── TapirJsonPlay.scala │ │ │ └── package.scala │ │ └── test │ │ ├── scala │ │ └── sttp │ │ │ └── tapir │ │ │ └── json │ │ │ └── play │ │ │ └── TapirJsonPlayTests.scala │ │ ├── scalajs │ │ └── sttp │ │ │ └── tapir │ │ │ └── json │ │ │ └── play │ │ │ └── TapirJsonPlayTestExtensions.scala │ │ └── scalajvm │ │ └── sttp │ │ └── tapir │ │ └── json │ │ └── play │ │ └── TapirJsonPlayTestExtensions.scala ├── sprayjson │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── json │ │ │ └── spray │ │ │ ├── TapirJsonSpray.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── json │ │ └── spray │ │ └── TapirJsonSprayTests.scala ├── tethys │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── json │ │ │ └── tethys │ │ │ ├── TapirJsonTethys.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── json │ │ └── tethys │ │ └── TapirJsonTethysTests.scala ├── upickle │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── json │ │ │ └── upickle │ │ │ ├── TapirJsonuPickle.scala │ │ │ └── package.scala │ │ └── test │ │ ├── scala │ │ └── sttp │ │ │ └── tapir │ │ │ └── json │ │ │ └── upickle │ │ │ └── TapirJsonuPickleTests.scala │ │ ├── scalajs │ │ └── sttp │ │ │ └── tapir │ │ │ └── json │ │ │ └── upickle │ │ │ └── TapirJsonuPickleTestExtensions.scala │ │ ├── scalajvm │ │ └── sttp │ │ │ └── tapir │ │ │ └── json │ │ │ └── upickle │ │ │ └── TapirJsonuPickleTestExtensions.scala │ │ └── scalanative │ │ └── sttp │ │ └── tapir │ │ └── json │ │ └── upickle │ │ └── TapirJsonuPickleTestExtensions.scala └── zio │ └── src │ ├── main │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── json │ │ └── zio │ │ ├── TapirJsonZio.scala │ │ └── package.scala │ └── test │ └── scala │ └── sttp │ └── tapir │ └── json │ └── zio │ └── TapirJsonZioTest.scala ├── metrics ├── datadog-metrics │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── metrics │ │ │ └── datadog │ │ │ └── DatadogMetrics.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── metrics │ │ └── datadog │ │ └── DatadogMetricsTest.scala ├── opentelemetry-metrics │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── metrics │ │ │ └── opentelemetry │ │ │ └── OpenTelemetryMetrics.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── metrics │ │ └── opentelemetry │ │ └── OpenTelemetryMetricsTest.scala ├── prometheus-metrics │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── metrics │ │ │ └── prometheus │ │ │ └── PrometheusMetrics.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── metrics │ │ └── prometheus │ │ └── PrometheusMetricsTest.scala └── zio-metrics │ └── src │ ├── main │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── metrics │ │ └── zio │ │ └── ZioMetrics.scala │ └── test │ └── scala │ └── sttp │ └── tapir │ └── server │ └── metrics │ └── zio │ └── ZioMetricsTest.scala ├── openapi-codegen ├── cli │ ├── src │ │ └── main │ │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── codegen │ │ │ ├── GenScala.scala │ │ │ └── Main.scala │ └── tapir-codegen.json ├── core │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── codegen │ │ │ ├── ClassDefinitionGenerator.scala │ │ │ ├── EndpointGenerator.scala │ │ │ ├── EnumGenerator.scala │ │ │ ├── JsonSerdeGenerator.scala │ │ │ ├── RootGenerator.scala │ │ │ ├── SchemaGenerator.scala │ │ │ ├── SecurityGenerator.scala │ │ │ ├── ServersGenerator.scala │ │ │ ├── ValidationGenerator.scala │ │ │ ├── XmlSerdeGenerator.scala │ │ │ ├── YamlParser.scala │ │ │ ├── openapi │ │ │ └── models │ │ │ │ ├── DefaultValueRenderer.scala │ │ │ │ ├── GenerationDirectives.scala │ │ │ │ ├── OpenapiComponent.scala │ │ │ │ ├── OpenapiModels.scala │ │ │ │ ├── OpenapiReqResp.scala │ │ │ │ ├── OpenapiSchemaType.scala │ │ │ │ ├── OpenapiSecuritySchemeType.scala │ │ │ │ ├── OpenapiServer.scala │ │ │ │ ├── OpenapiXml.scala │ │ │ │ └── SpecificationExtensionRenderer.scala │ │ │ └── util │ │ │ ├── DocUtils.scala │ │ │ ├── ErrUtils.scala │ │ │ ├── MapUtils.scala │ │ │ └── StringUtils.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── codegen │ │ ├── ClassDefinitionGeneratorSpec.scala │ │ ├── EndpointGeneratorSpec.scala │ │ ├── RootGeneratorSpec.scala │ │ ├── TestHelpers.scala │ │ ├── models │ │ ├── ModelParserSpec.scala │ │ └── SchemaParserSpec.scala │ │ └── testutils │ │ ├── CompileCheckTestBase.scala │ │ └── CompileCheckTestBaseSpec.scala └── sbt-plugin │ ├── README.md │ └── src │ ├── main │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── sbt │ │ ├── OpenapiCodegenKeys.scala │ │ ├── OpenapiCodegenPlugin.scala │ │ └── OpenapiCodegenTask.scala │ └── sbt-test │ └── sbt-openapi-codegen │ ├── caching │ ├── build.sbt │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ ├── src │ │ └── main │ │ │ └── scala │ │ │ └── Main.scala │ ├── swagger.yaml │ └── test │ ├── minimal │ ├── build.sbt │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ ├── src │ │ └── main │ │ │ └── scala │ │ │ └── Main.scala │ ├── swagger.yaml │ └── test │ ├── multi_file │ ├── Expected.scala.txt │ ├── Expected2.scala.txt │ ├── build.sbt │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ ├── src │ │ └── main │ │ │ └── scala │ │ │ └── Main.scala │ ├── swagger.yaml │ ├── swagger2.yaml │ └── test │ ├── oneOf-json-roundtrip-zio │ ├── Expected.scala.txt │ ├── ExpectedJsonSerdes.scala.txt │ ├── ExpectedSchemas.scala.txt │ ├── build.sbt │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ ├── src │ │ ├── main │ │ │ └── scala │ │ │ │ └── Main.scala │ │ └── test │ │ │ └── scala │ │ │ └── JsonRoundtrip.scala │ ├── swagger.yaml │ └── test │ ├── oneOf-json-roundtrip │ ├── Expected.scala.txt │ ├── ExpectedJsonSerdes.scala.txt │ ├── ExpectedSchemas.scala.txt │ ├── ExpectedValidators.scala.txt │ ├── build.sbt │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ ├── src │ │ ├── main │ │ │ └── scala │ │ │ │ └── Main.scala │ │ └── test │ │ │ └── scala │ │ │ ├── BinaryEndpoints.scala │ │ │ ├── JsonRoundtrip.scala │ │ │ ├── ServerSpec.scala │ │ │ └── ValidationSpec.scala │ ├── swagger.yaml │ └── test │ ├── oneOf-json-roundtrip_jsoniter │ ├── Expected.scala.txt │ ├── ExpectedJsonSerdes.scala.txt │ ├── ExpectedXmlSerdes.scala.txt │ ├── build.sbt │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ ├── src │ │ ├── main │ │ │ └── scala │ │ │ │ └── Main.scala │ │ └── test │ │ │ └── scala │ │ │ ├── BinaryEndpoints.scala │ │ │ ├── JsonRoundtrip.scala │ │ │ └── XmlRoundtrip.scala │ ├── swagger.yaml │ └── test │ ├── oneOf-json-roundtrip_jsoniter_scala3 │ ├── Expected.scala.txt │ ├── ExpectedJsonSerdes.scala.txt │ ├── build.sbt │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ ├── src │ │ ├── main │ │ │ └── scala │ │ │ │ └── Main.scala │ │ └── test │ │ │ └── scala │ │ │ └── JsonRoundtrip.scala │ ├── swagger.yaml │ └── test │ ├── oneOf-json-roundtrip_scala3 │ ├── Expected.scala.txt │ ├── build.sbt │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ ├── src │ │ ├── main │ │ │ └── scala │ │ │ │ └── Main.scala │ │ └── test │ │ │ └── scala │ │ │ ├── JsonRoundtrip.scala │ │ │ └── ServerSpec.scala │ ├── swagger.yaml │ └── test │ ├── option-overrides │ ├── build.sbt │ ├── example_swagger.yaml │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ ├── src │ │ └── main │ │ │ └── scala │ │ │ └── Main.scala │ └── test │ └── petstore │ ├── Expected.scala.txt │ ├── ExpectedJsonSerdes.scala.txt │ ├── ExpectedSchemas.scala.txt │ ├── ExpectedXmlSerdes.scala.txt │ ├── build.sbt │ ├── project │ ├── build.properties │ └── plugins.sbt │ ├── src │ ├── main │ │ └── scala │ │ │ └── Main.scala │ └── test │ │ └── scala │ │ └── XmlRoundtrip.scala │ ├── swagger.yaml │ └── test ├── perf-tests ├── README.md ├── results │ ├── 100users-1min-18-03-2022-local-adamw.md │ ├── 100users-5min-09-03-2022-ec2.md │ └── 100users-5min-09-03-2022-local.md └── src │ ├── main │ ├── resources │ │ ├── 64KB.json │ │ └── logback.xml │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── perf │ │ ├── Common.scala │ │ ├── apis │ │ ├── Endpoints.scala │ │ ├── ServerName.scala │ │ ├── ServerRunner.scala │ │ └── TypeScanner.scala │ │ ├── http4s │ │ └── Http4s.scala │ │ ├── netty │ │ ├── cats │ │ │ └── NettyCats.scala │ │ └── future │ │ │ └── NettyFuture.scala │ │ ├── nima │ │ └── Nima.scala │ │ ├── pekko │ │ └── PekkoHttp.scala │ │ ├── play │ │ └── Play.scala │ │ └── vertx │ │ ├── Vertx.scala │ │ └── cats │ │ └── VertxCats.scala │ └── test │ └── scala │ └── sttp │ └── tapir │ └── perf │ ├── HistogramPrinter.scala │ └── Simulations.scala ├── project ├── FileUtils.scala ├── GenerateListOfExamples.scala ├── GenerateMimeByExtensionDB.scala ├── PollingUtils.scala ├── VerifyExamplesCompileUsingScalaCli.scala ├── Versions.scala ├── build.properties ├── build.sbt └── plugins.sbt ├── server ├── akka-grpc-server │ └── src │ │ └── main │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── akkagrpc │ │ ├── AkkaGrpcRequestBody.scala │ │ ├── AkkaGrpcServerInterpreter.scala │ │ └── AkkaGrpcToResponseBody.scala ├── akka-http-server │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── akkahttp │ │ │ ├── AkkaBodyListener.scala │ │ │ ├── AkkaHttpServerInterpreter.scala │ │ │ ├── AkkaHttpServerOptions.scala │ │ │ ├── AkkaModel.scala │ │ │ ├── AkkaRequestBody.scala │ │ │ ├── AkkaServerRequest.scala │ │ │ ├── AkkaServerSentEvents.scala │ │ │ ├── AkkaStreamSizeExceptionInterceptor.scala │ │ │ ├── AkkaToResponseBody.scala │ │ │ ├── AkkaWebSockets.scala │ │ │ ├── ContentTypeCache.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── akkahttp │ │ ├── AkkaHttpServerStubTest.scala │ │ ├── AkkaHttpServerTest.scala │ │ ├── AkkaHttpTestServerInterpreter.scala │ │ └── AkkaServerSentEventsTest.scala ├── armeria-server │ ├── cats │ │ └── src │ │ │ ├── main │ │ │ └── scala │ │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── server │ │ │ │ └── armeria │ │ │ │ └── cats │ │ │ │ ├── ArmeriaCatsServerInterpreter.scala │ │ │ │ ├── ArmeriaCatsServerOptions.scala │ │ │ │ ├── CatsMonadAsyncError.scala │ │ │ │ └── TapirCatsService.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── armeria │ │ │ └── cats │ │ │ ├── ArmeriaCatsServerTest.scala │ │ │ └── ArmeriaCatsTestServerInterpreter.scala │ ├── src │ │ ├── main │ │ │ └── scala │ │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── server │ │ │ │ └── armeria │ │ │ │ ├── ArmeriaBodyListener.scala │ │ │ │ ├── ArmeriaFutureServerInterpreter.scala │ │ │ │ ├── ArmeriaFutureServerOptions.scala │ │ │ │ ├── ArmeriaRequestBody.scala │ │ │ │ ├── ArmeriaServerOptions.scala │ │ │ │ ├── ArmeriaServerRequest.scala │ │ │ │ ├── ArmeriaToResponseBody.scala │ │ │ │ ├── FutureConversion.scala │ │ │ │ ├── HeaderMapping.scala │ │ │ │ ├── MethodMapping.scala │ │ │ │ ├── ResultMapping.scala │ │ │ │ ├── RouteMapping.scala │ │ │ │ ├── StreamCompatible.scala │ │ │ │ ├── TapirFutureService.scala │ │ │ │ ├── TapirService.scala │ │ │ │ └── package.scala │ │ └── test │ │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── armeria │ │ │ ├── ArmeriaFutureServerTest.scala │ │ │ ├── ArmeriaTestFutureServerInterpreter.scala │ │ │ ├── ArmeriaTestServerInterpreter.scala │ │ │ └── RouteMappingTest.scala │ └── zio │ │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── armeria │ │ │ └── zio │ │ │ ├── ArmeriaZioServerInterpreter.scala │ │ │ ├── ArmeriaZioServerOptions.scala │ │ │ ├── RIOMonadAsyncError.scala │ │ │ └── TapirZioService.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── armeria │ │ └── zio │ │ ├── ArmeriaZioServerTest.scala │ │ └── ArmeriaZioTestServerInterpreter.scala ├── core │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ ├── interceptor │ │ │ ├── CustomiseInterceptors.scala │ │ │ ├── DecodeFailureContext.scala │ │ │ ├── DecodeSuccessContext.scala │ │ │ ├── EndpointHandler.scala │ │ │ ├── Interceptor.scala │ │ │ ├── RequestHandler.scala │ │ │ ├── RequestResult.scala │ │ │ ├── SecurityFailureContext.scala │ │ │ ├── content │ │ │ │ └── NotAcceptableInterceptor.scala │ │ │ ├── cors │ │ │ │ ├── CORSConfig.scala │ │ │ │ └── CORSInterceptor.scala │ │ │ ├── decodefailure │ │ │ │ ├── DecodeFailureHandler.scala │ │ │ │ └── DecodeFailureInterceptor.scala │ │ │ ├── exception │ │ │ │ ├── ExceptionContext.scala │ │ │ │ ├── ExceptionHandler.scala │ │ │ │ └── ExceptionInterceptor.scala │ │ │ ├── log │ │ │ │ ├── ExceptionContext.scala │ │ │ │ ├── ServerLog.scala │ │ │ │ └── ServerLogInterceptor.scala │ │ │ ├── metrics │ │ │ │ └── MetricsEndpointInterceptor.scala │ │ │ └── reject │ │ │ │ ├── RejectContext.scala │ │ │ │ ├── RejectHandler.scala │ │ │ │ └── RejectInterceptor.scala │ │ │ ├── interpreter │ │ │ ├── BodyListener.scala │ │ │ ├── DecodeBasicInputs.scala │ │ │ ├── EncodeOutputs.scala │ │ │ ├── FilterServerEndpoints.scala │ │ │ ├── InputValue.scala │ │ │ ├── RequestBody.scala │ │ │ ├── ServerInterpreter.scala │ │ │ └── ToResponseBody.scala │ │ │ ├── metrics │ │ │ └── Metric.scala │ │ │ └── model │ │ │ ├── EndpointExtensions.scala │ │ │ ├── ServerResponse.scala │ │ │ └── ValuedEndpointOutput.scala │ │ └── test │ │ ├── scala │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ ├── TestUtil.scala │ │ │ ├── interceptor │ │ │ ├── cors │ │ │ │ ├── CORSConfigTest.scala │ │ │ │ └── CORSInterceptorTest.scala │ │ │ └── decodefailure │ │ │ │ └── DefaultDecodeFailureHandlerTest.scala │ │ │ └── interpreter │ │ │ ├── DecodeBasicInputsTest.scala │ │ │ ├── FilterServerEndpointsTest.scala │ │ │ └── ServerInterpreterTest.scala │ │ └── scalajvm │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── interpreter │ │ └── DecodeBasicInputsResultTest.scala ├── finatra-server │ ├── cats │ │ └── src │ │ │ ├── main │ │ │ └── scala │ │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── server │ │ │ │ └── finatra │ │ │ │ └── cats │ │ │ │ ├── FinatraCatsServerInterpreter.scala │ │ │ │ ├── FinatraCatsServerOptions.scala │ │ │ │ └── conversions.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── finatra │ │ │ └── cats │ │ │ ├── FinatraCatsTestServerInterpreter.scala │ │ │ └── FinatraServerCatsTests.scala │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── finatra │ │ │ ├── FileChunk.scala │ │ │ ├── FinatraBodyListener.scala │ │ │ ├── FinatraContent.scala │ │ │ ├── FinatraRequestBody.scala │ │ │ ├── FinatraRoute.scala │ │ │ ├── FinatraServerInterpreter.scala │ │ │ ├── FinatraServerOptions.scala │ │ │ ├── FinatraServerRequest.scala │ │ │ ├── FinatraToResponseBody.scala │ │ │ └── TapirController.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── finatra │ │ ├── FinatraServerStubTest.scala │ │ ├── FinatraServerTest.scala │ │ └── FinatraTestServerInterpreter.scala ├── http4s-server │ ├── src │ │ ├── main │ │ │ ├── scala │ │ │ │ └── sttp │ │ │ │ │ └── tapir │ │ │ │ │ └── server │ │ │ │ │ └── http4s │ │ │ │ │ ├── Http4sBodyListener.scala │ │ │ │ │ ├── Http4sRequestBody.scala │ │ │ │ │ ├── Http4sServerInterpreter.scala │ │ │ │ │ ├── Http4sServerOptions.scala │ │ │ │ │ ├── Http4sServerRequest.scala │ │ │ │ │ ├── Http4sServerSentEvents.scala │ │ │ │ │ ├── Http4sToResponseBody.scala │ │ │ │ │ ├── Http4sWebSockets.scala │ │ │ │ │ └── package.scala │ │ │ ├── scalajvm │ │ │ │ └── sttp │ │ │ │ │ └── tapir │ │ │ │ │ └── server │ │ │ │ │ └── http4s │ │ │ │ │ └── Http4sDefaultServerLog.scala │ │ │ └── scalanative │ │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── server │ │ │ │ └── http4s │ │ │ │ └── Http4sDefaultServerLog.scala │ │ └── test │ │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── http4s │ │ │ ├── Http4sServerSentEventsTest.scala │ │ │ ├── Http4sServerStubTest.scala │ │ │ ├── Http4sServerTest.scala │ │ │ └── Http4sTestServerInterpreter.scala │ └── zio │ │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── http4s │ │ │ └── ztapir │ │ │ ├── ConvertStreams.scala │ │ │ ├── ZHttp4sServerInterpreter.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── http4s │ │ └── ztapir │ │ ├── ZEndpointTest.scala │ │ ├── ZHttp4sServerStubTest.scala │ │ ├── ZHttp4sServerTest.scala │ │ └── ZHttp4sTestServerInterpreter.scala ├── jdkhttp-server │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── jdkhttp │ │ │ ├── JdkHttpServer.scala │ │ │ ├── JdkHttpServerInterpreter.scala │ │ │ ├── JdkHttpServerOptions.scala │ │ │ ├── internal │ │ │ ├── JdkHttpBodyListener.scala │ │ │ ├── JdkHttpRequestBody.scala │ │ │ ├── JdkHttpServerRequest.scala │ │ │ ├── JdkHttpToResponseBody.scala │ │ │ ├── KMPMatcher.scala │ │ │ ├── LimitedInputStream.scala │ │ │ ├── ParsedMultiPart.scala │ │ │ └── package.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── jdkhttp │ │ ├── JdkHttpServerTest.scala │ │ ├── JdkHttpTestServerInterpreter.scala │ │ └── internal │ │ └── KMPMatcherTest.scala ├── netty-server │ ├── cats │ │ └── src │ │ │ ├── main │ │ │ ├── scala-2.12 │ │ │ │ └── sttp │ │ │ │ │ └── tapir │ │ │ │ │ └── server │ │ │ │ │ └── netty │ │ │ │ │ └── cats │ │ │ │ │ └── internal │ │ │ │ │ └── ExecutionContexts.scala │ │ │ ├── scala-3-2.13+ │ │ │ │ └── sttp │ │ │ │ │ └── tapir │ │ │ │ │ └── server │ │ │ │ │ └── netty │ │ │ │ │ └── cats │ │ │ │ │ └── internal │ │ │ │ │ └── ExecutionContexts.scala │ │ │ └── scala │ │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── server │ │ │ │ └── netty │ │ │ │ └── cats │ │ │ │ ├── NettyCatsServer.scala │ │ │ │ ├── NettyCatsServerInterpreter.scala │ │ │ │ ├── NettyCatsServerOptions.scala │ │ │ │ └── internal │ │ │ │ ├── CatsUtil.scala │ │ │ │ ├── Fs2StreamCompatible.scala │ │ │ │ ├── NettyCatsRequestBody.scala │ │ │ │ └── WebSocketPipeProcessor.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── netty │ │ │ └── cats │ │ │ ├── NettyCatsServerStubTest.scala │ │ │ ├── NettyCatsServerTest.scala │ │ │ ├── NettyCatsTestServerInterpreter.scala │ │ │ └── NettyFs2StreamingCancellationTest.scala │ ├── src │ │ ├── main │ │ │ └── scala │ │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── server │ │ │ │ └── netty │ │ │ │ ├── NettyConfig.scala │ │ │ │ ├── NettyFutureServer.scala │ │ │ │ ├── NettyFutureServerInterpreter.scala │ │ │ │ ├── NettyFutureServerOptions.scala │ │ │ │ ├── NettyResponseContent.scala │ │ │ │ ├── NettyServerRequest.scala │ │ │ │ ├── NettySocketConfig.scala │ │ │ │ ├── internal │ │ │ │ ├── FutureUtil.scala │ │ │ │ ├── NettyBodyListener.scala │ │ │ │ ├── NettyBootstrap.scala │ │ │ │ ├── NettyConnectionCounter.scala │ │ │ │ ├── NettyDefaults.scala │ │ │ │ ├── NettyFutureRequestBody.scala │ │ │ │ ├── NettyRequestBody.scala │ │ │ │ ├── NettyServerHandler.scala │ │ │ │ ├── NettyServerInterpreter.scala │ │ │ │ ├── NettyStreamingRequestBody.scala │ │ │ │ ├── NettyToResponseBody.scala │ │ │ │ ├── NettyToStreamsResponseBody.scala │ │ │ │ ├── RunAsync.scala │ │ │ │ ├── StreamCompatible.scala │ │ │ │ ├── UnhandledExceptionHandler.scala │ │ │ │ ├── package.scala │ │ │ │ ├── reactivestreams │ │ │ │ │ ├── CancellingSubscriber.scala │ │ │ │ │ ├── FileRangePublisher.scala │ │ │ │ │ ├── FileWriterSubscriber.scala │ │ │ │ │ ├── InputStreamPublisher.scala │ │ │ │ │ ├── LimitedLengthSubscriber.scala │ │ │ │ │ ├── PromisingSubscriber.scala │ │ │ │ │ ├── SimpleSubscriber.scala │ │ │ │ │ ├── SubscribeTrackingStreamedHttpRequest.scala │ │ │ │ │ └── SubscriberInputStream.scala │ │ │ │ └── ws │ │ │ │ │ ├── WebSocketAutoPingHandler.scala │ │ │ │ │ ├── WebSocketFrameConverters.scala │ │ │ │ │ └── WebSocketPingPongFrameHandler.scala │ │ │ │ └── package.scala │ │ └── test │ │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── netty │ │ │ ├── NettyFutureRequestTimeoutTests.scala │ │ │ ├── NettyFutureServerStubTest.scala │ │ │ ├── NettyFutureServerTest.scala │ │ │ ├── NettyFutureTestServerInterpreter.scala │ │ │ ├── NettyServerRequestSpec.scala │ │ │ └── internal │ │ │ └── reactivestreams │ │ │ └── SubscriberInputStreamTest.scala │ ├── sync │ │ └── src │ │ │ ├── main │ │ │ └── scala │ │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── server │ │ │ │ └── netty │ │ │ │ └── sync │ │ │ │ ├── NettySyncServer.scala │ │ │ │ ├── NettySyncServerInterpreter.scala │ │ │ │ ├── NettySyncServerOptions.scala │ │ │ │ ├── OxServerSentEvents.scala │ │ │ │ ├── OxStreams.scala │ │ │ │ ├── internal │ │ │ │ ├── NettySyncRequestBody.scala │ │ │ │ ├── NettySyncToResponseBody.scala │ │ │ │ ├── reactivestreams │ │ │ │ │ ├── ChannelSubscription.scala │ │ │ │ │ └── OxProcessor.scala │ │ │ │ └── ws │ │ │ │ │ └── OxSourceWebSocketProcessor.scala │ │ │ │ └── sync.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── netty │ │ │ └── sync │ │ │ ├── NettySyncRequestTimeoutTests.scala │ │ │ ├── NettySyncServerTest.scala │ │ │ ├── NettySyncTestServerInterpreter.scala │ │ │ └── perf │ │ │ └── NettySyncServerRunner.scala │ └── zio │ │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── netty │ │ │ └── zio │ │ │ ├── NettyZioServer.scala │ │ │ ├── NettyZioServerInterpreter.scala │ │ │ ├── NettyZioServerOptions.scala │ │ │ └── internal │ │ │ ├── NettyZioRequestBody.scala │ │ │ ├── ZioStreamCompatible.scala │ │ │ └── ZioUtil.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── netty │ │ └── zio │ │ ├── NettyZioServerStubTest.scala │ │ ├── NettyZioServerTest.scala │ │ └── NettyZioTestServerInterpreter.scala ├── nima-server │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── nima │ │ │ ├── NimaServerInterpreter.scala │ │ │ ├── NimaServerOptions.scala │ │ │ └── internal │ │ │ ├── NimaBodyListener.scala │ │ │ ├── NimaRequestBody.scala │ │ │ ├── NimaServerRequest.scala │ │ │ ├── NimaToResponseBody.scala │ │ │ └── package.scala │ │ └── test │ │ ├── resources │ │ └── logback.xml │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── nima │ │ ├── NimaServerTest.scala │ │ └── NimaTestServerInterpreter.scala ├── pekko-grpc-server │ └── src │ │ └── main │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── pekkogrpc │ │ ├── PekkoGrpcRequestBody.scala │ │ ├── PekkoGrpcServerInterpreter.scala │ │ └── PekkoGrpcToResponseBody.scala ├── pekko-http-server │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── pekkohttp │ │ │ ├── ContentTypeCache.scala │ │ │ ├── PekkoBodyListener.scala │ │ │ ├── PekkoHttpServerInterpreter.scala │ │ │ ├── PekkoHttpServerOptions.scala │ │ │ ├── PekkoModel.scala │ │ │ ├── PekkoRequestBody.scala │ │ │ ├── PekkoServerRequest.scala │ │ │ ├── PekkoServerSentEvents.scala │ │ │ ├── PekkoStreamSizeExceptionInterceptor.scala │ │ │ ├── PekkoToResponseBody.scala │ │ │ ├── PekkoWebSockets.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── pekkohttp │ │ ├── PekkoHttpServerStubTest.scala │ │ ├── PekkoHttpServerTest.scala │ │ ├── PekkoHttpTestServerInterpreter.scala │ │ └── PekkoServerSentEventsTest.scala ├── play-server │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── play │ │ │ ├── PlayBodyListener.scala │ │ │ ├── PlayRequestBody.scala │ │ │ ├── PlayServerInterpreter.scala │ │ │ ├── PlayServerOptions.scala │ │ │ ├── PlayServerRequest.scala │ │ │ ├── PlayToResponseBody.scala │ │ │ ├── PlayWebSockets.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── play │ │ ├── PlayServerStubTest.scala │ │ ├── PlayServerTest.scala │ │ ├── PlayServerWithContextTest.scala │ │ └── PlayTestServerInterpreter.scala ├── play29-server │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── play │ │ │ ├── PlayBodyListener.scala │ │ │ ├── PlayRequestBody.scala │ │ │ ├── PlayServerInterpreter.scala │ │ │ ├── PlayServerOptions.scala │ │ │ ├── PlayServerRequest.scala │ │ │ ├── PlayToResponseBody.scala │ │ │ ├── PlayWebSockets.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── play │ │ ├── PlayServerStubTest.scala │ │ ├── PlayServerTest.scala │ │ ├── PlayServerWithContextTest.scala │ │ └── PlayTestServerInterpreter.scala ├── sttp-mock-server │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── mockserver │ │ │ ├── MockServerException.scala │ │ │ ├── SttpMockServerClient.scala │ │ │ ├── impl │ │ │ └── JsonCodecs.scala │ │ │ └── model.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── mockserver │ │ ├── SttpMockServerClientSpec.scala │ │ └── fixture │ │ └── fixture.scala ├── sttp-stub-server │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── stub │ │ │ ├── SttpRequest.scala │ │ │ ├── SttpRequestBody.scala │ │ │ ├── SttpRequestDecoder.scala │ │ │ ├── SttpResponseEncoder.scala │ │ │ ├── SttpStubServer.scala │ │ │ ├── StubServerInterpreter.scala │ │ │ ├── TapirStubInterpreter.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── stub │ │ ├── SttpClientTestUsingStub.scala │ │ ├── SttpStubServerTest.scala │ │ └── TapirStubInterpreterTest.scala ├── sttp-stub4-server │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── stub4 │ │ │ ├── SttpRequest.scala │ │ │ ├── SttpRequestBody.scala │ │ │ ├── SttpRequestDecoder.scala │ │ │ ├── SttpResponseEncoder.scala │ │ │ ├── SttpStubServer.scala │ │ │ ├── StubServerInterpreter.scala │ │ │ ├── TapirStubInterpreter.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── stub4 │ │ ├── SttpClientTestUsingStub.scala │ │ ├── SttpStubServerTest.scala │ │ └── TapirStubInterpreterTest.scala ├── tests │ └── src │ │ └── main │ │ ├── resources │ │ ├── test │ │ │ ├── d1 │ │ │ │ ├── d2 │ │ │ │ │ └── r4.txt │ │ │ │ ├── index.html │ │ │ │ └── r3.txt │ │ │ ├── r1.txt │ │ │ ├── r2.txt │ │ │ ├── r3.txt │ │ │ └── r3.txt.gz │ │ └── test2 │ │ │ └── r5.txt │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── tests │ │ ├── AllServerTests.scala │ │ ├── CreateServerStubTest.scala │ │ ├── CreateServerTest.scala │ │ ├── ServerBasicTests.scala │ │ ├── ServerCORSTests.scala │ │ ├── ServerCancellationTests.scala │ │ ├── ServerContentNegotiationTests.scala │ │ ├── ServerFileTests.scala │ │ ├── ServerFilesTests.scala │ │ ├── ServerGracefulShutdownTests.scala │ │ ├── ServerMappingTests.scala │ │ ├── ServerMetricsTest.scala │ │ ├── ServerMultipartTests.scala │ │ ├── ServerOneOfBodyTests.scala │ │ ├── ServerOneOfTests.scala │ │ ├── ServerOptionsTests.scala │ │ ├── ServerRejectTests.scala │ │ ├── ServerSecurityTests.scala │ │ ├── ServerStreamingTests.scala │ │ ├── ServerStubStreamingTest.scala │ │ ├── ServerStubTest.scala │ │ ├── ServerValidationTests.scala │ │ ├── ServerWebSocketTests.scala │ │ ├── Sleeper.scala │ │ ├── TestServerInterpreter.scala │ │ └── package.scala ├── vertx-server │ ├── cats │ │ └── src │ │ │ ├── main │ │ │ └── scala │ │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── server │ │ │ │ └── vertx │ │ │ │ └── cats │ │ │ │ ├── VertxCatsServerInterpreter.scala │ │ │ │ ├── VertxCatsServerOptions.scala │ │ │ │ └── streams │ │ │ │ └── fs2.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── vertx │ │ │ └── cats │ │ │ ├── CatsVertxServerTest.scala │ │ │ ├── CatsVertxTestServerInterpreter.scala │ │ │ ├── VertxStubServerTest.scala │ │ │ └── streams │ │ │ └── Fs2StreamTest.scala │ ├── src │ │ ├── main │ │ │ └── scala │ │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── server │ │ │ │ └── vertx │ │ │ │ ├── RichResponse.scala │ │ │ │ ├── VertxBodyListener.scala │ │ │ │ ├── VertxErrorHandler.scala │ │ │ │ ├── VertxFutureServerInterpreter.scala │ │ │ │ ├── VertxFutureServerOptions.scala │ │ │ │ ├── VertxServerOptions.scala │ │ │ │ ├── decoders │ │ │ │ ├── VertxRequestBody.scala │ │ │ │ └── VertxServerRequest.scala │ │ │ │ ├── encoders │ │ │ │ ├── VertxOutputEncoders.scala │ │ │ │ ├── VertxToResponseBody.scala │ │ │ │ └── package.scala │ │ │ │ ├── handlers │ │ │ │ └── package.scala │ │ │ │ ├── interpreters │ │ │ │ ├── CommonServerInterpreter.scala │ │ │ │ ├── FromVFuture.scala │ │ │ │ └── RunAsync.scala │ │ │ │ ├── routing │ │ │ │ ├── MethodMapping.scala │ │ │ │ └── PathMapping.scala │ │ │ │ └── streams │ │ │ │ ├── LimitedReadStream.scala │ │ │ │ ├── Pipe.scala │ │ │ │ ├── ReadStreamCompatible.scala │ │ │ │ ├── domain.scala │ │ │ │ ├── package.scala │ │ │ │ └── websocket.scala │ │ └── test │ │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── vertx │ │ │ ├── VertxBlockingServerTest.scala │ │ │ ├── VertxServerTest.scala │ │ │ ├── VertxStubServerTest.scala │ │ │ ├── VertxTestServerBlockingInterpreter.scala │ │ │ ├── VertxTestServerInterpreter.scala │ │ │ └── streams │ │ │ └── FakeStream.scala │ └── zio │ │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── server │ │ │ └── vertx │ │ │ └── zio │ │ │ ├── VertxZioServerInterpreter.scala │ │ │ ├── VertxZioServerOptions.scala │ │ │ └── streams │ │ │ └── zio.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── vertx │ │ └── zio │ │ ├── VertxStubServerTest.scala │ │ ├── ZioVertxServerTest.scala │ │ ├── ZioVertxTestServerInterpreter.scala │ │ └── streams │ │ └── ZStreamTest.scala └── zio-http-server │ └── src │ ├── main │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── ziohttp │ │ ├── ZioHttpBodyListener.scala │ │ ├── ZioHttpInterpreter.scala │ │ ├── ZioHttpRequestBody.scala │ │ ├── ZioHttpResponseBody.scala │ │ ├── ZioHttpServerOptions.scala │ │ ├── ZioHttpServerRequest.scala │ │ ├── ZioHttpToResponseBody.scala │ │ ├── ZioWebSockets.scala │ │ └── package.scala │ └── test │ └── scala │ ├── sttp │ └── tapir │ │ └── server │ │ └── ziohttp │ │ ├── ZioHttpCompositionTest.scala │ │ ├── ZioHttpServerStubTest.scala │ │ ├── ZioHttpServerTest.scala │ │ └── ZioHttpTestServerInterpreter.scala │ └── zio │ ├── http │ └── netty │ │ └── TestChannelFactories.scala │ └── test │ └── package.scala ├── serverless └── aws │ ├── cdk-tests │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── serverless │ │ │ └── aws │ │ │ └── cdk │ │ │ └── tests │ │ │ ├── AwsCdkAppTemplate.scala │ │ │ ├── CdkTestLambdaHandler.scala │ │ │ └── tests.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── serverless │ │ └── aws │ │ └── cdk │ │ └── tests │ │ └── AwsCdkAppLocalHttpTest.scala │ ├── cdk │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── app-template │ │ │ │ ├── bin │ │ │ │ └── tapir-cdk-stack.ts │ │ │ │ ├── cdk.json │ │ │ │ ├── gitignore │ │ │ │ ├── jest.config.js │ │ │ │ ├── lib │ │ │ │ └── stack-template.ts │ │ │ │ ├── package.json │ │ │ │ ├── readme.md │ │ │ │ └── tsconfig.json │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── serverless │ │ │ └── aws │ │ │ └── cdk │ │ │ ├── AwsCdkInterpreter.scala │ │ │ ├── AwsCdkOptions.scala │ │ │ ├── CdkAppTemplate.scala │ │ │ └── internal │ │ │ ├── AppTemplateFiles.scala │ │ │ ├── Method.scala │ │ │ ├── ParseStackTemplate.scala │ │ │ ├── Request.scala │ │ │ ├── Segment.scala │ │ │ ├── StackFile.scala │ │ │ ├── Tree.scala │ │ │ ├── TreeToTypeScript.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── serverless │ │ └── aws │ │ └── cdk │ │ ├── IOLambdaHandlerV1Test.scala │ │ ├── internal │ │ ├── ParseStackTemplateTest.scala │ │ ├── RequestTest.scala │ │ ├── SegmentTest.scala │ │ ├── TreeTest.scala │ │ └── TreeToTypeScriptTest.scala │ │ └── test │ │ ├── IOLambdaHandlerV1.scala │ │ └── TestEndpoints.scala │ ├── examples │ └── src │ │ └── main │ │ ├── scalajs │ │ └── sttp │ │ │ └── tapir │ │ │ └── serverless │ │ │ └── aws │ │ │ └── examples │ │ │ ├── LambdaApiJsExample.scala │ │ │ └── LambdaApiJsResourceExample.scala │ │ └── scalajvm │ │ └── sttp │ │ └── tapir │ │ └── serverless │ │ └── aws │ │ └── examples │ │ ├── CdkAppExample.scala │ │ ├── LambdaApiExample.scala │ │ ├── LambdaApiV1Example.scala │ │ ├── SamJsTemplateExample.scala │ │ ├── SamTemplateExample.scala │ │ └── TerraformConfigExample.scala │ ├── lambda-cats-effect-tests │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── serverless │ │ │ └── aws │ │ │ └── lambda │ │ │ └── tests │ │ │ ├── IOLambdaHandlerV2.scala │ │ │ ├── LambdaSamTemplate.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── serverless │ │ └── aws │ │ └── lambda │ │ └── tests │ │ ├── AwsLambdaCreateServerStubTest.scala │ │ ├── AwsLambdaSamLocalHttpTest.scala │ │ └── AwsLambdaStubHttpTest.scala │ ├── lambda-cats-effect │ └── src │ │ ├── main │ │ ├── scala │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── serverless │ │ │ │ └── aws │ │ │ │ └── lambda │ │ │ │ ├── AwsCatsEffectServerInterpreter.scala │ │ │ │ ├── AwsCatsEffectServerOptions.scala │ │ │ │ ├── LambdaHandler.scala │ │ │ │ └── runtime │ │ │ │ └── AwsLambdaRuntimeInvocation.scala │ │ ├── scalajs │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── serverless │ │ │ │ └── aws │ │ │ │ └── lambda │ │ │ │ └── js │ │ │ │ └── AwsJsRouteHandler.scala │ │ └── scalajvm │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── serverless │ │ │ └── aws │ │ │ └── lambda │ │ │ └── runtime │ │ │ └── AwsLambdaRuntime.scala │ │ └── test │ │ └── scalajvm │ │ └── sttp │ │ └── tapir │ │ └── serverless │ │ └── aws │ │ └── lambda │ │ └── runtime │ │ └── AwsLambdaRuntimeInvocationTest.scala │ ├── lambda-core │ └── src │ │ ├── main │ │ ├── scala │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── serverless │ │ │ │ └── aws │ │ │ │ └── lambda │ │ │ │ ├── AwsBodyListener.scala │ │ │ │ ├── AwsFutureServerInterpreter.scala │ │ │ │ ├── AwsFutureServerOptions.scala │ │ │ │ ├── AwsRequestBody.scala │ │ │ │ ├── AwsServerInterpreter.scala │ │ │ │ ├── AwsServerOptions.scala │ │ │ │ ├── AwsServerRequest.scala │ │ │ │ ├── model.scala │ │ │ │ └── package.scala │ │ ├── scalajs │ │ │ └── sttp │ │ │ │ └── tapir │ │ │ │ └── serverless │ │ │ │ └── aws │ │ │ │ └── lambda │ │ │ │ ├── AwsToResponseBody.scala │ │ │ │ └── js │ │ │ │ ├── AwsJsRequest.scala │ │ │ │ ├── AwsJsResponse.scala │ │ │ │ └── package.scala │ │ └── scalajvm │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── serverless │ │ │ └── aws │ │ │ └── lambda │ │ │ └── AwsToResponseBody.scala │ │ └── test │ │ └── scalajvm │ │ └── sttp │ │ └── tapir │ │ └── serverless │ │ └── aws │ │ └── lambda │ │ └── MapperTest.scala │ ├── lambda-zio-tests │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── serverless │ │ │ └── aws │ │ │ └── ziolambda │ │ │ └── tests │ │ │ ├── LambdaSamTemplate.scala │ │ │ ├── ZioLambdaHandlerImpl.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── serverless │ │ └── aws │ │ └── ziolambda │ │ └── tests │ │ ├── AwsLambdaCreateServerStubTest.scala │ │ ├── AwsLambdaSamLocalHttpTest.scala │ │ └── AwsLambdaStubHttpTest.scala │ ├── lambda-zio │ └── src │ │ └── main │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── serverless │ │ └── aws │ │ └── ziolambda │ │ ├── AwsZioServerInterpreter.scala │ │ ├── AwsZioServerOptions.scala │ │ └── ZioLambdaHandler.scala │ ├── sam │ └── src │ │ ├── main │ │ └── scala │ │ │ └── sttp │ │ │ └── tapir │ │ │ └── serverless │ │ │ └── aws │ │ │ └── sam │ │ │ ├── AwsSamInterpreter.scala │ │ │ ├── AwsSamOptions.scala │ │ │ ├── AwsSamTemplateEncoders.scala │ │ │ ├── EndpointsToSamTemplate.scala │ │ │ ├── Printer.scala │ │ │ ├── model.scala │ │ │ └── parameter │ │ │ ├── AwsSamOptionsParameterized.scala │ │ │ └── InputParameter.scala │ │ └── test │ │ ├── resources │ │ ├── code_source_template.yaml │ │ ├── code_source_template_with_params_and_role.yaml │ │ ├── http_api_template.yaml │ │ └── image_source_template.yaml │ │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── serverless │ │ └── aws │ │ └── sam │ │ ├── VerifySamTemplateTest.scala │ │ └── parameter │ │ ├── AwsSamOptionsParameterizedTest.scala │ │ └── InputParametersTest.scala │ └── terraform │ └── src │ ├── main │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── serverless │ │ └── aws │ │ └── terraform │ │ ├── AwsTerraformEncoders.scala │ │ ├── AwsTerraformInterpreter.scala │ │ ├── AwsTerraformOptions.scala │ │ ├── EndpointsToTerraformConfig.scala │ │ ├── TerraformResource.scala │ │ └── model.scala │ └── test │ ├── resources │ ├── endpoint_with_params.json │ ├── endpoints_common_paths.json │ ├── root_endpoint.json │ └── simple_endpoint.json │ └── scala │ └── sttp │ └── tapir │ └── serverless │ └── aws │ └── terraform │ └── VerifyTerraformTemplateTest.scala ├── testing └── src │ ├── main │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── testing │ │ ├── EndpointVerificationError.scala │ │ └── EndpointVerifier.scala │ └── test │ └── scala │ └── sttp │ └── tapir │ └── testing │ └── EndpointVerifierTest.scala ├── tests └── src │ └── main │ ├── resources │ └── logback.xml │ ├── scala │ └── sttp │ │ └── tapir │ │ └── tests │ │ ├── Basic.scala │ │ ├── ContentNegotiation.scala │ │ ├── Files.scala │ │ ├── Mapping.scala │ │ ├── Multipart.scala │ │ ├── OneOf.scala │ │ ├── OneOfBody.scala │ │ ├── Security.scala │ │ ├── Streaming.scala │ │ ├── Test.scala │ │ ├── TestUtil.scala │ │ ├── Validation.scala │ │ ├── data │ │ ├── BasketOfFruits.scala │ │ ├── Color.scala │ │ ├── CustomError.scala │ │ ├── Entity.scala │ │ ├── Fruit.scala │ │ ├── FruitAmount.scala │ │ ├── FruitAmountWrapper.scala │ │ ├── FruitData.scala │ │ ├── FruitError.scala │ │ └── FruitErrorDetail.scala │ │ └── package.scala │ ├── scalajs │ └── sttp │ │ └── tapir │ │ └── tests │ │ └── TestUtilExtensions.scala │ ├── scalajvm │ └── sttp │ │ └── tapir │ │ └── tests │ │ ├── TestSuite.scala │ │ └── TestUtilExtensions.scala │ └── scalanative │ └── sttp │ └── tapir │ └── tests │ └── TestUtilExtensions.scala └── tracing ├── opentelemetry-tracing └── src │ ├── main │ └── scala │ │ └── sttp │ │ └── tapir │ │ └── server │ │ └── tracing │ │ └── opentelemetry │ │ ├── OpenTelemetryTracing.scala │ │ └── OpenTelemetryTracingConfig.scala │ └── test │ └── scala │ └── sttp │ └── tapir │ └── server │ └── tracing │ └── opentelemetry │ └── OpenTelemetryTracingTest.scala └── otel4s-tracing └── src ├── main └── scala │ └── sttp │ └── tapir │ └── server │ └── tracing │ └── otel4s │ ├── Otel4sTracing.scala │ └── Otel4sTracingConfig.scala └── test └── scala └── sttp └── tapir └── server └── tracing └── otel4s └── Otel4sTracingTest.scala /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report 4 | title: "[BUG]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Tapir version: *** 11 | 12 | Scala version: *** 13 | 14 | **Describe the bug** 15 | 16 | What is the problem? 17 | 18 | **How to reproduce?** 19 | 20 | Maybe you can provide code to reproduce the problem? 21 | 22 | **Additional information** 23 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | # Set update schedule for GitHub Actions 2 | 3 | version: 2 4 | updates: 5 | 6 | - package-ecosystem: "github-actions" 7 | directory: "/" 8 | schedule: 9 | # Check for updates to GitHub Actions every week 10 | interval: "weekly" 11 | # Raise all pull requests with labels 12 | labels: 13 | - "dependency" 14 | - "automerge" -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | labels: 3 | - label: "automerge" 4 | authors: ["softwaremill-ci"] 5 | files: 6 | - "build.sbt" 7 | - "project/Versions.scala" 8 | - "project/plugins.sbt" 9 | - "examples\\/src\\/main\\/scala\\/.*" 10 | - label: "dependency" 11 | authors: ["softwaremill-ci"] 12 | files: 13 | - "build.sbt" 14 | - "project/Versions.scala" 15 | - "project/plugins.sbt" 16 | - "examples\\/src\\/main\\/scala\\/.*" 17 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | categories: 2 | - title: 'Dependency updates' 3 | labels: 4 | - 'dependency' 5 | template: | 6 | ## What’s Changed 7 | 8 | $CHANGES -------------------------------------------------------------------------------- /.github/workflows/dependency-graph.yml: -------------------------------------------------------------------------------- 1 | # https://scala-lang.org/blog/2022/07/18/secure-your-dependencies-on-github.html 2 | name: Update Dependency Graph 3 | 4 | on: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | update-graph: 11 | name: Update Dependency Graph 12 | runs-on: ubuntu-24.04 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: sbt/setup-sbt@v1 16 | - uses: scalacenter/sbt-dependency-submission@v3 17 | -------------------------------------------------------------------------------- /.github/workflows/rebase-cmd-dispatch.yml: -------------------------------------------------------------------------------- 1 | # On any comment, it will look for '/rebase' in the comment body and in case of hit, it dispatches rebase cmd 2 | # with event type 'rebase-command' which triggers 'rebase-command` WF that performs the rebase operation. 3 | name: Slash Command Dispatch 4 | on: 5 | issue_comment: 6 | types: [created] 7 | jobs: 8 | rebase-cmd-dispatch: 9 | uses: softwaremill/github-actions-workflows/.github/workflows/rebase-cmd-dispatch.yml@main 10 | secrets: 11 | repo-github-token: ${{ secrets.REPO_GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/rebase-cmd.yml: -------------------------------------------------------------------------------- 1 | name: rebase-command 2 | on: 3 | repository_dispatch: 4 | types: [rebase-command] 5 | jobs: 6 | rebase: 7 | uses: softwaremill/github-actions-workflows/.github/workflows/rebase-cmd.yml@main 8 | secrets: 9 | repo-github-token: ${{ secrets.REPO_GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/scala-steward.yml: -------------------------------------------------------------------------------- 1 | name: Scala Steward 2 | 3 | # This workflow will launch at 00:00 every day 4 | on: 5 | schedule: 6 | - cron: '0 0 * * *' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | scala-steward: 11 | uses: softwaremill/github-actions-workflows/.github/workflows/scala-steward.yml@main 12 | secrets: 13 | repo-github-token: ${{secrets.REPO_GITHUB_TOKEN}} 14 | -------------------------------------------------------------------------------- /.github/workflows/test-report.yml: -------------------------------------------------------------------------------- 1 | name: 'Test Report' 2 | on: 3 | workflow_run: 4 | workflows: ['CI'] 5 | types: 6 | - completed 7 | 8 | permissions: 9 | contents: read 10 | actions: read 11 | checks: write 12 | 13 | jobs: 14 | test-report: 15 | uses: softwaremill/github-actions-workflows/.github/workflows/test-report.yml@main -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | 4 | .cache 5 | .history 6 | .env/ 7 | .lib/ 8 | /cdk 9 | /aws-cdk-tests 10 | .vscode/ 11 | dist/* 12 | target/ 13 | lib_managed/ 14 | src_managed/ 15 | project/boot/ 16 | project/plugins/project/ 17 | .bloop 18 | .metals 19 | metals.sbt 20 | .sbt 21 | template.yaml 22 | aws-lambda-cats-effect-template.yaml 23 | aws-lambda-zio-template.yaml 24 | .scala-build 25 | 26 | .bsp 27 | .idea* 28 | .java-version 29 | 30 | file-uploads 31 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | sphinx: 4 | configuration: generated-doc/out/conf.py 5 | 6 | python: 7 | install: 8 | - requirements: generated-doc/out/requirements.txt 9 | 10 | build: 11 | os: ubuntu-22.04 12 | tools: 13 | python: "3.12" 14 | -------------------------------------------------------------------------------- /.sbtopts: -------------------------------------------------------------------------------- 1 | -J-Xmx8G 2 | -J-Xss2M 3 | -Dsbt.task.timings=false 4 | -------------------------------------------------------------------------------- /.scala-steward.conf: -------------------------------------------------------------------------------- 1 | updates.ignore = [ 2 | {groupId = "org.scala-lang", artifactId = "scala-compiler", version = "2.12."}, 3 | {groupId = "org.scala-lang", artifactId = "scala-compiler", version = "2.13."}, 4 | {groupId = "org.scala-lang", artifactId = "scala-compiler", version = "3."}, 5 | {groupId = "io.circe", artifactId="circe-yaml", version="1.15.0" } 6 | ] 7 | updates.pin = [ 8 | {groupId = "com.typesafe.akka", version = "2.6."}, 9 | # Gatling uses scopt 3.7.1 and fails on Gatling/testOnly for scopt 4.x 10 | {groupId = "com.github.scopt", artifactId = "scopt", version = "3.7.1"}, 11 | {groupId = "org.scala-lang", artifactId = "scala3-library", version = "3.3."}, 12 | {groupId = "org.scala-lang", artifactId = "scala3-library_sjs1", version = "3.3."} 13 | ] 14 | -------------------------------------------------------------------------------- /.scalafix.conf: -------------------------------------------------------------------------------- 1 | OrganizeImports.groupedImports = AggressiveMerge 2 | OrganizeImports.targetDialect = Scala3 3 | OrganizeImports.removeUnused = false -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = 3.9.4 2 | maxColumn = 140 3 | runner.dialect = scala213 4 | fileOverride { 5 | "glob:**/scala-3/**" { 6 | runner.dialect = scala3 7 | } 8 | "glob:**/examples/**" { 9 | runner.dialect = scala3 10 | } 11 | "glob:**/integrations/iron/**" { 12 | runner.dialect = scala3 13 | } 14 | "glob:**/json/pickler/**" { 15 | runner.dialect = scala3 16 | } 17 | "glob:**/server/netty-server/sync/**" { 18 | runner.dialect = scala3 19 | } 20 | } -------------------------------------------------------------------------------- /banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/banner.png -------------------------------------------------------------------------------- /client/http4s-client/src/main/scala/sttp/tapir/client/http4s/Http4sClientOptions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.http4s 2 | 3 | import sttp.tapir.{Defaults, TapirFile} 4 | 5 | case class Http4sClientOptions(createFile: () => TapirFile) 6 | 7 | object Http4sClientOptions { 8 | val default: Http4sClientOptions = Http4sClientOptions(Defaults.createTempFile) 9 | } 10 | -------------------------------------------------------------------------------- /client/http4s-client/src/test/scala/sttp/tapir/client/http4s/Http4ClientStreamingTests.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.http4s 2 | 3 | import cats.effect.IO 4 | 5 | import fs2.text 6 | import sttp.capabilities.fs2.Fs2Streams 7 | import sttp.tapir.client.tests.ClientStreamingTests 8 | 9 | class Http4ClientStreamingTests extends Http4sClientTests[Fs2Streams[IO]] with ClientStreamingTests[Fs2Streams[IO]] { 10 | override val streams: Fs2Streams[IO] = Fs2Streams[IO] 11 | override def mkStream(s: String): streams.BinaryStream = fs2.Stream(s).through(text.utf8.encode) 12 | override def rmStream(s: streams.BinaryStream): String = s.through(text.utf8.decode).compile.string.unsafeRunSync() 13 | 14 | streamingTests() 15 | } 16 | -------------------------------------------------------------------------------- /client/http4s-client/src/test/scala/sttp/tapir/client/http4s/Http4sClientBasicTests.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.http4s 2 | 3 | import sttp.tapir.client.tests.ClientBasicTests 4 | 5 | class Http4sClientBasicTests extends Http4sClientTests[Any] with ClientBasicTests { 6 | tests() 7 | } 8 | -------------------------------------------------------------------------------- /client/http4s-client/src/test/scala/sttp/tapir/client/http4s/Http4sClientRequestTests.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.http4s 2 | 3 | import cats.effect._ 4 | import org.scalatest.funsuite.AnyFunSuite 5 | import org.scalatest.matchers.should.Matchers 6 | import sttp.tapir._ 7 | 8 | class Http4sClientRequestTests extends AnyFunSuite with Matchers { 9 | test("should exclude optional query parameter when its value is None") { 10 | // given 11 | val testEndpoint = endpoint.get.in(query[Option[String]]("param")) 12 | 13 | // when 14 | val (http4sRequest, _) = Http4sClientInterpreter[IO]() 15 | .toRequest(testEndpoint, baseUri = None) 16 | .apply(None) 17 | 18 | // then 19 | http4sRequest.queryString shouldBe empty 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/play-client/src/main/scala/sttp/tapir/client/play/PlayClientOptions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.play 2 | 3 | import sttp.tapir.{Defaults, TapirFile} 4 | 5 | case class PlayClientOptions(createFile: () => TapirFile) 6 | 7 | object PlayClientOptions { 8 | val default: PlayClientOptions = PlayClientOptions(Defaults.createTempFile) 9 | } 10 | -------------------------------------------------------------------------------- /client/play-client/src/test/scala/sttp/tapir/client/play/PlayClientBasicTests.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.play 2 | 3 | import sttp.tapir.client.tests.ClientBasicTests 4 | 5 | class PlayClientBasicTests extends PlayClientTests[Any] with ClientBasicTests { 6 | 7 | tests() 8 | 9 | } 10 | -------------------------------------------------------------------------------- /client/play29-client/src/main/scala/sttp/tapir/client/play/PlayClientOptions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.play 2 | 3 | import sttp.tapir.{Defaults, TapirFile} 4 | 5 | case class PlayClientOptions(createFile: () => TapirFile) 6 | 7 | object PlayClientOptions { 8 | val default: PlayClientOptions = PlayClientOptions(Defaults.createTempFile) 9 | } 10 | -------------------------------------------------------------------------------- /client/play29-client/src/test/scala/sttp/tapir/client/play/PlayClientBasicTests.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.play 2 | 3 | import sttp.tapir.client.tests.ClientBasicTests 4 | 5 | class PlayClientBasicTests extends PlayClientTests[Any] with ClientBasicTests { 6 | 7 | tests() 8 | 9 | } 10 | -------------------------------------------------------------------------------- /client/sttp-client/src/main/scala/sttp/tapir/client/sttp/SttpClientOptions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp 2 | 3 | import sttp.tapir.{Defaults, TapirFile} 4 | 5 | case class SttpClientOptions(createFile: () => TapirFile) 6 | 7 | object SttpClientOptions { 8 | val default: SttpClientOptions = SttpClientOptions(Defaults.createTempFile) 9 | } 10 | -------------------------------------------------------------------------------- /client/sttp-client/src/main/scalajs/sttp/tapir/client/sttp/EndpointToSttpClientExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp 2 | 3 | import sttp.model.StatusCode 4 | 5 | private[sttp] trait EndpointToSttpClientExtensions { this: EndpointToSttpClient[_] => 6 | 7 | /** This needs to be platform-specific due to #2663, as on JS we don't get access to the 101 status code. */ 8 | val webSocketSuccessStatusCode: StatusCode = StatusCode.Ok 9 | } 10 | -------------------------------------------------------------------------------- /client/sttp-client/src/main/scalajs/sttp/tapir/client/sttp/ws/fs2/TapirSttpClientFs2WebSockets.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp.ws.fs2 2 | 3 | import cats.effect.Concurrent 4 | import sttp.capabilities.WebSockets 5 | import sttp.capabilities.fs2.Fs2Streams 6 | import sttp.tapir.client.sttp.WebSocketToPipe 7 | 8 | trait TapirSttpClientFs2WebSockets { 9 | implicit def webSocketsSupportedForFs2Streams[F[_]: Concurrent]: WebSocketToPipe[Fs2Streams[F] with WebSockets] = 10 | new WebSocketToFs2Pipe[F, Fs2Streams[F] with WebSockets] 11 | } 12 | -------------------------------------------------------------------------------- /client/sttp-client/src/main/scalajs/sttp/tapir/client/sttp/ws/fs2/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp.ws 2 | 3 | package object fs2 extends TapirSttpClientFs2WebSockets 4 | -------------------------------------------------------------------------------- /client/sttp-client/src/main/scalajs/sttp/tapir/client/sttp/ws/zio/TapirSttpClientZioWebSockets.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp.ws.zio 2 | 3 | import sttp.capabilities.WebSockets 4 | import sttp.capabilities.zio.ZioStreams 5 | import sttp.tapir.client.sttp.WebSocketToPipe 6 | 7 | trait TapirSttpClientZioWebSockets { 8 | implicit val webSocketsSupportedForZioStreams: WebSocketToPipe[ZioStreams with WebSockets] = 9 | new WebSocketToZioPipe[ZioStreams with WebSockets] 10 | } 11 | -------------------------------------------------------------------------------- /client/sttp-client/src/main/scalajs/sttp/tapir/client/sttp/ws/zio/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp.ws 2 | 3 | package object zio extends TapirSttpClientZioWebSockets 4 | -------------------------------------------------------------------------------- /client/sttp-client/src/main/scalajvm-2/sttp/tapir/client/sttp/ws/akkahttp/TapirSttpClientAkkaHttpWebSockets.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp.ws.akkahttp 2 | 3 | import sttp.capabilities.WebSockets 4 | import sttp.capabilities.akka.AkkaStreams 5 | import sttp.tapir.client.sttp.WebSocketToPipe 6 | 7 | import scala.concurrent.ExecutionContext 8 | 9 | trait TapirSttpClientAkkaHttpWebSockets { 10 | implicit def webSocketsSupportedForAkkaStreams(implicit ec: ExecutionContext): WebSocketToPipe[AkkaStreams with WebSockets] = 11 | new WebSocketToAkkaPipe[AkkaStreams with WebSockets] 12 | } 13 | -------------------------------------------------------------------------------- /client/sttp-client/src/main/scalajvm-2/sttp/tapir/client/sttp/ws/akkahttp/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp.ws 2 | 3 | package object akkahttp extends TapirSttpClientAkkaHttpWebSockets 4 | -------------------------------------------------------------------------------- /client/sttp-client/src/main/scalajvm/sttp/tapir/client/sttp/EndpointToSttpClientExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp 2 | 3 | import sttp.model.StatusCode 4 | 5 | private[sttp] trait EndpointToSttpClientExtensions { this: EndpointToSttpClient[_] => 6 | 7 | /** This needs to be platform-specific due to #2663, as on JS we don't get access to the 101 status code. */ 8 | val webSocketSuccessStatusCode: StatusCode = StatusCode.SwitchingProtocols 9 | } 10 | -------------------------------------------------------------------------------- /client/sttp-client/src/main/scalajvm/sttp/tapir/client/sttp/ws/fs2/TapirSttpClientFs2WebSockets.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp.ws.fs2 2 | 3 | import cats.effect.Concurrent 4 | import sttp.capabilities.WebSockets 5 | import sttp.capabilities.fs2.Fs2Streams 6 | import sttp.tapir.client.sttp.WebSocketToPipe 7 | 8 | trait TapirSttpClientFs2WebSockets { 9 | implicit def webSocketsSupportedForFs2Streams[F[_]: Concurrent]: WebSocketToPipe[Fs2Streams[F] with WebSockets] = 10 | new WebSocketToFs2Pipe[F, Fs2Streams[F] with WebSockets] 11 | } 12 | -------------------------------------------------------------------------------- /client/sttp-client/src/main/scalajvm/sttp/tapir/client/sttp/ws/fs2/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp.ws 2 | 3 | package object fs2 extends TapirSttpClientFs2WebSockets 4 | -------------------------------------------------------------------------------- /client/sttp-client/src/main/scalajvm/sttp/tapir/client/sttp/ws/pekkohttp/TapirSttpClientPekkoHttpWebSockets.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp.ws.pekkohttp 2 | 3 | import sttp.capabilities.WebSockets 4 | import sttp.capabilities.pekko.PekkoStreams 5 | import sttp.tapir.client.sttp.WebSocketToPipe 6 | 7 | import scala.concurrent.ExecutionContext 8 | 9 | trait TapirSttpClientPekkoHttpWebSockets { 10 | implicit def webSocketsSupportedForPekkoStreams(implicit ec: ExecutionContext): WebSocketToPipe[PekkoStreams with WebSockets] = 11 | new WebSocketToPekkoPipe[PekkoStreams with WebSockets] 12 | } 13 | -------------------------------------------------------------------------------- /client/sttp-client/src/main/scalajvm/sttp/tapir/client/sttp/ws/pekkohttp/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp.ws 2 | 3 | package object pekkohttp extends TapirSttpClientPekkoHttpWebSockets 4 | -------------------------------------------------------------------------------- /client/sttp-client/src/main/scalajvm/sttp/tapir/client/sttp/ws/zio/TapirSttpClientZioWebSockets.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp.ws.zio 2 | 3 | import sttp.capabilities.WebSockets 4 | import sttp.capabilities.zio.ZioStreams 5 | import sttp.tapir.client.sttp.WebSocketToPipe 6 | 7 | trait TapirSttpClientZioWebSockets { 8 | implicit val webSocketsSupportedForZioStreams: WebSocketToPipe[ZioStreams with WebSockets] = 9 | new WebSocketToZioPipe[ZioStreams with WebSockets] 10 | } 11 | -------------------------------------------------------------------------------- /client/sttp-client/src/main/scalajvm/sttp/tapir/client/sttp/ws/zio/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp.ws 2 | 3 | package object zio extends TapirSttpClientZioWebSockets 4 | -------------------------------------------------------------------------------- /client/sttp-client/src/main/scalanative/sttp/tapir/client/sttp/EndpointToSttpClientExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp 2 | 3 | import sttp.model.StatusCode 4 | 5 | private[sttp] trait EndpointToSttpClientExtensions { this: EndpointToSttpClient[_] => 6 | 7 | /** This needs to be platform-specific due to #2663, as on JS we don't get access to the 101 status code. */ 8 | val webSocketSuccessStatusCode: StatusCode = StatusCode.SwitchingProtocols 9 | } 10 | -------------------------------------------------------------------------------- /client/sttp-client/src/test/scala/sttp/tapir/client/sttp/SttpClientBasicTests.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp 2 | 3 | import sttp.tapir.client.tests.ClientBasicTests 4 | 5 | class SttpClientBasicTests extends SttpClientTests[Any] with ClientBasicTests { 6 | override def wsToPipe: WebSocketToPipe[Any] = implicitly 7 | 8 | tests() 9 | } 10 | -------------------------------------------------------------------------------- /client/sttp-client/src/test/scala/sttp/tapir/client/sttp/SttpClientMultipartTests.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp 2 | 3 | import sttp.tapir.client.tests.ClientMultipartTests 4 | 5 | class SttpClientMultipartTests extends SttpClientTests[Any] with ClientMultipartTests { 6 | override def wsToPipe: WebSocketToPipe[Any] = implicitly 7 | 8 | multipartTests() 9 | } 10 | -------------------------------------------------------------------------------- /client/sttp-client/src/test/scalajvm/sttp/tapir/client/sttp/SttpClientBasicZioTests.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp 2 | 3 | import sttp.tapir.client.tests.ClientBasicTests 4 | 5 | class SttpClientBasicZioTests extends SttpClientZioTests[Any] with ClientBasicTests { 6 | override def wsToPipe: WebSocketToPipe[Any] = implicitly 7 | 8 | tests() 9 | } 10 | -------------------------------------------------------------------------------- /client/sttp-client4/src/main/scala/sttp/tapir/client/sttp4/SttpClientOptions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4 2 | 3 | import sttp.tapir.{Defaults, TapirFile} 4 | 5 | case class SttpClientOptions(createFile: () => TapirFile) 6 | 7 | object SttpClientOptions { 8 | val default: SttpClientOptions = SttpClientOptions(Defaults.createTempFile) 9 | } 10 | -------------------------------------------------------------------------------- /client/sttp-client4/src/main/scala/sttp/tapir/client/sttp4/stream/StreamsNotWebSockets.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4.stream 2 | 3 | import sttp.capabilities.WebSockets 4 | 5 | // Type class to ensure S extends Streams[S] but NOT WebSockets 6 | trait StreamsNotWebSockets[S] 7 | object StreamsNotWebSockets { 8 | // This implicit is available if S does NOT extend WebSockets 9 | implicit def allowIfNotWebSockets[S]: StreamsNotWebSockets[S] = new StreamsNotWebSockets[S] {} 10 | 11 | // This implicit is triggered if S extends WebSockets, causing ambiguity 12 | implicit def disallowIfWebSockets[S <: WebSockets]: StreamsNotWebSockets[S] = ??? 13 | } 14 | -------------------------------------------------------------------------------- /client/sttp-client4/src/main/scalajs/sttp/tapir/client/sttp4/WebSocketEndpointToSttpClientExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4 2 | 3 | import sttp.model.StatusCode 4 | 5 | private[sttp4] trait WebSocketEndpointToSttpClientExtensions { 6 | 7 | /** This needs to be platform-specific due to #2663, as on JS we don't get access to the 101 status code. */ 8 | val webSocketSuccessStatusCode: StatusCode = StatusCode.Ok 9 | } 10 | -------------------------------------------------------------------------------- /client/sttp-client4/src/main/scalajs/sttp/tapir/client/sttp4/ws/fs2/TapirSttpClientFs2WebSockets.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4.ws.fs2 2 | 3 | import cats.effect.Concurrent 4 | import sttp.capabilities.WebSockets 5 | import sttp.capabilities.fs2.Fs2Streams 6 | import sttp.tapir.client.sttp4.WebSocketToPipe 7 | 8 | trait TapirSttpClientFs2WebSockets { 9 | implicit def webSocketsSupportedForFs2Streams[F[_]: Concurrent]: WebSocketToPipe[Fs2Streams[F] with WebSockets] = 10 | new WebSocketToFs2Pipe[F, Fs2Streams[F] with WebSockets] 11 | } 12 | -------------------------------------------------------------------------------- /client/sttp-client4/src/main/scalajs/sttp/tapir/client/sttp4/ws/fs2/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4.ws 2 | 3 | package object fs2 extends TapirSttpClientFs2WebSockets 4 | -------------------------------------------------------------------------------- /client/sttp-client4/src/main/scalajs/sttp/tapir/client/sttp4/ws/zio/TapirSttpClientZioWebSockets.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4.ws.zio 2 | 3 | import sttp.capabilities.WebSockets 4 | import sttp.capabilities.zio.ZioStreams 5 | import sttp.tapir.client.sttp4.WebSocketToPipe 6 | 7 | trait TapirSttpClientZioWebSockets { 8 | implicit val webSocketsSupportedForZioStreams: WebSocketToPipe[ZioStreams with WebSockets] = 9 | new WebSocketToZioPipe[ZioStreams with WebSockets] 10 | } 11 | -------------------------------------------------------------------------------- /client/sttp-client4/src/main/scalajs/sttp/tapir/client/sttp4/ws/zio/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4.ws 2 | 3 | package object zio extends TapirSttpClientZioWebSockets 4 | -------------------------------------------------------------------------------- /client/sttp-client4/src/main/scalajvm-2/sttp/tapir/client/sttp4/ws/akkahttp/TapirSttpClientAkkaHttpWebSockets.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4.ws.akkahttp 2 | 3 | import sttp.capabilities.WebSockets 4 | import sttp.capabilities.akka.AkkaStreams 5 | import sttp.tapir.client.sttp4.WebSocketToPipe 6 | 7 | import scala.concurrent.ExecutionContext 8 | 9 | trait TapirSttpClientAkkaHttpWebSockets { 10 | implicit def webSocketsSupportedForAkkaStreams(implicit ec: ExecutionContext): WebSocketToPipe[AkkaStreams with WebSockets] = 11 | new WebSocketToAkkaPipe[AkkaStreams with WebSockets] 12 | } 13 | -------------------------------------------------------------------------------- /client/sttp-client4/src/main/scalajvm-2/sttp/tapir/client/sttp4/ws/akkahttp/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4.ws 2 | 3 | package object akkahttp extends TapirSttpClientAkkaHttpWebSockets 4 | -------------------------------------------------------------------------------- /client/sttp-client4/src/main/scalajvm/sttp/tapir/client/sttp4/WebSocketEndpointToSttpClientExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4 2 | 3 | import sttp.model.StatusCode 4 | 5 | private[sttp4] trait WebSocketEndpointToSttpClientExtensions { 6 | 7 | /** This needs to be platform-specific due to #2663, as on JS we don't get access to the 101 status code. */ 8 | val webSocketSuccessStatusCode: StatusCode = StatusCode.SwitchingProtocols 9 | } 10 | -------------------------------------------------------------------------------- /client/sttp-client4/src/main/scalajvm/sttp/tapir/client/sttp4/ws/fs2/TapirSttpClientFs2WebSockets.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4.ws.fs2 2 | 3 | import cats.effect.Concurrent 4 | import sttp.capabilities.WebSockets 5 | import sttp.capabilities.fs2.Fs2Streams 6 | import sttp.tapir.client.sttp4.WebSocketToPipe 7 | 8 | trait TapirSttpClientFs2WebSockets { 9 | implicit def webSocketsSupportedForFs2Streams[F[_]: Concurrent]: WebSocketToPipe[Fs2Streams[F] with WebSockets] = 10 | new WebSocketToFs2Pipe[F, Fs2Streams[F] with WebSockets] 11 | } 12 | -------------------------------------------------------------------------------- /client/sttp-client4/src/main/scalajvm/sttp/tapir/client/sttp4/ws/fs2/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4.ws 2 | 3 | package object fs2 extends TapirSttpClientFs2WebSockets 4 | -------------------------------------------------------------------------------- /client/sttp-client4/src/main/scalajvm/sttp/tapir/client/sttp4/ws/pekkohttp/TapirSttpClientPekkoHttpWebSockets.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4.ws.pekkohttp 2 | 3 | import sttp.capabilities.WebSockets 4 | import sttp.capabilities.pekko.PekkoStreams 5 | import sttp.tapir.client.sttp4.WebSocketToPipe 6 | 7 | import scala.concurrent.ExecutionContext 8 | 9 | trait TapirSttpClientPekkoHttpWebSockets { 10 | implicit def webSocketsSupportedForPekkoStreams(implicit ec: ExecutionContext): WebSocketToPipe[PekkoStreams with WebSockets] = 11 | new WebSocketToPekkoPipe[PekkoStreams with WebSockets] 12 | } 13 | -------------------------------------------------------------------------------- /client/sttp-client4/src/main/scalajvm/sttp/tapir/client/sttp4/ws/pekkohttp/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4.ws 2 | 3 | package object pekkohttp extends TapirSttpClientPekkoHttpWebSockets 4 | -------------------------------------------------------------------------------- /client/sttp-client4/src/main/scalajvm/sttp/tapir/client/sttp4/ws/zio/TapirSttpClientZioWebSockets.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4.ws.zio 2 | 3 | import sttp.capabilities.WebSockets 4 | import sttp.capabilities.zio.ZioStreams 5 | import sttp.tapir.client.sttp4.WebSocketToPipe 6 | 7 | trait TapirSttpClientZioWebSockets { 8 | implicit val webSocketsSupportedForZioStreams: WebSocketToPipe[ZioStreams with WebSockets] = 9 | new WebSocketToZioPipe[ZioStreams with WebSockets] 10 | } 11 | -------------------------------------------------------------------------------- /client/sttp-client4/src/main/scalajvm/sttp/tapir/client/sttp4/ws/zio/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4.ws 2 | 3 | package object zio extends TapirSttpClientZioWebSockets 4 | -------------------------------------------------------------------------------- /client/sttp-client4/src/main/scalanative/sttp/tapir/client/sttp4/WebSocketEndpointToSttpClientExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4 2 | 3 | import sttp.model.StatusCode 4 | 5 | private[sttp4] trait WebSocketEndpointToSttpClientExtensions { 6 | 7 | /** This needs to be platform-specific due to #2663, as on JS we don't get access to the 101 status code. */ 8 | val webSocketSuccessStatusCode: StatusCode = StatusCode.SwitchingProtocols 9 | } 10 | -------------------------------------------------------------------------------- /client/sttp-client4/src/test/scala/sttp/tapir/client/sttp4/SttpClientMultipartTests.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4 2 | 3 | import sttp.tapir.client.tests.ClientMultipartTests 4 | 5 | class SttpClientMultipartTests extends SttpClientTestsSender with ClientMultipartTests { 6 | multipartTests() 7 | } 8 | -------------------------------------------------------------------------------- /client/sttp-client4/src/test/scala/sttp/tapir/client/sttp4/SttpClientSimpleTests.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4 2 | 3 | import sttp.tapir.client.tests.ClientBasicTests 4 | 5 | class SttpClientSimpleTests extends SttpClientTestsSender with ClientBasicTests { 6 | tests() 7 | } 8 | -------------------------------------------------------------------------------- /client/sttp-client4/src/test/scalajvm/sttp/tapir/client/sttp4/SttpClientZioTests.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.sttp4 2 | 3 | import sttp.tapir.client.tests.ClientBasicTests 4 | 5 | class SttpClientZioTests extends SttpClientZioTestsSender with ClientBasicTests { 6 | tests() 7 | } 8 | -------------------------------------------------------------------------------- /client/tests/src/main/scalajs/sttp/tapir/client/tests/ClientTestsPlatform.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.tests 2 | 3 | import cats.effect.unsafe.IORuntime 4 | import scala.concurrent.ExecutionContext 5 | 6 | object ClientTestsPlatform { 7 | // Using the default ScalaTest execution context seems to cause issues on JS. 8 | // https://github.com/scalatest/scalatest/issues/1039 9 | val executionContext: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global 10 | val ioRT: IORuntime = cats.effect.unsafe.implicits.global 11 | 12 | val platformIsScalaJS: Boolean = true 13 | val platformIsScalaNative: Boolean = false 14 | } 15 | -------------------------------------------------------------------------------- /client/tests/src/main/scalajvm/sttp/tapir/client/tests/ClientTestsPlatform.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.client.tests 2 | 3 | import cats.effect.unsafe.IORuntime 4 | import scala.concurrent.ExecutionContext 5 | 6 | object ClientTestsPlatform { 7 | // Using the default ScalaTest execution context seems to cause issues on JS. 8 | // https://github.com/scalatest/scalatest/issues/1039 9 | val executionContext: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global 10 | val ioRT: IORuntime = cats.effect.unsafe.implicits.global 11 | 12 | val platformIsScalaJS: Boolean = false 13 | val platformIsScalaNative: Boolean = false 14 | } 15 | -------------------------------------------------------------------------------- /client/testserver/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %date [%thread] %-5level %logger{36} - %msg%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /core/src/main/boilerplate/sttp/tapir/internal/ParamsToSeq.scala.template: -------------------------------------------------------------------------------- 1 | package sttp.tapir.internal 2 | 3 | private[tapir] object ParamsToSeq { 4 | def apply[T](a: Any): Seq[_] = { 5 | a match { 6 | [2..#case ([#v1#]) => Seq([#v1#])# 7 | ] 8 | case v1 => Seq(v1) // single value is a catch-all so must be last 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/boilerplate/sttp/tapir/internal/SeqToParams.scala.template: -------------------------------------------------------------------------------- 1 | package sttp.tapir.internal; 2 | 3 | object SeqToParams { 4 | def apply[T](seq: Seq[T]): Any = { 5 | seq match { 6 | case Seq() => () 7 | case Seq(v1) => v1 8 | [2..#case Seq([#v1#]) => ([#v1#])# 9 | ] 10 | case _ => throw new IllegalArgumentException(s"Cannot convert $seq to params!") 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/boilerplate/sttp/tapir/typelevel/TupleArity.scala.template: -------------------------------------------------------------------------------- 1 | package sttp.tapir.typelevel 2 | 3 | /** 4 | * The arity of type `T` (if its a tuple), as viewed at compile-time (at run-time, a value that is determined to be 5 | * a "singular" type, might in fact be a tuple). 6 | */ 7 | trait TupleArity[T] { 8 | def arity: Int 9 | } 10 | 11 | object TupleArity extends LowPriorityTupleArity { 12 | [2..21#implicit def tupleArity1[[#A1#]]: TupleArity[([#A1#])] = new TupleArity[([#A1#])] { 13 | def arity: Int = 1 14 | }# 15 | ] 16 | } 17 | 18 | trait LowPriorityTupleArity { 19 | implicit def tupleArity1[A]: TupleArity[A] = new TupleArity[A] { 20 | def arity: Int = 1 21 | } 22 | } -------------------------------------------------------------------------------- /core/src/main/scala-2.13+/sttp/tapir/macros/ModifyMacroSupport.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.macros 2 | 3 | import scala.annotation.compileTimeOnly 4 | import scala.collection.Factory 5 | 6 | trait ModifyMacroSupport extends ModifyMacroFunctorSupport { 7 | implicit def traversableModifyFunctor[F[_], A](implicit 8 | fac: Factory[A, F[A]], 9 | ev: F[A] => Iterable[A] 10 | ): ModifyFunctor[F, A] = 11 | new ModifyFunctor[F, A] {} 12 | 13 | implicit class ModifyEachMap[F[_, _], K, T](t: F[K, T])(implicit fac: Factory[(K, T), F[K, T]]) { 14 | @compileTimeOnly(canOnlyBeUsedInsideModify("each")) 15 | def each: T = sys.error("") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/scala-2.13-/sttp/tapir/CodecExtensions2.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | trait CodecExtensions2 4 | -------------------------------------------------------------------------------- /core/src/main/scala-2/sttp/tapir/generic/auto/SchemaDerivation.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.generic.auto 2 | 3 | import sttp.tapir.Schema 4 | import sttp.tapir.generic.Derived 5 | import sttp.tapir.generic.internal.MagnoliaDerivedMacro 6 | 7 | trait SchemaDerivation extends SchemaMagnoliaDerivation { 8 | implicit def schemaForCaseClass[T]: Derived[Schema[T]] = macro MagnoliaDerivedMacro.generateDerivedGen[T] 9 | } 10 | -------------------------------------------------------------------------------- /core/src/main/scala-2/sttp/tapir/generic/auto/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.generic 2 | 3 | package object auto extends SchemaDerivation 4 | -------------------------------------------------------------------------------- /core/src/main/scala-2/sttp/tapir/generic/internal/MagnoliaDerivedMacro.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.generic.internal 2 | 3 | import magnolia1.Magnolia 4 | import sttp.tapir.generic.Derived 5 | 6 | private[tapir] object MagnoliaDerivedMacro { 7 | import scala.reflect.macros.whitebox 8 | 9 | def generateDerivedGen[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[Derived[T]] = { 10 | import c.universe._ 11 | c.Expr[Derived[T]](q"_root_.sttp.tapir.generic.Derived(${Magnolia.gen[T](c)(implicitly[c.WeakTypeTag[T]])})") 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/scala-2/sttp/tapir/internal/AttributeKeyMacro.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.internal 2 | 3 | import sttp.tapir.AttributeKey 4 | 5 | import scala.reflect.macros.blackbox 6 | 7 | private[tapir] object AttributeKeyMacro { 8 | def apply[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[AttributeKey[T]] = { 9 | import c.universe._ 10 | c.Expr[AttributeKey[T]]( 11 | q"new _root_.sttp.tapir.AttributeKey(${c.universe.show(implicitly[c.WeakTypeTag[T]].tpe)})" 12 | ) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/scala-2/sttp/tapir/internal/Debug.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.internal 2 | 3 | private[tapir] object Debug { 4 | import scala.reflect.macros.blackbox 5 | 6 | private val macroDebugEnabled = System.getenv("TAPIR_LOG_GENERATED_CODE") == "true" 7 | 8 | def logGeneratedCode(c: blackbox.Context)(typeName: String, tree: c.universe.Tree): Unit = { 9 | import c.universe._ 10 | if (macroDebugEnabled) { 11 | println(s"""$typeName macro output start:""") 12 | println(showCode(tree)) 13 | println(s"""$typeName macro output end.""") 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/scala-2/sttp/tapir/macros/AttributeKeyMacros.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.macros 2 | 3 | import sttp.tapir.AttributeKey 4 | import sttp.tapir.internal.AttributeKeyMacro 5 | 6 | trait AttributeKeyMacros { 7 | def apply[T]: AttributeKey[T] = macro AttributeKeyMacro[T] 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/scala-2/sttp/tapir/macros/EndpointInputMacros.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.macros 2 | 3 | import sttp.tapir.EndpointInput 4 | import sttp.tapir.internal.EndpointInputAnnotationsMacro 5 | 6 | trait EndpointInputMacros { 7 | 8 | /** Derives an input description using metadata specified with annotations on the given case class. Each field of the case class must be 9 | * annotated with one of the annotations from [[sttp.tapir.EndpointIO.annotations]]. Additional schema meta-data can be specified using 10 | * annotations from [[sttp.tapir.Schema.annotations]]. The result is mapped to an instance of the [[T]] type. 11 | */ 12 | def derived[T]: EndpointInput[T] = macro EndpointInputAnnotationsMacro.generateEndpointInput[T] 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/scala-2/sttp/tapir/macros/EndpointOutputMacros.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.macros 2 | 3 | import sttp.tapir.EndpointOutput 4 | import sttp.tapir.internal.EndpointOutputAnnotationsMacro 5 | 6 | trait EndpointOutputMacros { 7 | 8 | /** Derives an output description using metadata specified with annotations on the given case class. Each field of the case class must be 9 | * annotated with one of the annotations from [[sttp.tapir.EndpointIO.annotations]]. Additional schema meta-data can be specified using 10 | * annotations from [[sttp.tapir.Schema.annotations]]. The result is mapped to an instance of the [[T]] type. 11 | */ 12 | def derived[T]: EndpointOutput[T] = macro EndpointOutputAnnotationsMacro.generateEndpointOutput[T] 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/scala-2/sttp/tapir/macros/EndpointTransputMacros.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.macros 2 | 3 | import sttp.tapir.EndpointTransput 4 | import sttp.tapir.internal.MapToMacro 5 | 6 | trait EndpointTransputMacros[T] { this: EndpointTransput[T] => 7 | def mapTo[CASE_CLASS]: ThisType[CASE_CLASS] = macro MapToMacro.generateMapTo[ThisType, T, CASE_CLASS] 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/scala-2/sttp/tapir/macros/ErasureSameAsTypeMacros.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.macros 2 | 3 | import sttp.tapir.internal.ErasureSameAsTypeMacro 4 | import sttp.tapir.typelevel.ErasureSameAsType 5 | 6 | trait ErasureSameAsTypeMacros { 7 | implicit def instance[T]: ErasureSameAsType[T] = macro ErasureSameAsTypeMacro.instance[T] 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/scala-2/sttp/tapir/macros/FormCodecMacros.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.macros 2 | 3 | import sttp.tapir.generic.Configuration 4 | import sttp.tapir.internal.FormCodecMacro 5 | import sttp.tapir.{Codec, CodecFormat} 6 | 7 | trait FormCodecMacros { 8 | implicit def formCaseClassCodec[T <: Product with Serializable](implicit 9 | conf: Configuration 10 | ): Codec[String, T, CodecFormat.XWwwFormUrlencoded] = 11 | macro FormCodecMacro.generateForCaseClass[T] 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/scala-2/sttp/tapir/macros/MultipartCodecMacros.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.macros 2 | 3 | import sttp.tapir.MultipartCodec 4 | import sttp.tapir.generic.Configuration 5 | import sttp.tapir.internal.MultipartCodecMacro 6 | 7 | trait MultipartCodecMacros { 8 | implicit def multipartCaseClassCodec[T <: Product with Serializable](implicit 9 | conf: Configuration 10 | ): MultipartCodec[T] = 11 | macro MultipartCodecMacro.generateForCaseClass[T] 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/scala-2/sttp/tapir/macros/SchemaAnnotationsMacros.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.macros 2 | 3 | import sttp.tapir.SchemaAnnotations 4 | import sttp.tapir.internal.SchemaAnnotationsMacro 5 | 6 | trait SchemaAnnotationsMacros { 7 | implicit def derived[T]: SchemaAnnotations[T] = macro SchemaAnnotationsMacro.derived[T] 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/scala-2/sttp/tapir/macros/ValidatorMacros.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.macros 2 | 3 | import sttp.tapir.Validator 4 | import sttp.tapir.internal.ValidatorEnumerationMacro 5 | 6 | trait ValidatorMacros { 7 | 8 | /** Creates an enum validator where all subtypes of the sealed hierarchy `T` are `object` s. This enumeration will only be used for 9 | * documentation, as a value outside of the allowed values will not be decoded in the first place (the decoder has no other option than 10 | * to fail). 11 | */ 12 | def derivedEnumeration[T]: Validator.Enumeration[T] = macro ValidatorEnumerationMacro.apply[T] 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/scala-3-2.13+/sttp/tapir/model/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | package object model { 4 | 5 | /** Used to lookup codecs which split/combine values using a comma. */ 6 | type CommaSeparated[T] = Delimited[",", T] 7 | } 8 | -------------------------------------------------------------------------------- /core/src/main/scala-3/sttp/tapir/generic/auto/SchemaDerivation.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.generic.auto 2 | 3 | import sttp.tapir.Schema 4 | import sttp.tapir.generic.{Configuration, Derived} 5 | 6 | import scala.deriving.Mirror 7 | 8 | trait SchemaDerivation extends SchemaMagnoliaDerivation: 9 | inline implicit def schemaForCaseClass[T](implicit m: Mirror.Of[T], cfg: Configuration): Derived[Schema[T]] = Derived(derived[T]) 10 | -------------------------------------------------------------------------------- /core/src/main/scala-3/sttp/tapir/generic/auto/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.generic 2 | 3 | package object auto extends SchemaDerivation 4 | -------------------------------------------------------------------------------- /core/src/main/scala-3/sttp/tapir/macros/AttributeKeyMacros.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.macros 2 | 3 | import sttp.tapir.AttributeKey 4 | 5 | import scala.quoted.* 6 | 7 | trait AttributeKeyMacros { 8 | inline def apply[T]: AttributeKey[T] = ${ AttributeKeyMacros[T] } 9 | } 10 | 11 | private[tapir] object AttributeKeyMacros { 12 | def apply[T: Type](using q: Quotes): Expr[AttributeKey[T]] = { 13 | import quotes.reflect.* 14 | val t = TypeRepr.of[T] 15 | 16 | '{ new AttributeKey[T](${ Expr(t.show) }) } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/scala-3/sttp/tapir/macros/EndpointTransputMacros.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.macros 2 | 3 | import sttp.tapir.EndpointTransput 4 | import sttp.tapir.internal.MappingMacros 5 | 6 | import scala.deriving.Mirror 7 | 8 | trait EndpointTransputMacros[T] { this: EndpointTransput[T] => 9 | inline def mapTo[CASE_CLASS <: Product](using mc: Mirror.ProductOf[CASE_CLASS]): ThisType[CASE_CLASS] = 10 | this.map(MappingMacros.mappingImpl[T, CASE_CLASS]) 11 | 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/scala-3/sttp/tapir/macros/ModifyMacroSupport.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.macros 2 | 3 | import scala.annotation.nowarn 4 | import scala.collection.Factory 5 | 6 | trait ModifyMacroSupport extends ModifyMacroFunctorSupport { 7 | implicit def traversableModifyFunctor[F[_], A](implicit 8 | fac: Factory[A, F[A]], 9 | ev: F[A] => Iterable[A] 10 | ): ModifyFunctor[F, A] = 11 | new ModifyFunctor[F, A] {} 12 | 13 | implicit class ModifyEachMap[F[_, _], K, T](t: F[K, T])(implicit @nowarn fac: Factory[(K, T), F[K, T]]) { 14 | // @compileTimeOnly(canOnlyBeUsedInsideModify("each")) TODO 15 | def each: T = sys.error("") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/scala-3/sttp/tapir/macros/SchemaAnnotationsMacros.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.macros 2 | 3 | import sttp.tapir.SchemaAnnotations 4 | import sttp.tapir.internal.SchemaAnnotationsMacro 5 | 6 | trait SchemaAnnotationsMacros { 7 | implicit inline def derived[T]: SchemaAnnotations[T] = ${ SchemaAnnotationsMacro.derived[T] } 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/scala/sttp/tapir/FieldName.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | case class FieldName(name: String, encodedName: String) 4 | 5 | object FieldName { 6 | def apply(name: String) = new FieldName(name, name) 7 | } 8 | -------------------------------------------------------------------------------- /core/src/main/scala/sttp/tapir/InputStreamRange.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | import java.io.InputStream 4 | 5 | case class InputStreamRange(inputStream: () => InputStream, range: Option[RangeValue] = None) { 6 | def inputStreamFromRangeStart: () => InputStream = range.flatMap(_.start) match { 7 | case Some(start) if start > 0 => 8 | () => 9 | val openedStream = inputStream() 10 | val skipped = openedStream.skip(start) 11 | if (skipped == start) 12 | openedStream 13 | else 14 | throw new IllegalArgumentException(s"Illegal range start: $start, could skip only $skipped bytes") 15 | case _ => inputStream 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/scala/sttp/tapir/capabilities/NoStreams.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.capabilities 2 | 3 | import sttp.capabilities.Streams 4 | 5 | trait NoStreams extends Streams[NoStreams] { 6 | override type BinaryStream = Nothing 7 | override type Pipe[A, B] = Nothing 8 | } 9 | object NoStreams extends NoStreams 10 | -------------------------------------------------------------------------------- /core/src/main/scala/sttp/tapir/generic/Derived.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.generic 2 | 3 | case class Derived[T](value: T) extends AnyVal 4 | -------------------------------------------------------------------------------- /core/src/main/scala/sttp/tapir/macros/ModifyMacroFunctorSupport.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.macros 2 | 3 | trait ModifyMacroFunctorSupport { 4 | implicit class ModifyEach[F[_], T](t: F[T])(implicit f: ModifyFunctor[F, T]) { 5 | // @compileTimeOnly(canOnlyBeUsedInsideModify("each")) TODO 6 | def each: T = sys.error("") 7 | } 8 | 9 | trait ModifyFunctor[F[_], A] { 10 | // @compileTimeOnly(canOnlyBeUsedInsideModify("each")) TODO 11 | def each(fa: F[A])(f: A => A): F[A] = sys.error("") 12 | } 13 | 14 | implicit def optionModifyFunctor[A]: ModifyFunctor[Option, A] = 15 | new ModifyFunctor[Option, A] {} 16 | 17 | private[tapir] def canOnlyBeUsedInsideModify(method: String) = 18 | s"$method can only be used inside ignore" 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/scala/sttp/tapir/model/Delimited.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.model 2 | 3 | /** Used to represent lists of values delimited with `DELIMITER`. 4 | * 5 | * @see 6 | * [[sttp.tapir.Codec.delimited]] 7 | * @see 8 | * [[sttp.tapir.Schema.schemaForDelimited]] 9 | * @see 10 | * [[CommaSeparated]] 11 | */ 12 | case class Delimited[DELIMITER <: String, T](values: List[T]) 13 | -------------------------------------------------------------------------------- /core/src/main/scala/sttp/tapir/model/StatusCodeRange.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.model 2 | 3 | sealed abstract class StatusCodeRange(val range: Int) 4 | object StatusCodeRange { 5 | case object Informational extends StatusCodeRange(1) 6 | case object Success extends StatusCodeRange(2) 7 | case object Redirect extends StatusCodeRange(3) 8 | case object ClientError extends StatusCodeRange(4) 9 | case object ServerError extends StatusCodeRange(5) 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/scala/sttp/tapir/model/UsernamePassword.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.model 2 | 3 | case class UsernamePassword(username: String, password: Option[String]) 4 | -------------------------------------------------------------------------------- /core/src/main/scala/sttp/tapir/model/ws.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.model 2 | 3 | import sttp.tapir.DecodeResult 4 | import sttp.ws.{WebSocketException, WebSocketFrame} 5 | 6 | class UnsupportedWebSocketFrameException(f: WebSocketFrame) extends WebSocketException(s"Unsupported web socket frame: $f") 7 | 8 | class WebSocketFrameDecodeFailure(f: WebSocketFrame, failure: DecodeResult.Failure) 9 | extends WebSocketException(s"Cannot decode frame: $f, due to: $failure") 10 | -------------------------------------------------------------------------------- /core/src/main/scala/sttp/tapir/package.scala: -------------------------------------------------------------------------------- 1 | package sttp 2 | 3 | import sttp.model.Part 4 | 5 | package object tapir extends Tapir { 6 | // a part which contains one of the types supported by BodyType 7 | type RawPart = Part[_] 8 | type AnyPart = Part[_] 9 | // used in multipart codecs 10 | type AnyListCodec = Codec[_ <: List[_], _, _ <: CodecFormat] 11 | 12 | type AnyEndpoint = Endpoint[_, _, _, _, _] 13 | type PublicEndpoint[INPUT, ERROR_OUTPUT, OUTPUT, -R] = Endpoint[Unit, INPUT, ERROR_OUTPUT, OUTPUT, R] 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/scala/sttp/tapir/typelevel/BinaryTupleOp.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.typelevel 2 | 3 | trait BinaryTupleOp { 4 | def leftArity: Int 5 | def rightArity: Int 6 | } 7 | -------------------------------------------------------------------------------- /core/src/main/scala/sttp/tapir/typelevel/ErasureSameAsType.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.typelevel 2 | 3 | import sttp.tapir.macros.ErasureSameAsTypeMacros 4 | 5 | /** An instance should be available in the implicit scope if the erasure of `T` is equal to `T`, that is when we can check at runtime that a 6 | * value is of type `T` using the [[scala.reflect.ClassTag]]. 7 | */ 8 | trait ErasureSameAsType[T] 9 | 10 | object ErasureSameAsType extends ErasureSameAsTypeMacros 11 | -------------------------------------------------------------------------------- /core/src/main/scalajs/sttp/tapir/CodecExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | trait CodecExtensions 4 | -------------------------------------------------------------------------------- /core/src/main/scalajs/sttp/tapir/Defaults.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | import scala.scalajs.js 4 | import scala.scalajs.js.JSConverters._ 5 | import scala.scalajs.js.typedarray.AB2TA 6 | 7 | import org.scalajs.dom.BlobPart 8 | import org.scalajs.dom.File 9 | 10 | object Defaults { 11 | def createTempFile: () => TapirFile = () => 12 | new File( 13 | Iterable(Array.empty[Byte].toTypedArray.asInstanceOf[BlobPart]).toJSIterable, 14 | "temp.txt" 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/scalajs/sttp/tapir/TapirExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | import org.scalajs.dom.File 4 | 5 | trait TapirExtensions { 6 | type TapirFile = org.scalajs.dom.File 7 | } 8 | 9 | object TapirFile { 10 | def name(f: TapirFile): String = f.name 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/scalajs/sttp/tapir/static/TapirStaticContentEndpoints.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.static 2 | 3 | trait TapirStaticContentEndpoints 4 | -------------------------------------------------------------------------------- /core/src/main/scalajvm/sttp/tapir/CodecExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | import sttp.tapir.Codec.fileRange 4 | import sttp.tapir.CodecFormat.OctetStream 5 | 6 | import java.nio.file.Path 7 | 8 | trait CodecExtensions { 9 | implicit lazy val path: Codec[FileRange, Path, OctetStream] = fileRange.map(d => d.file.toPath)(e => FileRange(e.toFile)) 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/scalajvm/sttp/tapir/Defaults.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | import java.io.File 4 | 5 | object Defaults { 6 | def createTempFile: () => TapirFile = () => File.createTempFile("tapir", "tmp") 7 | def deleteFile(): TapirFile => Unit = file => { 8 | val _ = file.delete() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/scalajvm/sttp/tapir/TapirExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | import java.nio.file.Path 4 | 5 | trait TapirExtensions { 6 | type TapirFile = java.io.File 7 | def pathBody: EndpointIO.Body[FileRange, Path] = binaryBody(RawBodyType.FileBody)[Path] 8 | } 9 | 10 | object TapirFile { 11 | def name(f: TapirFile): String = f.getName 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/scalanative/sttp/tapir/CodecExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | import sttp.tapir.Codec.fileRange 4 | import sttp.tapir.CodecFormat.OctetStream 5 | 6 | import java.nio.file.Path 7 | 8 | trait CodecExtensions { 9 | implicit lazy val path: Codec[FileRange, Path, OctetStream] = fileRange.map(d => d.file.toPath)(e => FileRange(e.toFile)) 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/scalanative/sttp/tapir/Defaults.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | import java.io.File 4 | 5 | object Defaults { 6 | def createTempFile: () => TapirFile = () => File.createTempFile("tapir", "tmp") 7 | def deleteFile(): TapirFile => Unit = file => file.delete() 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/scalanative/sttp/tapir/TapirExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | import java.nio.file.Path 4 | 5 | trait TapirExtensions { 6 | type TapirFile = java.io.File 7 | def pathBody: EndpointIO.Body[FileRange, Path] = binaryBody(RawBodyType.FileBody)[Path] 8 | } 9 | 10 | object TapirFile { 11 | def name(f: TapirFile): String = f.getName 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/scalanative/sttp/tapir/static/TapirStaticContentEndpoints.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.static 2 | 3 | trait TapirStaticContentEndpoints 4 | -------------------------------------------------------------------------------- /core/src/test/scala-2/sttp/tapir/SchemaMacroTestData2.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | object SchemaMacroTestData2 { 4 | object ValueClasses { 5 | case class UserName(name: String) extends AnyVal 6 | case class DoubleValue(value: Double) extends AnyVal 7 | case class UserNameRequest(name: UserName) 8 | 9 | case class UserList(list: List[UserName]) extends AnyVal 10 | case class UserListRequest(list: UserList) 11 | } 12 | 13 | sealed trait Type 14 | object Type { 15 | final case class Num[N <: AnyVal: Numeric](n: N) extends Type 16 | final case class MapType(obj: Map[String, Type]) extends Type 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/src/test/scala/sttp/tapir/SchemaAnnotationsTestData.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | import sttp.tapir.Schema.annotations.{default, description, encodedExample, encodedName, format, hidden, validate} 4 | 5 | object SchemaAnnotationsTestData { 6 | @description("my-string") 7 | @encodedExample("encoded-example") 8 | @default(MyString("default"), encoded = Some("encoded-default")) 9 | @format("utf8") 10 | @Schema.annotations.deprecated 11 | @encodedName("encoded-name") 12 | @validate(Validator.pass[MyString]) 13 | @hidden 14 | case class MyString(value: String) 15 | } 16 | -------------------------------------------------------------------------------- /core/src/test/scala/sttp/tapir/SchemaApplyValidationTestData.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | //Moved to separate file because dotty issue - https://github.com/lampepfl/dotty/issues/12498 4 | object SchemaApplyValidationTestData { 5 | final case class RecursiveName(name: String, subNames: Option[Vector[RecursiveName]]) 6 | 7 | sealed trait Animal 8 | sealed trait Pet extends Animal {} 9 | case class Dog(name: String, friends: List[Pet]) extends Pet 10 | case class Cat(name: String, friends: List[Pet]) extends Pet 11 | 12 | case class SimpleDog(name: String) 13 | } 14 | -------------------------------------------------------------------------------- /core/src/test/scala/sttp/tapir/internal/RichEndpointOutputTest.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.internal 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | import sttp.model.MediaType 6 | import sttp.tapir._ 7 | 8 | class RichEndpointOutputTest extends AnyFlatSpec with Matchers { 9 | "output media type" should "match content type with lower and upper case charset" in { 10 | val o = endpoint.put 11 | .in("api" / path[String]("version")) 12 | .out(stringBody) 13 | .output 14 | 15 | o.hasOptionalBodyMatchingContent(MediaType.unsafeParse("text/plain; charset=utf-8")) should be(true) 16 | o.hasOptionalBodyMatchingContent(MediaType.unsafeParse("text/plain; charset=UTF-8")) should be(true) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/src/test/scalajs/sttp/tapir/EndpointTestExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | trait EndpointTestExtensions 4 | -------------------------------------------------------------------------------- /core/src/test/scalajs/sttp/tapir/generic/FormCodecDerivationTestExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.generic 2 | 3 | trait FormCodecDerivationTestExtensions 4 | -------------------------------------------------------------------------------- /core/src/test/scalajs/sttp/tapir/generic/MultipartCodecDerivationTestExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.generic 2 | 3 | import scala.scalajs.js 4 | import scala.scalajs.js.JSConverters._ 5 | import scala.scalajs.js.typedarray.AB2TA 6 | 7 | import org.scalajs.dom.BlobPart 8 | import org.scalajs.dom.File 9 | 10 | trait MultipartCodecDerivationTestExtensions { 11 | 12 | // Mimic java.io.File operations used in tests so we can keep the tests simpler. 13 | implicit class FileOps(file: File) { 14 | def getName: String = file.name 15 | def delete(): Unit = {} 16 | } 17 | 18 | def createTempFile() = new File( 19 | Iterable(Array.empty[Byte].toTypedArray.asInstanceOf[BlobPart]).toJSIterable, 20 | "temp.txt" 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /core/src/test/scalajs/sttp/tapir/typelevel/MatchTypeTestExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.typelevel 2 | 3 | trait MatchTypeTestExtensions { 4 | // In JS land, double is the only primitive numeric type so we just ignore the other numeric types here. 5 | val matcherAndTypes: Seq[(MatchType[_], Any)] = Seq( 6 | implicitly[MatchType[String]] -> "string", 7 | implicitly[MatchType[Boolean]] -> true, 8 | implicitly[MatchType[Char]] -> 'c', 9 | implicitly[MatchType[Double]] -> 42.2d 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /core/src/test/scalajvm/sttp/tapir/generic/FormCodecDerivationTestExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.generic 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | trait FormCodecDerivationTestExtensions extends AnyFlatSpec with Matchers { 7 | it should "report a user-friendly error when a codec for a parameter cannot be found" in { 8 | assertTypeError(""" 9 | |import sttp.tapir._ 10 | |trait NoCodecForThisTrait 11 | |case class Test5(f1: String, f2: NoCodecForThisTrait) 12 | |implicitly[Codec[String, Test5, CodecFormat.XWwwFormUrlencoded]] 13 | |""".stripMargin) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/src/test/scalajvm/sttp/tapir/generic/MultipartCodecDerivationTestExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.generic 2 | 3 | import java.io.File 4 | 5 | trait MultipartCodecDerivationTestExtensions { self: MultipartCodecDerivationTest => 6 | 7 | def createTempFile() = File.createTempFile("tapir", "test") 8 | 9 | it should "report a user-friendly error when a codec for a parameter cannot be found" in { 10 | assertTypeError(""" 11 | |import sttp.tapir._ 12 | |trait NoCodecForThisTrait 13 | |case class Test5(f1: String, f2: NoCodecForThisTrait) 14 | |implicitly[Codec[String, Test5, CodecFormat.XWwwFormUrlencoded]] 15 | |""".stripMargin) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/src/test/scalajvm/sttp/tapir/generic/SchemaGenericSemiautoTest.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.generic 2 | 3 | import org.scalatest.matchers.should.Matchers 4 | import org.scalatest.flatspec.AsyncFlatSpec 5 | 6 | class SchemaGenericSemiautoTest extends AsyncFlatSpec with Matchers { 7 | "Schema semiauto derivation" should "fail and hint if an implicit instance is missing" in { 8 | assertTypeError(""" 9 | |import sttp.tapir._ 10 | |case class Unknown(str: String) 11 | |case class Example(str: String, unknown: Unknown) 12 | |Schema.derived[Example] 13 | |""".stripMargin) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/src/test/scalajvm/sttp/tapir/internal/MimeByExtensionDBTest.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.internal 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | import sttp.model.MediaType 6 | 7 | class MimeByExtensionDBTest extends AnyFlatSpec with Matchers { 8 | it should "correctly find mime types for extensions" in { 9 | MimeByExtensionDB("txt") shouldBe Some(MediaType.TextPlain) 10 | MimeByExtensionDB("html") shouldBe Some(MediaType("text", "html")) 11 | MimeByExtensionDB("css") shouldBe Some(MediaType("text", "css", Some("UTF-8"))) 12 | MimeByExtensionDB("unknown") shouldBe None 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/test/scalajvm/sttp/tapir/typelevel/MatchTypeTestExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.typelevel 2 | 3 | trait MatchTypeTestExtensions { 4 | val t: Byte = 0xf.toByte 5 | val s: Short = 1 6 | 7 | val matcherAndTypes: Seq[(MatchType[_], Any)] = Seq( 8 | implicitly[MatchType[String]] -> "string", 9 | implicitly[MatchType[Boolean]] -> true, 10 | implicitly[MatchType[Char]] -> 'c', 11 | implicitly[MatchType[Byte]] -> t, 12 | implicitly[MatchType[Short]] -> s, 13 | implicitly[MatchType[Float]] -> 42.2f, 14 | implicitly[MatchType[Double]] -> 42.2d, 15 | implicitly[MatchType[Int]] -> 42 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /core/src/test/scalanative/sttp/tapir/EndpointTestExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | trait EndpointTestExtensions 4 | -------------------------------------------------------------------------------- /core/src/test/scalanative/sttp/tapir/generic/FormCodecDerivationTestExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.generic 2 | 3 | trait FormCodecDerivationTestExtensions 4 | -------------------------------------------------------------------------------- /core/src/test/scalanative/sttp/tapir/generic/MultipartCodecDerivationTestExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.generic 2 | 3 | import java.io.File 4 | 5 | trait MultipartCodecDerivationTestExtensions { 6 | def createTempFile() = File.createTempFile("tapir", "test") 7 | } 8 | -------------------------------------------------------------------------------- /core/src/test/scalanative/sttp/tapir/typelevel/MatchTypeTestExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.typelevel 2 | 3 | trait MatchTypeTestExtensions { 4 | val t: Byte = 0xf.toByte 5 | val s: Short = 1 6 | 7 | val matcherAndTypes: Seq[(MatchType[_], Any)] = Seq( 8 | implicitly[MatchType[String]] -> "string", 9 | implicitly[MatchType[Boolean]] -> true, 10 | implicitly[MatchType[Char]] -> 'c', 11 | implicitly[MatchType[Byte]] -> t, 12 | implicitly[MatchType[Short]] -> s, 13 | implicitly[MatchType[Float]] -> 42.2f, 14 | implicitly[MatchType[Double]] -> 42.2d, 15 | implicitly[MatchType[Int]] -> 42 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | _build_html -------------------------------------------------------------------------------- /doc/.python-version: -------------------------------------------------------------------------------- 1 | 3.12 2 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = tapir 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /doc/adopters/adobe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/adopters/adobe.png -------------------------------------------------------------------------------- /doc/adopters/broad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/adopters/broad.png -------------------------------------------------------------------------------- /doc/adopters/colisweb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/adopters/colisweb.png -------------------------------------------------------------------------------- /doc/adopters/ematiq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/adopters/ematiq.png -------------------------------------------------------------------------------- /doc/adopters/fugo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/adopters/fugo.png -------------------------------------------------------------------------------- /doc/adopters/hootsuite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/adopters/hootsuite.png -------------------------------------------------------------------------------- /doc/adopters/hunters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/adopters/hunters.png -------------------------------------------------------------------------------- /doc/adopters/iceo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/adopters/iceo.png -------------------------------------------------------------------------------- /doc/adopters/kaizo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/adopters/kaizo.png -------------------------------------------------------------------------------- /doc/adopters/kelkoogroup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/adopters/kelkoogroup.png -------------------------------------------------------------------------------- /doc/adopters/kensu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/adopters/kensu.png -------------------------------------------------------------------------------- /doc/adopters/moia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/adopters/moia.png -------------------------------------------------------------------------------- /doc/adopters/moneyfarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/adopters/moneyfarm.png -------------------------------------------------------------------------------- /doc/adopters/ocado.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/adopters/ocado.png -------------------------------------------------------------------------------- /doc/adopters/process_street.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/adopters/process_street.png -------------------------------------------------------------------------------- /doc/adopters/softwaremill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/adopters/softwaremill.png -------------------------------------------------------------------------------- /doc/adopters/swissborg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/adopters/swissborg.png -------------------------------------------------------------------------------- /doc/generate.md: -------------------------------------------------------------------------------- 1 | # Generate a Tapir project 2 | 3 | Not sure how to start? 4 | 5 | We recommend the defaults below (Direct-style stack & Netty server); this requires Java 21+. Otherwise, 6 | you might also try the Future stack + Netty, which works on Java 11+. 7 | 8 | If you'd like to include a JSON endpoint, using the [jsoniter](https://github.com/plokhotnyuk/jsoniter-scala) library 9 | might a good choice! 10 | 11 | ```{eval-rst} 12 | .. raw:: html 13 | 14 | 21 | ``` 22 | -------------------------------------------------------------------------------- /doc/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/doc/logo.png -------------------------------------------------------------------------------- /doc/other/adr.md: -------------------------------------------------------------------------------- 1 | # Architecture Decision Records 2 | 3 | ADRs covering some of Tapir's design decisions are available in the `adr` directory of the repository. They are [available here](https://github.com/softwaremill/tapir/tree/master/doc/adr). -------------------------------------------------------------------------------- /doc/other/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | ## StackOverflowException during compilation 4 | 5 | Sidenote for scala 2.12.4 and higher: if you encounter an issue with compiling your project because of 6 | a `StackOverflowException` related to [this](https://github.com/scala/bug/issues/10604) scala bug, 7 | please increase your stack memory. Example: 8 | 9 | ``` 10 | sbt -J-Xss4M clean compile 11 | ``` 12 | 13 | ## Logging of generated macros code 14 | For some cases, it may be helpful to examine how generated macros code looks like. 15 | To do that, just set an environmental variable and check compilation logs for details. 16 | 17 | ``` 18 | export TAPIR_LOG_GENERATED_CODE=true 19 | ``` 20 | -------------------------------------------------------------------------------- /doc/quickstart.md: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | 3 | To use tapir, add the following dependency to your project: 4 | 5 | ```scala 6 | "com.softwaremill.sttp.tapir" %% "tapir-core" % "@VERSION@" 7 | ``` 8 | 9 | This will import only the core classes needed to create endpoint descriptions. To generate a server or a client, you 10 | will need to add further dependencies. 11 | 12 | Many of tapir functionalities come as builder methods in the main package, hence it's easiest to work with tapir if 13 | you import the main package entirely, i.e.: 14 | 15 | ```scala 16 | import sttp.tapir.* 17 | ``` 18 | 19 | Finally, type: 20 | 21 | ```scala 22 | endpoint. 23 | ``` 24 | 25 | and see where auto-complete gets you! 26 | 27 | -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx_rtd_theme==2.0.0 2 | sphinx==7.3.7 3 | sphinx-autobuild==2024.4.16 4 | myst-parser==2.0.0 5 | -------------------------------------------------------------------------------- /doc/watch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sphinx-autobuild . _build/html 3 | -------------------------------------------------------------------------------- /docs/apispec-docs/src/main/scala/sttp/tapir/docs/apispec/DocsExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.docs.apispec 2 | 3 | import sttp.apispec.ExtensionValue 4 | import sttp.tapir.internal.IterableToListMap 5 | 6 | import scala.collection.immutable.ListMap 7 | 8 | private[docs] object DocsExtensions { 9 | def fromIterable(docsExtensions: Iterable[DocsExtension[_]]): ListMap[String, ExtensionValue] = 10 | docsExtensions.map(e => (e.key, ExtensionValue(e.rawValue))).toListMap 11 | } 12 | -------------------------------------------------------------------------------- /docs/apispec-docs/src/main/scala/sttp/tapir/docs/apispec/schema/MetaSchema.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.docs.apispec.schema 2 | 3 | sealed trait MetaSchema { 4 | def schemaId: String 5 | } 6 | 7 | case object MetaSchemaDraft04 extends MetaSchema { 8 | override lazy val schemaId: String = "http://json-schema.org/draft-04/schema#" 9 | } 10 | 11 | case object MetaSchemaDraft202012 extends MetaSchema { 12 | override lazy val schemaId: String = "http://json-schema.org/draft/2020-12/schema#" 13 | } 14 | -------------------------------------------------------------------------------- /docs/asyncapi-docs/src/main/scala/sttp/tapir/docs/asyncapi/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.docs 2 | 3 | package object asyncapi { 4 | private[docs] type MessageKey = String 5 | } 6 | -------------------------------------------------------------------------------- /docs/asyncapi-docs/src/test/resources/expected_string.yml: -------------------------------------------------------------------------------- 1 | asyncapi: 2.6.0 2 | info: 3 | title: The fruit basket 4 | version: '0.1' 5 | channels: 6 | /fruit: 7 | subscribe: 8 | operationId: onFruit 9 | message: 10 | $ref: '#/components/messages/string' 11 | publish: 12 | operationId: sendFruit 13 | message: 14 | $ref: '#/components/messages/string' 15 | bindings: 16 | ws: 17 | method: GET 18 | components: 19 | messages: 20 | string: 21 | payload: 22 | type: string 23 | contentType: text/plain 24 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/coproduct/expected_flat_either.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: title 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | parameters: 10 | - name: q 11 | in: query 12 | required: true 13 | schema: 14 | oneOf: 15 | - type: integer 16 | format: int32 17 | - type: string 18 | responses: 19 | '200': 20 | description: '' 21 | '400': 22 | description: 'Invalid value for: query parameter q' 23 | content: 24 | text/plain: 25 | schema: 26 | type: string -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/decode_failure_output/expected_default_400_response.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | parameters: 10 | - name: amount 11 | in: query 12 | required: true 13 | schema: 14 | type: integer 15 | format: int32 16 | minimum: 0 17 | responses: 18 | '200': 19 | description: '' 20 | '400': 21 | description: 'Invalid value for: query parameter amount' 22 | content: 23 | text/plain: 24 | schema: 25 | type: string 26 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/decode_failure_output/expected_no_400_response.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | parameters: 10 | - name: amount 11 | in: query 12 | required: true 13 | schema: 14 | type: integer 15 | format: int32 16 | minimum: 0 17 | responses: 18 | '200': 19 | description: '' 20 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/decode_failure_output/expected_response_defined_in_options.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | parameters: 10 | - name: amount 11 | in: query 12 | required: true 13 | schema: 14 | type: integer 15 | format: int32 16 | minimum: 0 17 | responses: 18 | '200': 19 | description: '' 20 | '400': 21 | description: Description defined in options. 22 | content: 23 | text/plain: 24 | schema: 25 | type: string 26 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/decode_failure_output/expected_user_defined_response.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | parameters: 10 | - name: amount 11 | in: query 12 | required: true 13 | schema: 14 | type: integer 15 | format: int32 16 | minimum: 0 17 | responses: 18 | '200': 19 | description: '' 20 | '400': 21 | description: User-defined description. 22 | content: 23 | text/plain: 24 | schema: 25 | type: string 26 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/example/expected_byte_buffer_example.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Users 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | responses: 10 | '200': 11 | description: '' 12 | headers: 13 | Content-Type: 14 | required: true 15 | schema: 16 | type: string 17 | content: 18 | text/csv: 19 | schema: 20 | type: string 21 | format: binary 22 | example: a,b,c,1024,e,f,42,g h,i -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/example/expected_fixed_header_example.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Examples 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | parameters: 10 | - name: Content-Type 11 | in: header 12 | required: true 13 | schema: 14 | type: string 15 | example: application/json 16 | responses: 17 | '200': 18 | description: '' 19 | '400': 20 | description: 'Invalid value for: header Content-Type: application/json' 21 | content: 22 | text/plain: 23 | schema: 24 | type: string 25 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_arbitrary_json_output.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | responses: 10 | '200': 11 | description: '' 12 | content: 13 | application/json: 14 | schema: {} -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_date_time.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Examples 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | parameters: 10 | - name: instant 11 | in: query 12 | required: true 13 | schema: 14 | type: string 15 | format: date-time 16 | responses: 17 | '200': 18 | description: '' 19 | '400': 20 | description: 'Invalid value for: query parameter instant' 21 | content: 22 | text/plain: 23 | schema: 24 | type: string -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_default_query_param.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | post: 8 | operationId: postRoot 9 | parameters: 10 | - name: name 11 | in: query 12 | required: false 13 | schema: 14 | default: tom 15 | type: string 16 | example: alan 17 | responses: 18 | '200': 19 | description: '' 20 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_deprecated.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /api: 7 | get: 8 | operationId: getApi 9 | responses: 10 | '200': 11 | description: '' 12 | deprecated: true -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_double_quoted.yml: -------------------------------------------------------------------------------- 1 | openapi: "3.1.0" 2 | info: 3 | title: "String style" 4 | version: "1.0" 5 | paths: 6 | /: 7 | get: 8 | description: "first line:\nsecond line" 9 | operationId: "getRoot" 10 | responses: 11 | '200': 12 | description: "" 13 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_empty.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Fruits 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | responses: 10 | '200': 11 | description: '' -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_exclusive_bounds.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Examples 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | parameters: 10 | - name: num 11 | in: query 12 | required: true 13 | schema: 14 | type: integer 15 | format: int32 16 | exclusiveMinimum: 0 17 | exclusiveMaximum: 42 18 | responses: 19 | '200': 20 | description: '' 21 | '400': 22 | description: 'Invalid value for: query parameter num' 23 | content: 24 | text/plain: 25 | schema: 26 | type: string 27 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_explicit_content_type_header.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: title 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | responses: 10 | '200': 11 | description: '' 12 | headers: 13 | Content-Type: 14 | required: true 15 | schema: 16 | type: string 17 | content: 18 | text/csv: 19 | schema: 20 | type: string -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_extensions_security_scheme.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: title 4 | version: '1.0' 5 | paths: 6 | /: 7 | post: 8 | operationId: postRoot 9 | responses: 10 | '200': 11 | description: '' 12 | security: 13 | - apiKeyAuth: [] 14 | components: 15 | securitySchemes: 16 | apiKeyAuth: 17 | type: apiKey 18 | name: Authorization 19 | in: header 20 | x-schema: 21 | string: a 22 | int: 1 -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_external.yml: -------------------------------------------------------------------------------- 1 | openapi: '3.1.0' 2 | info: 3 | title: 'Fruits' 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: 'getRoot' 9 | responses: 10 | '200': 11 | description: '' 12 | default: 13 | description: '' 14 | content: 15 | application/json: 16 | schema: 17 | $ref: 'https://example.com/models/model.yaml#/Problem' 18 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_fixed_header_input_request.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | parameters: 10 | - name: Location 11 | in: header 12 | required: true 13 | schema: 14 | type: string 15 | example: Poland 16 | responses: 17 | '200': 18 | description: '' 19 | '400': 20 | description: 'Invalid value for: header Location: Poland' 21 | content: 22 | text/plain: 23 | schema: 24 | type: string 25 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_fixed_header_output_response.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | responses: 10 | '200': 11 | description: '' 12 | headers: 13 | Location: 14 | required: true 15 | schema: 16 | type: string 17 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_fixed_status_code.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | responses: 10 | '308': 11 | description: '' 12 | headers: 13 | Location: 14 | required: true 15 | schema: 16 | type: string -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_fixed_status_code_2.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | responses: 10 | '204': 11 | description: '' 12 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_flag_query.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Flags 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | parameters: 10 | - name: flag 11 | in: query 12 | required: false 13 | allowEmptyValue: true 14 | schema: 15 | type: boolean 16 | responses: 17 | '200': 18 | description: '' 19 | content: 20 | text/plain: 21 | schema: 22 | type: string 23 | '400': 24 | description: 'Invalid value for: query parameter flag' 25 | content: 26 | text/plain: 27 | schema: 28 | type: string 29 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_json_query_param.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | post: 8 | operationId: postRoot 9 | parameters: 10 | - name: name 11 | in: query 12 | required: false 13 | content: 14 | application/json: 15 | schema: 16 | default: tom 17 | type: string 18 | example: alan 19 | responses: 20 | '200': 21 | description: '' 22 | '400': 23 | description: 'Invalid value for: query parameter name' 24 | content: 25 | text/plain: 26 | schema: 27 | type: string 28 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_literal.yml: -------------------------------------------------------------------------------- 1 | openapi: |- 2 | 3.1.0 3 | info: 4 | title: |- 5 | String style 6 | version: |- 7 | 1.0 8 | paths: 9 | /: 10 | get: 11 | description: |- 12 | first line: 13 | second line 14 | operationId: |- 15 | getRoot 16 | responses: 17 | '200': 18 | description: "" 19 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_localDateTime.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Examples 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | parameters: 10 | - name: localDateTime 11 | in: query 12 | required: true 13 | schema: 14 | type: string 15 | responses: 16 | '200': 17 | description: '' 18 | '400': 19 | description: 'Invalid value for: query parameter localDateTime' 20 | content: 21 | text/plain: 22 | schema: 23 | type: string 24 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_map_with_plain_values.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | responses: 10 | '200': 11 | description: '' 12 | content: 13 | application/json: 14 | schema: 15 | $ref: '#/components/schemas/Map_String' 16 | components: 17 | schemas: 18 | Map_String: 19 | title: Map_String 20 | type: object 21 | additionalProperties: 22 | type: string 23 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_multi_empty_outputs.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | responses: 10 | '200': 11 | description: success 12 | '202': 13 | description: maybe success -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_multiple.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Fruits 4 | version: '1.0' 5 | paths: 6 | /p1: 7 | get: 8 | operationId: getP1 9 | responses: 10 | '200': 11 | description: '' 12 | /p3: 13 | get: 14 | operationId: getP3 15 | responses: 16 | '200': 17 | description: '' 18 | /p2: 19 | get: 20 | operationId: getP2 21 | responses: 22 | '200': 23 | description: '' 24 | /p5: 25 | get: 26 | operationId: getP5 27 | responses: 28 | '200': 29 | description: '' 30 | /p4: 31 | get: 32 | operationId: getP4 33 | responses: 34 | '200': 35 | description: '' -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_optional_header.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | post: 8 | operationId: postRoot 9 | parameters: 10 | - name: X-test 11 | in: header 12 | required: false 13 | schema: 14 | type: string 15 | responses: 16 | '200': 17 | description: '' -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_recursive.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Fruits 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | responses: 10 | '200': 11 | description: '' 12 | content: 13 | application/json: 14 | schema: 15 | $ref: '#/components/schemas/F1' 16 | components: 17 | schemas: 18 | F1: 19 | title: F1 20 | type: object 21 | properties: 22 | data: 23 | type: array 24 | items: 25 | $ref: '#/components/schemas/F1' 26 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_streaming.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Fruits 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | requestBody: 10 | content: 11 | text/plain: 12 | schema: 13 | type: string 14 | required: true 15 | responses: 16 | '200': 17 | description: '' 18 | content: 19 | application/octet-stream: 20 | schema: 21 | type: string 22 | format: binary 23 | '400': 24 | description: 'Invalid value for: body' 25 | content: 26 | text/plain: 27 | schema: 28 | type: string 29 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_string_body_response.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /double: 7 | post: 8 | operationId: postDouble 9 | requestBody: 10 | content: 11 | text/plain: 12 | schema: 13 | type: string 14 | required: true 15 | responses: 16 | '200': 17 | description: '' -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/expected_unique_open_api_parameters.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Unique parameters 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | parameters: 10 | - name: correlation-id 11 | in: header 12 | required: true 13 | schema: 14 | type: string 15 | responses: 16 | '200': 17 | description: '' 18 | '400': 19 | description: 'Invalid value for: header correlation-id' 20 | content: 21 | text/plain: 22 | schema: 23 | type: string -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/oneOf/expected_one_of_status_code_range.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | responses: 10 | '200': 11 | description: '' 12 | headers: 13 | Location: 14 | required: true 15 | schema: 16 | type: string 17 | 4XX: 18 | description: bad input 19 | '404': 20 | description: not here -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/oneOf/expected_one_of_status_codes.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | responses: 10 | '200': 11 | description: '' 12 | headers: 13 | Location: 14 | required: true 15 | schema: 16 | type: string 17 | '400': 18 | description: '' 19 | '404': 20 | description: entity not found -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/oneOf/expected_status_codes_with_empty_output.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: test 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | responses: 10 | '200': 11 | description: '' 12 | '403': 13 | description: forbidden -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/security/expected_auth_with_bearer_format.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Fruits 4 | version: '1.0' 5 | paths: 6 | /secure/bearer: 7 | get: 8 | operationId: getSecureBearer 9 | responses: 10 | '200': 11 | description: '' 12 | content: 13 | text/plain: 14 | schema: 15 | type: string 16 | security: 17 | - httpAuth: [] 18 | components: 19 | securitySchemes: 20 | httpAuth: 21 | type: http 22 | scheme: bearer 23 | bearerFormat: JWT -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/security/expected_auth_with_description.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Fruits 4 | version: '1.0' 5 | paths: 6 | /secure/bearer: 7 | get: 8 | operationId: getSecureBearer 9 | responses: 10 | '200': 11 | description: '' 12 | content: 13 | text/plain: 14 | schema: 15 | type: string 16 | security: 17 | - httpAuth: [] 18 | components: 19 | securitySchemes: 20 | httpAuth: 21 | type: http 22 | description: put your token here 23 | scheme: bearer -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/security/expected_multiple_optional_auth.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Fruits 4 | version: '1.0' 5 | paths: 6 | /api1: 7 | get: 8 | operationId: getApi1 9 | responses: 10 | '200': 11 | description: '' 12 | security: 13 | - sec1: [] 14 | - sec2: [] 15 | /api2: 16 | get: 17 | operationId: getApi2 18 | responses: 19 | '200': 20 | description: '' 21 | security: 22 | - {} 23 | - sec1: [] 24 | - sec2: [] 25 | components: 26 | securitySchemes: 27 | sec1: 28 | type: apiKey 29 | name: apikey1 30 | in: header 31 | sec2: 32 | type: apiKey 33 | name: apikey2 34 | in: header 35 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/validator/expected_valid_enum_values.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | parameters: 10 | - name: amount 11 | in: query 12 | required: true 13 | schema: 14 | type: integer 15 | enum: 16 | - 1 17 | - 2 18 | format: int32 19 | responses: 20 | '200': 21 | description: '' 22 | '400': 23 | description: 'Invalid value for: query parameter amount' 24 | content: 25 | text/plain: 26 | schema: 27 | type: string 28 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/validator/expected_valid_modified_array_strings.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Entities 4 | version: '1.0' 5 | paths: 6 | /: 7 | get: 8 | operationId: getRoot 9 | responses: 10 | '200': 11 | description: '' 12 | content: 13 | application/json: 14 | schema: 15 | $ref: '#/components/schemas/ObjectWithStrings' 16 | components: 17 | schemas: 18 | ObjectWithStrings: 19 | title: ObjectWithStrings 20 | type: object 21 | properties: 22 | data: 23 | type: array 24 | maxItems: 1 25 | items: 26 | type: string 27 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/validator/expected_valid_query_tagged.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Fruits 4 | version: '1.0' 5 | paths: 6 | /add/path: 7 | get: 8 | operationId: getAddPath 9 | parameters: 10 | - name: fruit 11 | in: query 12 | required: true 13 | schema: 14 | type: string 15 | pattern: apple|banana 16 | responses: 17 | '200': 18 | description: '' 19 | '400': 20 | description: 'Invalid value for: query parameter fruit' 21 | content: 22 | text/plain: 23 | schema: 24 | type: string 25 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/validator/expected_valid_query_wrapped.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Fruits 4 | version: '1.0' 5 | paths: 6 | /add/path: 7 | get: 8 | operationId: getAddPath 9 | parameters: 10 | - name: amount 11 | in: query 12 | required: true 13 | schema: 14 | type: integer 15 | format: int32 16 | minimum: 1 17 | responses: 18 | '200': 19 | description: '' 20 | '400': 21 | description: 'Invalid value for: query parameter amount' 22 | content: 23 | text/plain: 24 | schema: 25 | type: string 26 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/resources/validator/expected_whole_numbers_in_examples_of_string_schemas.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: Fruits 4 | version: '1.0' 5 | paths: 6 | /positive: 7 | get: 8 | operationId: getPositive 9 | parameters: 10 | - name: x 11 | in: query 12 | required: true 13 | schema: 14 | type: integer 15 | minimum: 0 16 | responses: 17 | '200': 18 | description: '' 19 | '400': 20 | description: 'Invalid value for: query parameter x' 21 | content: 22 | text/plain: 23 | schema: 24 | type: string -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/scalajvm-2/sttp/tapir/docs/openapi/dtos/VerifyYamlCoproductTestData2.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.docs.openapi.dtos 2 | 3 | object VerifyYamlCoproductTestData2 { 4 | sealed trait Clause 5 | case class Expression(v: String) extends Clause 6 | case class Not(not: Clause) extends Clause 7 | } 8 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/scalajvm-2/sttp/tapir/docs/openapi/dtos/VerifyYamlTestData2.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.docs.openapi.dtos 2 | 3 | object VerifyYamlTestData2 { 4 | case class F1(data: List[F1]) 5 | } 6 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/scalajvm-3/sttp/tapir/docs/openapi/dtos/VerifyYamlTestData2.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.docs.openapi.dtos 2 | 3 | import io.circe.generic.semiauto._ 4 | import io.circe.{Decoder, Encoder} 5 | import sttp.tapir.Schema 6 | 7 | // TODO: move back to VerifyYamlTest companion after https://github.com/lampepfl/dotty/issues/12849 is fixed 8 | // TODO: remove explicit encoders/decoders/schemas once recursive auto-derivation is supported in magnolia 9 | object VerifyYamlTestData2 { 10 | case class F1(data: List[F1]) 11 | object F1 { 12 | implicit def f1Schema: Schema[F1] = Schema.derived[F1] 13 | implicit def f1Encoder: Encoder[F1] = deriveEncoder[F1] 14 | implicit def f1Decoder: Decoder[F1] = deriveDecoder[F1] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/scalajvm/sttp/tapir/docs/openapi/dtos/Book.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.docs.openapi.dtos 2 | 3 | case class Country(name: String) 4 | case class Author(name: String, country: Country) 5 | case class Genre(name: String, description: String) 6 | case class Book(title: String, genre: Genre, year: Int, author: Author) 7 | case class BooksQuery(genre: Option[String], limit: Option[Int]) 8 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/scalajvm/sttp/tapir/docs/openapi/dtos/VerifyYamlTestData.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.docs.openapi.dtos 2 | 3 | import sttp.tapir.Schema.annotations.default 4 | import sttp.tapir.tests.data.FruitAmount 5 | 6 | // TODO: move back to VerifyYamlTest companion after https://github.com/lampepfl/dotty/issues/12849 is fixed 7 | object VerifyYamlTestData { 8 | case class G[T](data: T) 9 | case class ObjectWrapper(value: FruitAmount) 10 | case class ObjectWithList(data: List[FruitAmount]) 11 | case class ObjectWithSet(data: Set[FruitAmount]) 12 | case class ObjectWithOption(data: Option[FruitAmount]) 13 | case class ObjectWithDefaults(@default("foo") name: String, @default(12) count: Int) 14 | } 15 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/scalajvm/sttp/tapir/docs/openapi/dtos/VerifyYamlValidatorTestData.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.docs.openapi.dtos 2 | 3 | import sttp.tapir.tests.data.FruitAmount 4 | 5 | // TODO: move back to VerifyYamlTest companion after https://github.com/lampepfl/dotty/issues/12849 is fixed 6 | object VerifyYamlValidatorTestData { 7 | case class ObjectWithList(data: List[FruitAmount]) 8 | case class ObjectWithStrings(data: List[String]) 9 | case class MyClass(myAttribute: Int) 10 | } 11 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/scalajvm/sttp/tapir/docs/openapi/dtos/a/Pet.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.docs.openapi.dtos.a 2 | 3 | case class Pet(name: String) 4 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/scalajvm/sttp/tapir/docs/openapi/dtos/b/Pet.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.docs.openapi.dtos.b 2 | 3 | case class Pet(legsCount: Int) 4 | -------------------------------------------------------------------------------- /docs/openapi-docs/src/test/scalajvm/sttp/tapir/docs/openapi/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.docs 2 | 3 | import scala.io.Source 4 | 5 | package object openapi { 6 | private[openapi] def load(fileName: String): String = { 7 | noIndentation( 8 | Source.fromInputStream(classOf[sttp.tapir.docs.openapi.VerifyYamlTest].getResourceAsStream(s"/$fileName")).getLines().mkString("\n") 9 | ) 10 | } 11 | private[openapi] def noIndentation(s: String): String = s.replaceAll("[ \t]", "").trim 12 | } 13 | -------------------------------------------------------------------------------- /examples/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %date [%thread] %-5level %logger{36} - %msg%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/src/main/resources/webapp/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/examples/src/main/resources/webapp/img/logo.png -------------------------------------------------------------------------------- /examples/src/main/resources/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 |

Path requested: .

12 | 13 | -------------------------------------------------------------------------------- /files/src/main/scalajs/sttp/tapir/files/TapirStaticContentEndpoints.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.files 2 | 3 | trait TapirStaticContentEndpoints 4 | -------------------------------------------------------------------------------- /files/src/main/scalanative/sttp/tapir/files/TapirStaticContentEndpoints.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.files 2 | 3 | trait TapirStaticContentEndpoints 4 | -------------------------------------------------------------------------------- /generated-doc/out/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | _build_html -------------------------------------------------------------------------------- /generated-doc/out/.python-version: -------------------------------------------------------------------------------- 1 | 3.12 2 | -------------------------------------------------------------------------------- /generated-doc/out/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = tapir 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /generated-doc/out/adopters/adobe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/adopters/adobe.png -------------------------------------------------------------------------------- /generated-doc/out/adopters/broad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/adopters/broad.png -------------------------------------------------------------------------------- /generated-doc/out/adopters/colisweb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/adopters/colisweb.png -------------------------------------------------------------------------------- /generated-doc/out/adopters/ematiq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/adopters/ematiq.png -------------------------------------------------------------------------------- /generated-doc/out/adopters/fugo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/adopters/fugo.png -------------------------------------------------------------------------------- /generated-doc/out/adopters/hootsuite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/adopters/hootsuite.png -------------------------------------------------------------------------------- /generated-doc/out/adopters/hunters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/adopters/hunters.png -------------------------------------------------------------------------------- /generated-doc/out/adopters/iceo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/adopters/iceo.png -------------------------------------------------------------------------------- /generated-doc/out/adopters/kaizo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/adopters/kaizo.png -------------------------------------------------------------------------------- /generated-doc/out/adopters/kelkoogroup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/adopters/kelkoogroup.png -------------------------------------------------------------------------------- /generated-doc/out/adopters/kensu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/adopters/kensu.png -------------------------------------------------------------------------------- /generated-doc/out/adopters/moia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/adopters/moia.png -------------------------------------------------------------------------------- /generated-doc/out/adopters/moneyfarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/adopters/moneyfarm.png -------------------------------------------------------------------------------- /generated-doc/out/adopters/ocado.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/adopters/ocado.png -------------------------------------------------------------------------------- /generated-doc/out/adopters/process_street.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/adopters/process_street.png -------------------------------------------------------------------------------- /generated-doc/out/adopters/softwaremill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/adopters/softwaremill.png -------------------------------------------------------------------------------- /generated-doc/out/adopters/swissborg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/adopters/swissborg.png -------------------------------------------------------------------------------- /generated-doc/out/generate.md: -------------------------------------------------------------------------------- 1 | # Generate a Tapir project 2 | 3 | Not sure how to start? 4 | 5 | We recommend the defaults below (Direct-style stack & Netty server); this requires Java 21+. Otherwise, 6 | you might also try the Future stack + Netty, which works on Java 11+. 7 | 8 | If you'd like to include a JSON endpoint, using the [jsoniter](https://github.com/plokhotnyuk/jsoniter-scala) library 9 | might a good choice! 10 | 11 | ```{eval-rst} 12 | .. raw:: html 13 | 14 | 21 | ``` 22 | -------------------------------------------------------------------------------- /generated-doc/out/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/generated-doc/out/logo.png -------------------------------------------------------------------------------- /generated-doc/out/other/adr.md: -------------------------------------------------------------------------------- 1 | # Architecture Decision Records 2 | 3 | ADRs covering some of Tapir's design decisions are available in the `adr` directory of the repository. They are [available here](https://github.com/softwaremill/tapir/tree/master/doc/adr). -------------------------------------------------------------------------------- /generated-doc/out/other/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | ## StackOverflowException during compilation 4 | 5 | Sidenote for scala 2.12.4 and higher: if you encounter an issue with compiling your project because of 6 | a `StackOverflowException` related to [this](https://github.com/scala/bug/issues/10604) scala bug, 7 | please increase your stack memory. Example: 8 | 9 | ``` 10 | sbt -J-Xss4M clean compile 11 | ``` 12 | 13 | ## Logging of generated macros code 14 | For some cases, it may be helpful to examine how generated macros code looks like. 15 | To do that, just set an environmental variable and check compilation logs for details. 16 | 17 | ``` 18 | export TAPIR_LOG_GENERATED_CODE=true 19 | ``` 20 | -------------------------------------------------------------------------------- /generated-doc/out/quickstart.md: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | 3 | To use tapir, add the following dependency to your project: 4 | 5 | ```scala 6 | "com.softwaremill.sttp.tapir" %% "tapir-core" % "1.11.33" 7 | ``` 8 | 9 | This will import only the core classes needed to create endpoint descriptions. To generate a server or a client, you 10 | will need to add further dependencies. 11 | 12 | Many of tapir functionalities come as builder methods in the main package, hence it's easiest to work with tapir if 13 | you import the main package entirely, i.e.: 14 | 15 | ```scala 16 | import sttp.tapir.* 17 | ``` 18 | 19 | Finally, type: 20 | 21 | ```scala 22 | endpoint. 23 | ``` 24 | 25 | and see where auto-complete gets you! 26 | 27 | -------------------------------------------------------------------------------- /generated-doc/out/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx_rtd_theme==2.0.0 2 | sphinx==7.3.7 3 | sphinx-autobuild==2024.4.16 4 | myst-parser==2.0.0 5 | -------------------------------------------------------------------------------- /generated-doc/out/watch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sphinx-autobuild . _build/html 3 | -------------------------------------------------------------------------------- /grpc/examples/src/main/protobuf/simple_books_example.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option java_multiple_files = true; 4 | option java_package = "sttp.tapir.grpc.examples.grpc_simple_books_example.gen"; 5 | 6 | service Library { 7 | rpc AddBook (AddBookMsg) returns (SimpleBook) {} 8 | } 9 | 10 | message SimpleBook { 11 | int32 id = 1; 12 | string title = 2; 13 | string description = 3; 14 | } 15 | 16 | message AddBookMsg { 17 | string title = 1; 18 | string description = 2; 19 | } -------------------------------------------------------------------------------- /grpc/pbdirect/src/main/scala/sttp/tapir/grpc/protobuf/pbdirect/pbdirect.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.grpc.protobuf 2 | 3 | package object pbdirect extends TapirProtobufPbDirect 4 | -------------------------------------------------------------------------------- /grpc/pekko-examples/src/main/protobuf/simple_books_example.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option java_multiple_files = true; 4 | option java_package = "sttp.tapir.grpc.examples.grpc_simple_books_example.gen"; 5 | 6 | service Library { 7 | rpc AddBook (AddBookMsg) returns (SimpleBook) {} 8 | } 9 | 10 | message SimpleBook { 11 | int32 id = 1; 12 | string title = 2; 13 | string description = 3; 14 | } 15 | 16 | message AddBookMsg { 17 | string title = 1; 18 | string description = 2; 19 | } -------------------------------------------------------------------------------- /grpc/protobuf/src/main/scala/sttp/tapir/grpc/protobuf/ProtobufAttributes.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.grpc.protobuf 2 | 3 | import sttp.tapir.AttributeKey 4 | 5 | object ProtobufAttributes { 6 | val ScalarValueAttribute = new AttributeKey[ProtobufScalarType]("scalar-type") 7 | } 8 | -------------------------------------------------------------------------------- /grpc/protobuf/src/main/scala/sttp/tapir/grpc/protobuf/model/Protobuf.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.grpc.protobuf.model 2 | 3 | final case class Protobuf(messages: List[ProtobufMessage], services: List[ProtobufService], options: ProtobufOptions) 4 | -------------------------------------------------------------------------------- /grpc/protobuf/src/main/scala/sttp/tapir/grpc/protobuf/model/ProtobufMessage.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.grpc.protobuf.model 2 | 3 | import sttp.tapir.grpc.protobuf.ProtobufMessageRef 4 | 5 | sealed trait ProtobufMessage { 6 | def name: MessageName 7 | } 8 | 9 | case class ProtobufProductMessage(name: MessageName, fields: Iterable[ProtobufMessageField]) extends ProtobufMessage 10 | case class ProtobufCoproductMessage(name: MessageName, alternatives: Iterable[ProtobufMessageField]) extends ProtobufMessage 11 | -------------------------------------------------------------------------------- /grpc/protobuf/src/main/scala/sttp/tapir/grpc/protobuf/model/ProtobufMessageField.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.grpc.protobuf.model 2 | 3 | import sttp.tapir.grpc.protobuf.ProtobufType 4 | 5 | //Simplified model for PoC, should be used only for scalar values 6 | case class ProtobufMessageField( 7 | `type`: ProtobufType, 8 | name: String, 9 | maybeId: Option[Int] 10 | ) 11 | -------------------------------------------------------------------------------- /grpc/protobuf/src/main/scala/sttp/tapir/grpc/protobuf/model/ProtobufOptions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.grpc.protobuf.model 2 | 3 | case class ProtobufOptions(maybePackageName: Option[PackageName]) 4 | 5 | object ProtobufOptions { 6 | val empty: ProtobufOptions = ProtobufOptions(None) 7 | } 8 | -------------------------------------------------------------------------------- /grpc/protobuf/src/main/scala/sttp/tapir/grpc/protobuf/model/ProtobufService.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.grpc.protobuf.model 2 | 3 | case class ProtobufService(name: ServiceName, methods: List[ProtobufServiceMethod]) 4 | -------------------------------------------------------------------------------- /grpc/protobuf/src/main/scala/sttp/tapir/grpc/protobuf/model/ProtobufServiceMethod.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.grpc.protobuf.model 2 | 3 | case class ProtobufServiceMethod(name: MethodName, input: MessageReference, output: MessageReference) 4 | -------------------------------------------------------------------------------- /grpc/protobuf/src/main/scala/sttp/tapir/grpc/protobuf/model/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.grpc.protobuf 2 | 3 | package object model { 4 | type MessageName = String 5 | type ServiceName = String 6 | type MethodName = String 7 | type MessageReference = String 8 | type PackageName = String 9 | 10 | } 11 | -------------------------------------------------------------------------------- /grpc/protobuf/src/main/scala/sttp/tapir/grpc/protobuf/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.grpc 2 | 3 | package object protobuf {} 4 | -------------------------------------------------------------------------------- /grpc/protobuf/src/test/scala/sttp/tapir/grpc/protobuf/ProtobufMatchers.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.grpc.protobuf 2 | 3 | import org.scalatest.Assertion 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | trait ProtobufMatchers extends Matchers { 7 | def matchProtos(result: String, expected: String): Assertion = 8 | result.filterNot(_.isWhitespace) shouldBe expected.filterNot(_.isWhitespace) 9 | } 10 | -------------------------------------------------------------------------------- /integrations/cats/src/main/scala/sttp/tapir/integ/cats/ExampleInstances.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.integ.cats 2 | 3 | import cats.Functor 4 | import sttp.tapir.EndpointIO.Example 5 | 6 | trait ExampleInstances { 7 | implicit val exampleFunctor: Functor[Example] = new Functor[Example] { 8 | override def map[A, B](example: Example[A])(f: A => B): Example[B] = example.map(f) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /integrations/cats/src/main/scala/sttp/tapir/integ/cats/ModifyFunctorInstances.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.integ.cats 2 | 3 | import cats.data.{Chain, NonEmptyChain, NonEmptyList, NonEmptySet} 4 | import sttp.tapir._ 5 | 6 | trait ModifyFunctorInstances { 7 | 8 | implicit def nonEmptyListModifyFuntor[A]: ModifyFunctor[NonEmptyList, A] = 9 | new ModifyFunctor[NonEmptyList, A] {} 10 | 11 | implicit def nonEmptySetModifyFunctor[A]: ModifyFunctor[NonEmptySet, A] = 12 | new ModifyFunctor[NonEmptySet, A] {} 13 | 14 | implicit def chainModifyFunctor[A]: ModifyFunctor[Chain, A] = 15 | new ModifyFunctor[Chain, A] {} 16 | 17 | implicit def nonEmptyChainModifyFunctor[A]: ModifyFunctor[NonEmptyChain, A] = 18 | new ModifyFunctor[NonEmptyChain, A] {} 19 | } 20 | -------------------------------------------------------------------------------- /integrations/cats/src/main/scala/sttp/tapir/integ/cats/ServerEndpointSyntax.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.integ.cats 2 | 3 | import cats.~> 4 | import sttp.tapir.server.ServerEndpoint 5 | 6 | trait ServerEndpointSyntax { 7 | implicit class ServerEndpointImapK[R, F[_]](endpoint: ServerEndpoint[R, F]) { 8 | import MonadErrorSyntax._ 9 | 10 | def imapK[G[_]](fk: F ~> G)(gk: G ~> F): ServerEndpoint[R, G] = 11 | ServerEndpoint( 12 | endpoint.endpoint, 13 | securityLogic = monadError => (a: endpoint.SECURITY_INPUT) => fk(endpoint.securityLogic(monadError.imapK(gk)(fk))(a)), 14 | logic = monadError => (u: endpoint.PRINCIPAL) => (i: endpoint.INPUT) => fk(endpoint.logic(monadError.imapK(gk)(fk))(u)(i)) 15 | ) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /integrations/cats/src/main/scala/sttp/tapir/integ/cats/ValidatorCats.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.integ.cats 2 | 3 | import cats.Foldable 4 | import sttp.tapir.Validator 5 | 6 | object ValidatorCats { 7 | 8 | def nonEmptyFoldable[F[_]: Foldable, T]: Validator[F[T]] = 9 | Validator.nonEmpty[T, List].contramap[F[T]](Foldable[F].toList) 10 | } 11 | -------------------------------------------------------------------------------- /integrations/cats/src/main/scala/sttp/tapir/integ/cats/codec.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.integ.cats 2 | 3 | object codec extends TapirCodecCats 4 | -------------------------------------------------------------------------------- /integrations/cats/src/main/scala/sttp/tapir/integ/cats/instances.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.integ.cats 2 | 3 | object instances extends ExampleInstances with EndpointIOInstances with ModifyFunctorInstances 4 | -------------------------------------------------------------------------------- /integrations/cats/src/main/scala/sttp/tapir/integ/cats/syntax.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.integ.cats 2 | 3 | object syntax extends ServerEndpointSyntax with MonadErrorSyntax 4 | -------------------------------------------------------------------------------- /integrations/cats/src/test/scala/sttp/tapir/integ/cats/ModifyFunctorInstancesTestData.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.integ.cats 2 | 3 | import cats.data.{Chain, NonEmptyChain, NonEmptyList, NonEmptySet} 4 | 5 | // This needs to be in a separate file than ModifyFunctorInstancesTest as otherwise compiling for Scala 3 using Scala.JS 6 | // gives: 7 | // [error] |Cannot call macro class NonEmptyListWrapper defined in the same source file 8 | // [error] | This location contains code that was inlined from ModifyFunctorInstancesTest.scala:16 9 | 10 | case class NonEmptyListWrapper(f1: NonEmptyList[String]) 11 | case class NonEmptySetWrapper(f1: NonEmptySet[String]) 12 | case class ChainWrapper(f1: Chain[String]) 13 | case class NonEmptyChainWrapper(f1: NonEmptyChain[String]) 14 | -------------------------------------------------------------------------------- /integrations/enumeratum/src/main/scala/sttp/tapir/codec/enumeratum/enumeratum.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.codec 2 | 3 | package object enumeratum extends TapirCodecEnumeratum 4 | -------------------------------------------------------------------------------- /integrations/iron/src/main/scala/sttp/iron/codec/iron/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.codec 2 | 3 | package object iron extends TapirCodecIron 4 | -------------------------------------------------------------------------------- /integrations/iron/src/test/scala-3/sttp/iron/codec/iron/RefinedInt.scala: -------------------------------------------------------------------------------- 1 | package sttp.iron.codec.iron 2 | 3 | import io.github.iltotore.iron.* 4 | import io.github.iltotore.iron.constraint.all.* 5 | 6 | type RefinedIntConstraint = Interval.ClosedOpen[0, 10] 7 | object RefinedInt extends RefinedType[Int, RefinedIntConstraint] 8 | type RefinedInt = RefinedInt.T 9 | -------------------------------------------------------------------------------- /integrations/monix-newtype/src/main/scala/sttp/tapir/codec/monix/newtype/newtype.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.codec.monix 2 | 3 | package object newtype extends TapirCodecMonixNewType 4 | -------------------------------------------------------------------------------- /integrations/newtype/src/main/scala/sttp/tapir/codec/newtype/TapirCodecNewType.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.codec.newtype 2 | 3 | import io.estatico.newtype.Coercible 4 | import sttp.tapir._ 5 | 6 | trait TapirCodecNewType { 7 | implicit def schemaForNewType[A, B](implicit ev: Coercible[Schema[A], Schema[B]], schema: Schema[A]): Schema[B] = ev(schema) 8 | 9 | implicit def codecForNewType[L, A, B, CF <: CodecFormat](implicit 10 | ev1: Coercible[A, B], 11 | ev2: Coercible[B, A], 12 | codec: Codec[L, A, CF] 13 | ): Codec[L, B, CF] = codec.map(ev1(_))(ev2(_)) 14 | } 15 | -------------------------------------------------------------------------------- /integrations/newtype/src/main/scala/sttp/tapir/codec/newtype/newtype.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.codec 2 | 3 | package object newtype extends TapirCodecNewType 4 | -------------------------------------------------------------------------------- /integrations/refined/src/main/scala/sttp/tapir/codec/refined/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.codec 2 | 3 | package object refined extends TapirCodecRefined 4 | -------------------------------------------------------------------------------- /integrations/zio-prelude/src/main/scala-2/sttp/tapir/codec/zio/prelude/newtype/TapirNewtypeSupport.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.codec.zio.prelude.newtype 2 | 3 | import sttp.tapir._ 4 | import zio.prelude.Newtype 5 | 6 | trait TapirNewtypeSupport[A] { self: Newtype[A] => 7 | implicit def tapirCodec[L, CF <: CodecFormat](implicit codec: Codec[L, A, CF]): Codec[L, Type, CF] = 8 | TapirNewtype[A, self.type](self).tapirCodec 9 | 10 | implicit def tapirSchema(implicit schema: Schema[A]): Schema[Type] = 11 | TapirNewtype[A, self.type](self).tapirSchema 12 | } 13 | -------------------------------------------------------------------------------- /integrations/zio-prelude/src/main/scala-3/sttp/tapir/codec/zio/prelude/newtype/TapirNewtypeSupport.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.codec.zio.prelude.newtype 2 | 3 | import sttp.tapir._ 4 | import zio.prelude.NewtypeCustom 5 | 6 | trait TapirNewtypeSupport[A] { self: NewtypeCustom[A] => 7 | implicit def tapirCodec[L, CF <: CodecFormat](implicit codec: Codec[L, A, CF]): Codec[L, Type, CF] = 8 | TapirNewtype[A, self.type](self).tapirCodec 9 | 10 | implicit def tapirSchema(implicit schema: Schema[A]): Schema[Type] = 11 | TapirNewtype[A, self.type](self).tapirSchema 12 | } 13 | -------------------------------------------------------------------------------- /integrations/zio-prelude/src/test/scala-2/sttp/tapir/codec/zio/prelude/newtype/TapirNewtypeTestFixture.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.codec.zio.prelude.newtype 2 | 3 | import zio.prelude.Assertion.{divisibleBy, endsWith, greaterThan, startsWith} 4 | import zio.prelude.{Newtype, Subtype} 5 | 6 | object TapirNewtypeTestFixture { 7 | object StringNewtype extends Newtype[String] { 8 | override def assertion = assert(startsWith("foo") && endsWith("baz")) 9 | } 10 | type StringNewtype = StringNewtype.Type 11 | 12 | object IntSubtype extends Subtype[Int] { 13 | override def assertion = assert(divisibleBy(2) && greaterThan(5)) 14 | } 15 | type IntSubtype = IntSubtype.Type 16 | } 17 | -------------------------------------------------------------------------------- /integrations/zio-prelude/src/test/scala-3/sttp/tapir/codec/zio/prelude/newtype/PalindromeValidator.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.codec.zio.prelude.newtype 2 | 3 | import zio.prelude.{Validator, AssertionError} 4 | 5 | object PalindromeValidator 6 | extends Validator[String](str => if (str.reverse == str) Right(()) else Left(AssertionError.failure("isPalindrome"))) 7 | -------------------------------------------------------------------------------- /integrations/zio/src/main/scala/sttp/tapir/ztapir/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | package object ztapir extends Tapir with ZTapir 4 | -------------------------------------------------------------------------------- /integrations/zio/src/test/scala/sttp/tapir/ztapir/TestError.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.ztapir 2 | 3 | import sttp.tapir.CodecFormat.TextPlain 4 | import sttp.tapir.{Codec, DecodeResult} 5 | 6 | sealed trait TestError 7 | 8 | object TestError { 9 | case object SomeError extends TestError 10 | 11 | implicit val codec: Codec[String, TestError, TextPlain] = Codec.string.mapDecode { 12 | case "SomeError" => DecodeResult.Value(SomeError: TestError) 13 | case value => DecodeResult.Error(value, new RuntimeException(s"Unable to decode value $value")) 14 | }(_.toString) 15 | } 16 | -------------------------------------------------------------------------------- /json/circe/src/main/scala/sttp/tapir/json/circe/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.json 2 | 3 | package object circe extends TapirJsonCirce 4 | -------------------------------------------------------------------------------- /json/json4s/src/main/scala/sttp/tapir/json/json4s/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.json 2 | 3 | package object json4s extends TapirJson4s 4 | -------------------------------------------------------------------------------- /json/jsoniter/src/main/scala/sttp/tapir/json/jsoniter/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.json 2 | 3 | package object jsoniter extends TapirJsonJsoniter 4 | -------------------------------------------------------------------------------- /json/pickler/src/main/scala/sttp/tapir/json/pickler/UpickleHelpers.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.json.pickler 2 | 3 | private[pickler] trait UpickleHelpers: 4 | def scanChildren[T, V](xs: Seq[T])(f: T => V) = // copied from uPickle 5 | var x: V = null.asInstanceOf[V] 6 | val i = xs.iterator 7 | while (x == null && i.hasNext) { 8 | val t = f(i.next()) 9 | if (t != null) x = t 10 | } 11 | x 12 | -------------------------------------------------------------------------------- /json/pickler/src/main/scala/sttp/tapir/json/pickler/generic.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.json.pickler.generic 2 | 3 | import sttp.tapir.json.pickler.Pickler 4 | import sttp.tapir.json.pickler.PicklerConfiguration 5 | 6 | import scala.reflect.ClassTag 7 | import scala.deriving.Mirror 8 | 9 | /** Import `sttp.tapir.json.pickler.auto.*`` for automatic generic pickler derivation. A [[Pickler]] will be derived at the usage side using 10 | * [[Pickler.derived]] for each type where a given `Pickler` is not available in the current given/implicit scope. 11 | */ 12 | object auto { 13 | inline implicit def picklerForCaseClass[T: ClassTag](implicit m: Mirror.Of[T], c: PicklerConfiguration): Pickler[T] = Pickler.derived[T] 14 | } 15 | -------------------------------------------------------------------------------- /json/pickler/src/main/scala/sttp/tapir/json/pickler/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.json.pickler 2 | 3 | import sttp.tapir._ 4 | 5 | def jsonBody[T: Pickler]: EndpointIO.Body[String, T] = stringBodyUtf8AnyFormat(summon[Pickler[T]].toCodec) 6 | 7 | def jsonBodyWithRaw[T: Pickler]: EndpointIO.Body[String, (String, T)] = stringBodyUtf8AnyFormat( 8 | Codec.tupledWithRaw(summon[Pickler[T]].toCodec) 9 | ) 10 | 11 | def jsonQuery[T: Pickler](name: String): EndpointInput.Query[T] = 12 | queryAnyFormat[T, CodecFormat.Json](name, Codec.jsonQuery(summon[Pickler[T]].toCodec)) 13 | -------------------------------------------------------------------------------- /json/playjson/src/main/scala/sttp/tapir/json/play/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.json 2 | 3 | package object play extends TapirJsonPlay 4 | -------------------------------------------------------------------------------- /json/playjson/src/test/scalajs/sttp/tapir/json/play/TapirJsonPlayTestExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.json.play 2 | 3 | trait TapirJsonPlayTestExtensions 4 | -------------------------------------------------------------------------------- /json/sprayjson/src/main/scala/sttp/tapir/json/spray/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.json 2 | 3 | package object spray extends TapirJsonSpray 4 | -------------------------------------------------------------------------------- /json/tethys/src/main/scala/sttp/tapir/json/tethys/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.json 2 | 3 | package object tethys extends TapirJsonTethys 4 | -------------------------------------------------------------------------------- /json/upickle/src/main/scala/sttp/tapir/json/upickle/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.json 2 | 3 | package object upickle extends TapirJsonuPickle 4 | -------------------------------------------------------------------------------- /json/upickle/src/test/scalajs/sttp/tapir/json/upickle/TapirJsonuPickleTestExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.json.upickle 2 | 3 | trait TapirJsonuPickleTestExtensions 4 | -------------------------------------------------------------------------------- /json/upickle/src/test/scalanative/sttp/tapir/json/upickle/TapirJsonuPickleTestExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.json.upickle 2 | 3 | trait TapirJsonuPickleTestExtensions 4 | -------------------------------------------------------------------------------- /json/zio/src/main/scala/sttp/tapir/json/zio/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.json 2 | 3 | package object zio extends TapirJsonZio 4 | -------------------------------------------------------------------------------- /openapi-codegen/cli/src/main/scala/sttp/tapir/codegen/Main.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.codegen 2 | 3 | import cats.effect.{ExitCode, IO} 4 | import com.monovore.decline.Opts 5 | import com.monovore.decline.effect.CommandIOApp 6 | 7 | object Main 8 | extends CommandIOApp( 9 | BuildInfo.name, 10 | "Tapir Command Line Tools", 11 | true, 12 | version = BuildInfo.version 13 | ) { 14 | 15 | override def main: Opts[IO[ExitCode]] = 16 | Opts.subcommand(GenScala.cmd) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /openapi-codegen/cli/tapir-codegen.json: -------------------------------------------------------------------------------- 1 | { 2 | "repositories": [ 3 | "central" 4 | ], 5 | "dependencies": [ 6 | "com.softwaremill::tapir-codegen:latest.release" 7 | ] 8 | } -------------------------------------------------------------------------------- /openapi-codegen/core/src/main/scala/sttp/tapir/codegen/YamlParser.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.codegen 2 | 3 | import sttp.tapir.codegen.openapi.models.OpenapiModels.OpenapiDocument 4 | import io.circe.yaml.parser 5 | 6 | object YamlParser { 7 | import cats.implicits._ 8 | import io.circe._ 9 | 10 | def parseFile(yamlString: String): Either[Error, OpenapiDocument] = { 11 | parser 12 | .parse(yamlString) 13 | .leftMap(err => err: Error) 14 | .flatMap(_.as[OpenapiDocument]) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /openapi-codegen/core/src/main/scala/sttp/tapir/codegen/openapi/models/GenerationDirectives.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.codegen.openapi.models 2 | 3 | object GenerationDirectives { 4 | val extensionKey = "tapir-codegen-directives" 5 | val jsonBodyAsString = "json-body-as-string" 6 | val securityPrefixKey = "tapir-codegen-security-path-prefixes" 7 | } 8 | -------------------------------------------------------------------------------- /openapi-codegen/core/src/main/scala/sttp/tapir/codegen/openapi/models/OpenapiXml.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.codegen.openapi.models 2 | 3 | object OpenapiXml { 4 | sealed trait XmlConfiguration 5 | final case class XmlArrayConfiguration(name: Option[String] = None, wrapped: Option[Boolean] = None, itemName: Option[String] = None) 6 | extends XmlConfiguration { 7 | def isWrapped: Boolean = wrapped.contains(true) 8 | } 9 | final case class XmlObjectConfiguration(name: Option[String], prefix: Option[String], namespace: Option[String]) extends XmlConfiguration 10 | final case class XmlItemConfiguration(name: Option[String], attribute: Option[Boolean]) extends XmlConfiguration 11 | } 12 | -------------------------------------------------------------------------------- /openapi-codegen/core/src/main/scala/sttp/tapir/codegen/util/ErrUtils.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.codegen.util 2 | 3 | case class Location(path: String, method: String) { 4 | override def toString: String = s"${method.toUpperCase} ${path}" 5 | } 6 | 7 | object ErrUtils { 8 | def bail(msg: String)(implicit location: Location): Nothing = throw new NotImplementedError(s"$msg at $location") 9 | } 10 | -------------------------------------------------------------------------------- /openapi-codegen/core/src/main/scala/sttp/tapir/codegen/util/MapUtils.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.codegen.util 2 | 3 | object MapUtils { 4 | 5 | def merge[A, B](m1: Map[A, Seq[B]], m2: Map[A, Seq[B]]): Map[A, Seq[B]] = 6 | m2.iterator.foldLeft(m1) { case (m, (k, v)) => 7 | m.get(k) match { 8 | case Some(value) => m + (k -> (value ++ v)) 9 | case None => m + (k -> v) 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/README.md: -------------------------------------------------------------------------------- 1 | ### Developer notes 2 | 3 | #### Testing 4 | ``` 5 | sbt 6 | project openapiCodegen2_12 7 | test 8 | scripted 9 | ``` 10 | 11 | #### Local debugging 12 | ``` 13 | cd sbt/sbt-openapi-codegen/src/sbt-test/sbt-openapi-codegen/minimal/ 14 | sbt -Dplugin.version=0.1-SNAPSHOT run 15 | cat target/swagger.yaml 16 | cat target/scala-2.13/classes/sttp/tapir/generated/TapirGeneratedEndpoints.scala 17 | ``` 18 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/caching/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.0 2 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/caching/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""| 5 | | 6 | |The system property 'plugin.version' is not defined. 7 | |Specify this property using the scriptedLaunchOpts -D. 8 | | 9 | |""".stripMargin) 10 | else addSbtPlugin("com.softwaremill.sttp.tapir" % "sbt-openapi-codegen" % pluginVersion) 11 | } 12 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/caching/test: -------------------------------------------------------------------------------- 1 | > clean 2 | > run 3 | > check 4 | $ copy-file target/scala-2.13/src_managed/main/sbt-openapi-codegen/TapirGeneratedEndpoints.scala target/TapirGeneratedEndpoints.scala 5 | > compile 6 | $ newer target/TapirGeneratedEndpoints.scala target/scala-2.13/src_managed/main/sbt-openapi-codegen/TapirGeneratedEndpoints.scala 7 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/minimal/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.0 2 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/minimal/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""| 5 | | 6 | |The system property 'plugin.version' is not defined. 7 | |Specify this property using the scriptedLaunchOpts -D. 8 | | 9 | |""".stripMargin) 10 | else addSbtPlugin("com.softwaremill.sttp.tapir" % "sbt-openapi-codegen" % pluginVersion) 11 | } 12 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/minimal/test: -------------------------------------------------------------------------------- 1 | > clean 2 | > generateTapirDefinitions 3 | > run 4 | > check 5 | 6 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/multi_file/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.0 2 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/multi_file/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""| 5 | | 6 | |The system property 'plugin.version' is not defined. 7 | |Specify this property using the scriptedLaunchOpts -D. 8 | | 9 | |""".stripMargin) 10 | else addSbtPlugin("com.softwaremill.sttp.tapir" % "sbt-openapi-codegen" % pluginVersion) 11 | } 12 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/multi_file/src/main/scala/Main.scala: -------------------------------------------------------------------------------- 1 | object Main extends App { 2 | import sttp.apispec.openapi.circe.yaml._ 3 | import sttp.tapir.generated.swagger.{TapirGeneratedEndpoints => TGE1} 4 | import sttp.tapir.generated.swagger2.{TapirGeneratedEndpoints => TGE2} 5 | import sttp.tapir.docs.openapi._ 6 | 7 | val docs = OpenAPIDocsInterpreter().toOpenAPI(TGE1.generatedEndpoints ++ TGE2.generatedEndpoints, "My Bookshop", "1.0") 8 | 9 | import java.nio.file.{Paths, Files} 10 | import java.nio.charset.StandardCharsets 11 | 12 | Files.write(Paths.get("target/swagger.yaml"), docs.toYaml.getBytes(StandardCharsets.UTF_8)) 13 | } 14 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/multi_file/swagger2.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | title: My Bookshop 4 | version: '1.0' 5 | paths: 6 | /books: 7 | put: 8 | operationId: putBooksGenreYear 9 | requestBody: 10 | content: 11 | application/json: 12 | schema: 13 | $ref: '#/components/schemas/Book' 14 | responses: 15 | '204': 16 | description: OK 17 | components: 18 | schemas: 19 | Book: 20 | title: Book 21 | required: 22 | - title 23 | type: object 24 | properties: 25 | title: 26 | type: string 27 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/multi_file/test: -------------------------------------------------------------------------------- 1 | > clean 2 | > generateTapirDefinitions 3 | > run 4 | > check 5 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip-zio/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.9.9 2 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip-zio/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""| 5 | | 6 | |The system property 'plugin.version' is not defined. 7 | |Specify this property using the scriptedLaunchOpts -D. 8 | | 9 | |""".stripMargin) 10 | else addSbtPlugin("com.softwaremill.sttp.tapir" % "sbt-openapi-codegen" % pluginVersion) 11 | } 12 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip-zio/src/main/scala/Main.scala: -------------------------------------------------------------------------------- 1 | object Main extends App { 2 | import sttp.apispec.openapi.circe.yaml._ 3 | import sttp.tapir.generated._ 4 | import sttp.tapir.docs.openapi._ 5 | 6 | val docs = OpenAPIDocsInterpreter().toOpenAPI(TapirGeneratedEndpoints.generatedEndpoints, "My Bookshop", "1.0") 7 | 8 | import java.nio.file.{Paths, Files} 9 | import java.nio.charset.StandardCharsets 10 | 11 | Files.write(Paths.get("target/swagger.yaml"), docs.toYaml.getBytes(StandardCharsets.UTF_8)) 12 | } 13 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip-zio/test: -------------------------------------------------------------------------------- 1 | > clean 2 | > generateTapirDefinitions 3 | > run 4 | > test 5 | > check 6 | 7 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.0 2 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""| 5 | | 6 | |The system property 'plugin.version' is not defined. 7 | |Specify this property using the scriptedLaunchOpts -D. 8 | | 9 | |""".stripMargin) 10 | else addSbtPlugin("com.softwaremill.sttp.tapir" % "sbt-openapi-codegen" % pluginVersion) 11 | } 12 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip/src/main/scala/Main.scala: -------------------------------------------------------------------------------- 1 | object Main extends App { 2 | import sttp.apispec.openapi.circe.yaml._ 3 | import sttp.tapir.generated._ 4 | import sttp.tapir.docs.openapi._ 5 | 6 | val docs = OpenAPIDocsInterpreter().toOpenAPI(TapirGeneratedEndpoints.generatedEndpoints, "My Bookshop", "1.0") 7 | 8 | import java.nio.file.{Paths, Files} 9 | import java.nio.charset.StandardCharsets 10 | 11 | Files.write(Paths.get("target/swagger.yaml"), docs.toYaml.getBytes(StandardCharsets.UTF_8)) 12 | } 13 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip/src/test/scala/ServerSpec.scala: -------------------------------------------------------------------------------- 1 | import org.scalatest.freespec.AnyFreeSpec 2 | import org.scalatest.matchers.should.Matchers 3 | import sttp.tapir.generated.TapirGeneratedEndpoints._ 4 | 5 | class ServerSpec extends AnyFreeSpec with Matchers { 6 | 7 | "can construct uri with default values" in { 8 | Servers.`https://{environment}.my-co.org:{port}/api/{customer}/prefix`.uri().toString() shouldEqual 9 | "https://prod.my-co.org:1234/api/big-dogs/prefix" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip/test: -------------------------------------------------------------------------------- 1 | > clean 2 | > generateTapirDefinitions 3 | > run 4 | > test 5 | > check 6 | 7 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip_jsoniter/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.0 2 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip_jsoniter/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""| 5 | | 6 | |The system property 'plugin.version' is not defined. 7 | |Specify this property using the scriptedLaunchOpts -D. 8 | | 9 | |""".stripMargin) 10 | else addSbtPlugin("com.softwaremill.sttp.tapir" % "sbt-openapi-codegen" % pluginVersion) 11 | } 12 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip_jsoniter/src/main/scala/Main.scala: -------------------------------------------------------------------------------- 1 | object Main extends App { 2 | import sttp.apispec.openapi.circe.yaml._ 3 | import sttp.tapir.generated._ 4 | import sttp.tapir.docs.openapi._ 5 | 6 | val docs = OpenAPIDocsInterpreter().toOpenAPI(TapirGeneratedEndpoints.generatedEndpoints, "My Bookshop", "1.0") 7 | 8 | import java.nio.file.{Paths, Files} 9 | import java.nio.charset.StandardCharsets 10 | 11 | Files.write(Paths.get("target/swagger.yaml"), docs.toYaml.getBytes(StandardCharsets.UTF_8)) 12 | } 13 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip_jsoniter/test: -------------------------------------------------------------------------------- 1 | > clean 2 | > generateTapirDefinitions 3 | > run 4 | > test 5 | > check 6 | 7 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip_jsoniter_scala3/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.0 2 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip_jsoniter_scala3/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""| 5 | | 6 | |The system property 'plugin.version' is not defined. 7 | |Specify this property using the scriptedLaunchOpts -D. 8 | | 9 | |""".stripMargin) 10 | else addSbtPlugin("com.softwaremill.sttp.tapir" % "sbt-openapi-codegen" % pluginVersion) 11 | } 12 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip_jsoniter_scala3/src/main/scala/Main.scala: -------------------------------------------------------------------------------- 1 | object Main extends App { 2 | import sttp.apispec.openapi.circe.yaml._ 3 | import sttp.tapir.generated._ 4 | import sttp.tapir.docs.openapi._ 5 | 6 | val docs = OpenAPIDocsInterpreter().toOpenAPI(TapirGeneratedEndpoints.generatedEndpoints, "My Bookshop", "1.0") 7 | 8 | import java.nio.file.{Paths, Files} 9 | import java.nio.charset.StandardCharsets 10 | 11 | Files.write(Paths.get("target/swagger.yaml"), docs.toYaml.getBytes(StandardCharsets.UTF_8)) 12 | } 13 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip_jsoniter_scala3/test: -------------------------------------------------------------------------------- 1 | > clean 2 | > generateTapirDefinitions 3 | > run 4 | > test 5 | > check 6 | 7 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip_scala3/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.0 2 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip_scala3/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""| 5 | | 6 | |The system property 'plugin.version' is not defined. 7 | |Specify this property using the scriptedLaunchOpts -D. 8 | | 9 | |""".stripMargin) 10 | else addSbtPlugin("com.softwaremill.sttp.tapir" % "sbt-openapi-codegen" % pluginVersion) 11 | } 12 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip_scala3/src/main/scala/Main.scala: -------------------------------------------------------------------------------- 1 | object Main extends App { 2 | import sttp.apispec.openapi.circe.yaml._ 3 | import sttp.tapir.generated._ 4 | import sttp.tapir.docs.openapi._ 5 | 6 | val docs = OpenAPIDocsInterpreter().toOpenAPI(TapirGeneratedEndpoints.generatedEndpoints, "My Bookshop", "1.0") 7 | 8 | import java.nio.file.{Paths, Files} 9 | import java.nio.charset.StandardCharsets 10 | 11 | Files.write(Paths.get("target/swagger.yaml"), docs.toYaml.getBytes(StandardCharsets.UTF_8)) 12 | } 13 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip_scala3/src/test/scala/ServerSpec.scala: -------------------------------------------------------------------------------- 1 | import org.scalatest.freespec.AnyFreeSpec 2 | import org.scalatest.matchers.should.Matchers 3 | import sttp.tapir.generated.TapirGeneratedEndpoints._ 4 | 5 | class ServerSpec extends AnyFreeSpec with Matchers { 6 | 7 | "can construct uri with default values" in { 8 | Servers.`https://{environment}.my-co.org:{port}/api/{customer}/prefix`.uri().toString() shouldEqual 9 | "https://prod.my-co.org:1234/api/big-dogs/prefix" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/oneOf-json-roundtrip_scala3/test: -------------------------------------------------------------------------------- 1 | > clean 2 | > generateTapirDefinitions 3 | > run 4 | > test 5 | > check 6 | 7 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/option-overrides/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.0 2 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/option-overrides/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""| 5 | | 6 | |The system property 'plugin.version' is not defined. 7 | |Specify this property using the scriptedLaunchOpts -D. 8 | | 9 | |""".stripMargin) 10 | else addSbtPlugin("com.softwaremill.sttp.tapir" % "sbt-openapi-codegen" % pluginVersion) 11 | } 12 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/option-overrides/test: -------------------------------------------------------------------------------- 1 | > clean 2 | > generateTapirDefinitions 3 | > run 4 | > check 5 | 6 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/petstore/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.0 2 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/petstore/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | { 2 | val pluginVersion = System.getProperty("plugin.version") 3 | if (pluginVersion == null) 4 | throw new RuntimeException("""| 5 | | 6 | |The system property 'plugin.version' is not defined. 7 | |Specify this property using the scriptedLaunchOpts -D. 8 | | 9 | |""".stripMargin) 10 | else addSbtPlugin("com.softwaremill.sttp.tapir" % "sbt-openapi-codegen" % pluginVersion) 11 | } 12 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/petstore/src/main/scala/Main.scala: -------------------------------------------------------------------------------- 1 | object Main extends App { 2 | import sttp.apispec.openapi.circe.yaml._ 3 | import sttp.tapir.generated._ 4 | import sttp.tapir.docs.openapi._ 5 | 6 | val docs = 7 | OpenAPIDocsInterpreter().toOpenAPI(TapirGeneratedEndpoints.generatedEndpoints, "Swagger Petstore - OpenAPI 3.0", "1.0.27-SNAPSHOT") 8 | 9 | import java.nio.file.{Paths, Files} 10 | import java.nio.charset.StandardCharsets 11 | 12 | Files.write(Paths.get("target/swagger.yaml"), docs.toYaml.getBytes(StandardCharsets.UTF_8)) 13 | } 14 | -------------------------------------------------------------------------------- /openapi-codegen/sbt-plugin/src/sbt-test/sbt-openapi-codegen/petstore/test: -------------------------------------------------------------------------------- 1 | > clean 2 | > generateTapirDefinitions 3 | > run 4 | > test 5 | > check 6 | 7 | -------------------------------------------------------------------------------- /perf-tests/results/100users-1min-18-03-2022-local-adamw.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | 2019 mbp, 2,3 GHz 8-Core Intel Core i9 4 | 5 | # Results 6 | 7 | * vanilla akka-http, multi-route: 23278 req/s 8 | * tapir akka-http, multi-route: 8277 req/s 9 | 10 | ## After introducing `FilterServerEndpoints` 11 | 12 | * vanilla akka-http, multi-route: 25531 req/s 13 | * tapir akka-http, multi-route: 45407 req/s -------------------------------------------------------------------------------- /perf-tests/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %date [%thread] %-5level %logger{36} - %msg%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /perf-tests/src/main/scala/sttp/tapir/perf/apis/ServerName.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.perf.apis 2 | 3 | import sttp.tapir.perf.Common._ 4 | 5 | sealed trait ServerName { 6 | def shortName: String 7 | def fullName: String 8 | } 9 | case class KnownServerName(shortName: String, fullName: String) extends ServerName 10 | 11 | /** Used when running a suite without specifying servers and assuming that a server has been started externally. */ 12 | case object ExternalServerName extends ServerName { 13 | override def shortName: String = "External" 14 | override def fullName: String = "External" 15 | } 16 | 17 | object ServerName { 18 | def fromShort(shortName: String): ServerName = 19 | KnownServerName(shortName, s"${rootPackage}.${shortName}Server") 20 | } 21 | -------------------------------------------------------------------------------- /project/FileUtils.scala: -------------------------------------------------------------------------------- 1 | import java.io.File 2 | import java.nio.file.{FileVisitResult, Files, Path, SimpleFileVisitor} 3 | import java.nio.file.attribute.BasicFileAttributes 4 | 5 | object FileUtils { 6 | def listScalaFiles(basePath: File): Seq[Path] = { 7 | val dirPath = basePath.toPath 8 | var result = Vector.empty[Path] 9 | 10 | val fileVisitor = new SimpleFileVisitor[Path] { 11 | override def visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult = { 12 | if (file.toString.endsWith(".scala")) { 13 | result = result :+ file 14 | } 15 | FileVisitResult.CONTINUE 16 | } 17 | } 18 | 19 | Files.walkFileTree(dirPath, fileVisitor) 20 | result 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.0 2 | -------------------------------------------------------------------------------- /project/build.sbt: -------------------------------------------------------------------------------- 1 | // needed for GenerateMimeDb 2 | libraryDependencies += "io.circe" %% "circe-parser" % "0.14.13" 3 | -------------------------------------------------------------------------------- /server/armeria-server/cats/src/main/scala/sttp/tapir/server/armeria/cats/CatsMonadAsyncError.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.armeria.cats 2 | 3 | import cats.effect.Async 4 | import cats.syntax.functor._ 5 | import sttp.monad.{Canceler, MonadAsyncError} 6 | import sttp.tapir.integ.cats.effect.CatsMonadError 7 | 8 | // Forked from sttp.client3.impl.cats.CatsMonadAsyncError 9 | private class CatsMonadAsyncError[F[_]](implicit F: Async[F]) extends CatsMonadError[F] with MonadAsyncError[F] { 10 | override def async[T](register: ((Either[Throwable, T]) => Unit) => Canceler): F[T] = 11 | F.async(cb => F.delay(register(cb)).map(c => Some(F.delay(c.cancel())))) 12 | 13 | override def blocking[T](t: => T): F[T] = 14 | F.blocking(t) 15 | } 16 | -------------------------------------------------------------------------------- /server/armeria-server/src/main/scala/sttp/tapir/server/armeria/ArmeriaServerOptions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.armeria 2 | 3 | import sttp.tapir.TapirFile 4 | import sttp.tapir.server.interceptor.Interceptor 5 | 6 | trait ArmeriaServerOptions[F[_]] { 7 | def createFile: () => F[TapirFile] 8 | def deleteFile: TapirFile => F[Unit] 9 | def interceptors: List[Interceptor[F]] 10 | } 11 | -------------------------------------------------------------------------------- /server/armeria-server/src/main/scala/sttp/tapir/server/armeria/FutureConversion.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.armeria 2 | 3 | import scala.concurrent.Future 4 | 5 | private[armeria] trait FutureConversion[F[_]] { 6 | def from[A](f: => Future[A]): F[A] 7 | 8 | def to[A](f: => F[A]): Future[A] 9 | } 10 | 11 | private[armeria] object FutureConversion { 12 | val identity: FutureConversion[Future] = new FutureConversion[Future] { 13 | override def from[A](f: => Future[A]): Future[A] = f 14 | 15 | override def to[A](f: => Future[A]): Future[A] = f 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/armeria-server/src/main/scala/sttp/tapir/server/armeria/StreamCompatible.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.armeria 2 | 3 | import com.linecorp.armeria.common.HttpData 4 | import org.reactivestreams.Publisher 5 | import sttp.capabilities.Streams 6 | 7 | private[armeria] trait StreamCompatible[S <: Streams[S]] { 8 | val streams: S 9 | def asStreamMessage(s: streams.BinaryStream): Publisher[HttpData] 10 | def fromArmeriaStream(s: Publisher[HttpData], maxBytes: Option[Long]): streams.BinaryStream 11 | } 12 | -------------------------------------------------------------------------------- /server/armeria-server/src/main/scala/sttp/tapir/server/armeria/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server 2 | 3 | import com.linecorp.armeria.common.HttpData 4 | import com.linecorp.armeria.common.stream.StreamMessage 5 | 6 | package object armeria { 7 | private[armeria] type ArmeriaResponseType = Either[StreamMessage[HttpData], HttpData] 8 | } 9 | -------------------------------------------------------------------------------- /server/armeria-server/src/test/scala/sttp/tapir/server/armeria/ArmeriaTestFutureServerInterpreter.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.armeria 2 | 3 | import sttp.capabilities.armeria.ArmeriaStreams 4 | import sttp.tapir.server.ServerEndpoint 5 | 6 | import scala.concurrent.Future 7 | 8 | class ArmeriaTestFutureServerInterpreter extends ArmeriaTestServerInterpreter[ArmeriaStreams, Future, ArmeriaFutureServerOptions] { 9 | 10 | override def route(es: List[ServerEndpoint[ArmeriaStreams, Future]], interceptors: Interceptors): TapirService[ArmeriaStreams, Future] = { 11 | val serverOptions: ArmeriaFutureServerOptions = interceptors(ArmeriaFutureServerOptions.customiseInterceptors).options 12 | ArmeriaFutureServerInterpreter(serverOptions).toService(es) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /server/core/src/main/scala/sttp/tapir/server/interceptor/DecodeFailureContext.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.interceptor 2 | 3 | import sttp.tapir.model.ServerRequest 4 | import sttp.tapir.{AnyEndpoint, DecodeResult, EndpointInput} 5 | 6 | case class DecodeFailureContext( 7 | endpoint: AnyEndpoint, 8 | failingInput: EndpointInput[_], 9 | failure: DecodeResult.Failure, 10 | request: ServerRequest 11 | ) 12 | -------------------------------------------------------------------------------- /server/core/src/main/scala/sttp/tapir/server/interceptor/DecodeSuccessContext.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.interceptor 2 | 3 | import sttp.tapir.Endpoint 4 | import sttp.tapir.model.ServerRequest 5 | import sttp.tapir.server.ServerEndpoint 6 | 7 | case class DecodeSuccessContext[F[_], A, U, I]( 8 | serverEndpoint: ServerEndpoint.Full[A, U, I, _, _, _, F], 9 | securityInput: A, 10 | principal: U, 11 | input: I, 12 | request: ServerRequest 13 | ) { 14 | def endpoint: Endpoint[A, I, _, _, _] = serverEndpoint.endpoint 15 | } 16 | -------------------------------------------------------------------------------- /server/core/src/main/scala/sttp/tapir/server/interceptor/RequestResult.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.interceptor 2 | 3 | import sttp.tapir.server.model.ServerResponse 4 | 5 | /** The result of processing a request: either a response, or a list of endpoint decoding failures. */ 6 | sealed trait RequestResult[+B] 7 | object RequestResult { 8 | case class Response[B](response: ServerResponse[B]) extends RequestResult[B] 9 | case class Failure(failures: List[DecodeFailureContext]) extends RequestResult[Nothing] 10 | } 11 | -------------------------------------------------------------------------------- /server/core/src/main/scala/sttp/tapir/server/interceptor/SecurityFailureContext.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.interceptor 2 | 3 | import sttp.tapir.Endpoint 4 | import sttp.tapir.model.ServerRequest 5 | import sttp.tapir.server.ServerEndpoint 6 | 7 | case class SecurityFailureContext[F[_], A]( 8 | serverEndpoint: ServerEndpoint.Full[A, _, _, _, _, _, F], 9 | securityInput: A, 10 | request: ServerRequest 11 | ) { 12 | def endpoint: Endpoint[A, _, _, _, _] = serverEndpoint.endpoint 13 | } 14 | -------------------------------------------------------------------------------- /server/core/src/main/scala/sttp/tapir/server/interceptor/exception/ExceptionContext.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.interceptor.exception 2 | 3 | import sttp.tapir.AnyEndpoint 4 | import sttp.tapir.model.ServerRequest 5 | 6 | case class ExceptionContext(e: Throwable, endpoint: AnyEndpoint, request: ServerRequest) 7 | -------------------------------------------------------------------------------- /server/core/src/main/scala/sttp/tapir/server/interceptor/log/ExceptionContext.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.interceptor.log 2 | 3 | import sttp.tapir.Endpoint 4 | import sttp.tapir.model.ServerRequest 5 | 6 | case class ExceptionContext[A, U]( 7 | endpoint: Endpoint[A, _, _, _, _], 8 | securityInput: Option[A], 9 | principal: Option[U], 10 | request: ServerRequest 11 | ) 12 | -------------------------------------------------------------------------------- /server/core/src/main/scala/sttp/tapir/server/interceptor/reject/RejectContext.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.interceptor.reject 2 | 3 | import sttp.tapir.model.ServerRequest 4 | import sttp.tapir.server.interceptor.RequestResult 5 | 6 | case class RejectContext(failure: RequestResult.Failure, request: ServerRequest) 7 | -------------------------------------------------------------------------------- /server/core/src/main/scala/sttp/tapir/server/interpreter/BodyListener.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.interpreter 2 | 3 | import scala.util.Try 4 | 5 | trait BodyListener[F[_], B] { 6 | def onComplete(body: B)(cb: Try[Unit] => F[Unit]): F[B] 7 | } 8 | 9 | object BodyListener { 10 | implicit class BodyListenerOps[B](body: B) { 11 | def onComplete[F[_]](cb: Try[Unit] => F[Unit])(implicit l: BodyListener[F, B]): F[B] = l.onComplete(body)(cb) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/core/src/main/scala/sttp/tapir/server/interpreter/ToResponseBody.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.interpreter 2 | 3 | import sttp.capabilities.Streams 4 | import sttp.model.HasHeaders 5 | import sttp.tapir.{CodecFormat, RawBodyType, WebSocketBodyOutput} 6 | 7 | import java.nio.charset.Charset 8 | 9 | trait ToResponseBody[B, S] { 10 | val streams: Streams[S] 11 | def fromRawValue[R](v: R, headers: HasHeaders, format: CodecFormat, bodyType: RawBodyType[R]): B // TODO: remove headers? 12 | def fromStreamValue(v: streams.BinaryStream, headers: HasHeaders, format: CodecFormat, charset: Option[Charset]): B 13 | def fromWebSocketPipe[REQ, RESP](pipe: streams.Pipe[REQ, RESP], o: WebSocketBodyOutput[streams.Pipe[REQ, RESP], REQ, RESP, _, S]): B 14 | } 15 | -------------------------------------------------------------------------------- /server/core/src/main/scala/sttp/tapir/server/model/ValuedEndpointOutput.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.model 2 | 3 | import sttp.tapir.EndpointOutput 4 | 5 | case class ValuedEndpointOutput[T](output: EndpointOutput[T], value: T) { 6 | def prepend[U](otherOutput: EndpointOutput[U], otherValue: U): ValuedEndpointOutput[(U, T)] = 7 | ValuedEndpointOutput(otherOutput.and(output), (otherValue, value)) 8 | 9 | def append[U](otherOutput: EndpointOutput[U], otherValue: U): ValuedEndpointOutput[(T, U)] = 10 | ValuedEndpointOutput(output.and(otherOutput), (value, otherValue)) 11 | } 12 | -------------------------------------------------------------------------------- /server/finatra-server/src/main/scala/sttp/tapir/server/finatra/FinatraContent.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.finatra 2 | 3 | import com.twitter.io.{Buf, Reader} 4 | 5 | sealed trait FinatraContent 6 | case class FinatraContentBuf(buf: Buf) extends FinatraContent 7 | case class FinatraContentReader(reader: Reader[Buf]) extends FinatraContent 8 | -------------------------------------------------------------------------------- /server/finatra-server/src/main/scala/sttp/tapir/server/finatra/FinatraRoute.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.finatra 2 | 3 | import com.twitter.finagle.http.{Method, Request, Response} 4 | import com.twitter.util.Future 5 | 6 | case class FinatraRoute(handler: Request => Future[Response], method: Method, path: String) 7 | -------------------------------------------------------------------------------- /server/http4s-server/zio/src/main/scala/sttp/tapir/server/http4s/ztapir/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.http4s 2 | 3 | import sttp.capabilities.zio.ZioStreams 4 | import sttp.model.sse.ServerSentEvent 5 | import sttp.tapir.ztapir.ZioServerSentEvents 6 | import sttp.tapir.{CodecFormat, StreamBodyIO, streamTextBody} 7 | import zio.stream._ 8 | 9 | import java.nio.charset.Charset 10 | 11 | package object ztapir { 12 | val serverSentEventsBody: StreamBodyIO[Stream[Throwable, Byte], Stream[Throwable, ServerSentEvent], ZioStreams] = { 13 | streamTextBody(ZioStreams)(CodecFormat.TextEventStream(), Some(Charset.forName("UTF-8"))) 14 | .map(ZioServerSentEvents.parseBytesToSSE)(ZioServerSentEvents.serialiseSSEToBytes) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /server/jdkhttp-server/src/main/scala/sttp/tapir/server/jdkhttp/internal/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.jdkhttp 2 | 3 | import sttp.monad.{IdentityMonad, MonadError} 4 | import sttp.shared.Identity 5 | 6 | package object internal { 7 | private[jdkhttp] implicit val idMonad: MonadError[Identity] = IdentityMonad 8 | } 9 | -------------------------------------------------------------------------------- /server/jdkhttp-server/src/main/scala/sttp/tapir/server/jdkhttp/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server 2 | 3 | import java.io.InputStream 4 | 5 | package object jdkhttp { 6 | 7 | type HttpServer = com.sun.net.httpserver.HttpServer 8 | type HttpsConfigurator = com.sun.net.httpserver.HttpsConfigurator 9 | 10 | type JdkHttpResponseBody = (InputStream, Option[Long]) 11 | } 12 | -------------------------------------------------------------------------------- /server/netty-server/cats/src/main/scala-2.12/sttp/tapir/server/netty/cats/internal/ExecutionContexts.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.netty.cats.internal 2 | 3 | import scala.concurrent.ExecutionContext 4 | 5 | object ExecutionContexts { 6 | val sameThread: ExecutionContext = new ExecutionContext { 7 | override def execute(runnable: Runnable): Unit = runnable.run() 8 | 9 | override def reportFailure(cause: Throwable): Unit = 10 | ExecutionContext.defaultReporter(cause) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /server/netty-server/cats/src/main/scala-3-2.13+/sttp/tapir/server/netty/cats/internal/ExecutionContexts.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.netty.cats.internal 2 | 3 | import scala.concurrent.ExecutionContext 4 | 5 | object ExecutionContexts { 6 | val sameThread: ExecutionContext = ExecutionContext.parasitic 7 | } 8 | -------------------------------------------------------------------------------- /server/netty-server/src/main/scala/sttp/tapir/server/netty/internal/NettyDefaults.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.netty.internal 2 | 3 | import org.slf4j.Logger 4 | 5 | object NettyDefaults { 6 | def debugLog(log: Logger, msg: String, exOpt: Option[Throwable]): Unit = 7 | if (log.isDebugEnabled) { 8 | exOpt match { 9 | case None => log.debug(msg) 10 | case Some(ex) => log.debug(msg, ex) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/netty-server/src/main/scala/sttp/tapir/server/netty/internal/RunAsync.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.netty.internal 2 | 3 | import sttp.shared.Identity 4 | 5 | import scala.concurrent.Future 6 | 7 | trait RunAsync[F[_]] { 8 | def apply(f: => F[Unit]): Unit 9 | } 10 | object RunAsync { 11 | final val Id: RunAsync[Identity] = new RunAsync[Identity] { 12 | override def apply(f: => Identity[Unit]): Unit = f 13 | } 14 | 15 | final val Future: RunAsync[Future] = new RunAsync[Future] { 16 | override def apply(f: => Future[Unit]): Unit = 17 | f: Unit 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/netty-server/src/main/scala/sttp/tapir/server/netty/internal/UnhandledExceptionHandler.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.netty.internal 2 | 3 | import io.netty.channel.{ChannelHandlerContext, ChannelInboundHandlerAdapter} 4 | import io.netty.util.internal.logging.InternalLoggerFactory 5 | 6 | private[internal] class UnhandledExceptionHandler extends ChannelInboundHandlerAdapter { 7 | private lazy val logger = InternalLoggerFactory.getInstance(getClass) 8 | 9 | override def exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable): Unit = { 10 | cause match { 11 | case ex => 12 | logger.warn("Unhandled exception", ex) 13 | } 14 | val _ = ctx.close() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /server/netty-server/src/main/scala/sttp/tapir/server/netty/internal/reactivestreams/PromisingSubscriber.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.netty.internal.reactivestreams 2 | 3 | import org.reactivestreams.Subscriber 4 | 5 | import scala.concurrent.Future 6 | 7 | trait PromisingSubscriber[R, A] extends Subscriber[A] { 8 | def future: Future[R] 9 | } 10 | -------------------------------------------------------------------------------- /server/netty-server/sync/src/main/scala/sttp/tapir/server/netty/sync/OxStreams.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.netty.sync 2 | 3 | import ox.Chunk 4 | import ox.flow.Flow 5 | import sttp.capabilities.Streams 6 | 7 | trait OxStreams extends Streams[OxStreams]: 8 | override type BinaryStream = Flow[Chunk[Byte]] 9 | override type Pipe[A, B] = Flow[A] => Flow[B] 10 | 11 | object OxStreams extends OxStreams 12 | -------------------------------------------------------------------------------- /server/netty-server/zio/src/main/scala/sttp/tapir/server/netty/zio/internal/ZioUtil.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.netty.zio.internal 2 | 3 | import io.netty.channel.{Channel, ChannelFuture} 4 | import sttp.tapir.server.netty.internal.FutureUtil 5 | import zio.{RIO, ZIO} 6 | 7 | private[zio] object ZioUtil { 8 | def nettyChannelFutureToScala[R](nettyFuture: ChannelFuture): RIO[R, Channel] = 9 | ZIO.fromFuture(_ => FutureUtil.nettyChannelFutureToScala(nettyFuture)) 10 | 11 | def nettyFutureToScala[R, T](f: io.netty.util.concurrent.Future[T]): RIO[R, T] = 12 | ZIO.fromFuture(_ => FutureUtil.nettyFutureToScala(f)) 13 | } 14 | -------------------------------------------------------------------------------- /server/nima-server/src/main/scala/sttp/tapir/server/nima/internal/NimaBodyListener.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.nima.internal 2 | 3 | import io.helidon.webserver.http.{ServerResponse => JavaNimaServerResponse} 4 | import sttp.shared.Identity 5 | import sttp.tapir.server.interpreter.BodyListener 6 | 7 | import java.io.InputStream 8 | import scala.util.{Success, Try} 9 | 10 | private[nima] class NimaBodyListener(res: JavaNimaServerResponse) extends BodyListener[Identity, InputStream] { 11 | override def onComplete(body: InputStream)(cb: Try[Unit] => Unit): InputStream = { 12 | res.whenSent(() => cb(Success(()))) 13 | body 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/nima-server/src/main/scala/sttp/tapir/server/nima/internal/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.nima 2 | 3 | import sttp.monad.{IdentityMonad, MonadError} 4 | import sttp.shared.Identity 5 | 6 | package object internal { 7 | private[nima] implicit val idMonad: MonadError[Identity] = IdentityMonad 8 | } 9 | -------------------------------------------------------------------------------- /server/nima-server/src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n%rEx 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /server/play-server/src/main/scala/sttp/tapir/server/play/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server 2 | 3 | import _root_.play.api.http.HttpEntity 4 | import _root_.play.api.http.websocket.Message 5 | import org.apache.pekko.stream.scaladsl.Flow 6 | 7 | package object play { 8 | type PlayResponseBody = Either[Flow[Message, Message, Any], HttpEntity] 9 | } 10 | -------------------------------------------------------------------------------- /server/play29-server/src/main/scala/sttp/tapir/server/play/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server 2 | 3 | import _root_.play.api.http.HttpEntity 4 | import _root_.play.api.http.websocket.Message 5 | import akka.stream.scaladsl.Flow 6 | 7 | package object play { 8 | type PlayResponseBody = Either[Flow[Message, Message, Any], HttpEntity] 9 | } 10 | -------------------------------------------------------------------------------- /server/sttp-stub-server/src/main/scala/sttp/tapir/server/stub/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server 2 | 3 | import sttp.capabilities.Streams 4 | 5 | package object stub extends SttpStubServer { 6 | 7 | private[stub] trait AnyStreams extends Streams[AnyStreams] { 8 | override type BinaryStream = Any 9 | override type Pipe[A, B] = Nothing 10 | } 11 | private[stub] object AnyStreams extends AnyStreams 12 | } 13 | -------------------------------------------------------------------------------- /server/sttp-stub4-server/src/main/scala/sttp/tapir/server/stub4/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server 2 | 3 | import sttp.capabilities.Streams 4 | 5 | package object stub4 extends SttpStubServer { 6 | 7 | private[stub4] trait AnyStreams extends Streams[AnyStreams] { 8 | override type BinaryStream = Any 9 | override type Pipe[A, B] = Nothing 10 | } 11 | private[stub4] object AnyStreams extends AnyStreams 12 | } 13 | -------------------------------------------------------------------------------- /server/tests/src/main/resources/test/d1/d2/r4.txt: -------------------------------------------------------------------------------- 1 | Resource 4 -------------------------------------------------------------------------------- /server/tests/src/main/resources/test/d1/index.html: -------------------------------------------------------------------------------- 1 | Index resource -------------------------------------------------------------------------------- /server/tests/src/main/resources/test/d1/r3.txt: -------------------------------------------------------------------------------- 1 | Resource 3 -------------------------------------------------------------------------------- /server/tests/src/main/resources/test/r1.txt: -------------------------------------------------------------------------------- 1 | Resource 1 -------------------------------------------------------------------------------- /server/tests/src/main/resources/test/r2.txt: -------------------------------------------------------------------------------- 1 | Resource 2 -------------------------------------------------------------------------------- /server/tests/src/main/resources/test/r3.txt: -------------------------------------------------------------------------------- 1 | Resource 3 -------------------------------------------------------------------------------- /server/tests/src/main/resources/test/r3.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softwaremill/tapir/36e7493eedb12ed5d6a9f41ec81e303fd7017034/server/tests/src/main/resources/test/r3.txt.gz -------------------------------------------------------------------------------- /server/tests/src/main/resources/test2/r5.txt: -------------------------------------------------------------------------------- 1 | Resource 5 -------------------------------------------------------------------------------- /server/tests/src/main/scala/sttp/tapir/server/tests/CreateServerStubTest.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.tests 2 | 3 | import sttp.client4.testing.{BackendStub, StreamBackendStub} 4 | import sttp.tapir.server.interceptor.CustomiseInterceptors 5 | 6 | import scala.concurrent.Future 7 | 8 | trait CreateServerStubTest[F[_], OPTIONS] { 9 | def customiseInterceptors: CustomiseInterceptors[F, OPTIONS] 10 | def stub: BackendStub[F] 11 | def asFuture[A]: F[A] => Future[A] 12 | def cleanUp(): Unit = () 13 | } 14 | -------------------------------------------------------------------------------- /server/tests/src/main/scala/sttp/tapir/server/tests/Sleeper.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.tests 2 | 3 | import scala.concurrent.duration._ 4 | import scala.concurrent.blocking 5 | import scala.concurrent.Future 6 | import scala.concurrent.ExecutionContext 7 | 8 | trait Sleeper[F[_]] { 9 | def sleep(duration: FiniteDuration): F[Unit] 10 | } 11 | 12 | object Sleeper { 13 | def futureSleeper(implicit ec: ExecutionContext): Sleeper[Future] = new Sleeper[Future] { 14 | override def sleep(duration: FiniteDuration): Future[Unit] = Future { 15 | blocking { 16 | Thread.sleep(duration.toMillis) 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/vertx-server/src/main/scala/sttp/tapir/server/vertx/interpreters/FromVFuture.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.vertx.interpreters 2 | 3 | import io.vertx.core.Future 4 | 5 | trait FromVFuture[F[_]] { 6 | def apply[T](f: => Future[T]): F[T] 7 | } 8 | -------------------------------------------------------------------------------- /server/vertx-server/src/main/scala/sttp/tapir/server/vertx/interpreters/RunAsync.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.vertx.interpreters 2 | 3 | trait RunAsync[F[_]] { 4 | def apply[T](f: => F[T]): Unit 5 | } 6 | -------------------------------------------------------------------------------- /server/zio-http-server/src/main/scala/sttp/tapir/server/ziohttp/ZioHttpResponseBody.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server.ziohttp 2 | 3 | import zio.stream.ZStream 4 | import zio.Chunk 5 | import zio.http.FormField 6 | 7 | sealed trait ZioHttpResponseBody { 8 | def contentLength: Option[Long] 9 | } 10 | 11 | case class ZioStreamHttpResponseBody(stream: ZStream[Any, Throwable, Byte], contentLength: Option[Long]) extends ZioHttpResponseBody 12 | 13 | case class ZioRawHttpResponseBody(bytes: Chunk[Byte], contentLength: Option[Long]) extends ZioHttpResponseBody 14 | 15 | case class ZioMultipartHttpResponseBody(formFields: List[FormField]) extends ZioHttpResponseBody { 16 | override def contentLength: Option[Long] = None 17 | } 18 | -------------------------------------------------------------------------------- /server/zio-http-server/src/main/scala/sttp/tapir/server/ziohttp/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.server 2 | import zio.http.WebSocketChannelEvent 3 | import zio.{ZIO, stream} 4 | 5 | package object ziohttp { 6 | type WebSocketHandler = 7 | stream.Stream[Throwable, WebSocketChannelEvent] => ZIO[Any, Throwable, stream.Stream[Throwable, WebSocketChannelEvent]] 8 | 9 | type ZioResponseBody = 10 | Either[WebSocketHandler, ZioHttpResponseBody] 11 | 12 | } 13 | -------------------------------------------------------------------------------- /server/zio-http-server/src/test/scala/zio/http/netty/TestChannelFactories.scala: -------------------------------------------------------------------------------- 1 | package zio.http.netty 2 | 3 | import io.netty.channel.{ChannelFactory, ServerChannel} 4 | import zio.ZLayer 5 | 6 | // Note: Workaround to access package private ChannelFactories 7 | object TestChannelFactories { 8 | val config: ZLayer[ChannelType.Config, Nothing, ChannelFactory[ServerChannel]] = ChannelFactories.Server.fromConfig 9 | } 10 | -------------------------------------------------------------------------------- /server/zio-http-server/src/test/scala/zio/test/package.scala: -------------------------------------------------------------------------------- 1 | package zio 2 | 3 | import zio.http._ 4 | import zio.http.netty.NettyConfig 5 | import zio.http.netty.server.NettyDriver 6 | import zio.http.netty.server.ServerEventLoopGroups 7 | import io.netty.channel.ChannelFactory 8 | import io.netty.channel.ServerChannel 9 | 10 | package object test { 11 | val driver: ZLayer[ServerEventLoopGroups & ChannelFactory[ServerChannel] & Server.Config, Nothing, Driver] = 12 | ZLayer.makeSome[ServerEventLoopGroups & ChannelFactory[ServerChannel] & Server.Config, Driver]( 13 | ZLayer.succeed(NettyConfig.default), 14 | NettyDriver.manual 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /serverless/aws/cdk/src/main/resources/app-template/bin/tapir-cdk-stack.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import * as cdk from 'aws-cdk-lib'; 3 | import { TapirCdkStack } from '../lib/tapir-cdk-stack'; 4 | 5 | const app = new cdk.App(); 6 | new TapirCdkStack(app, 'TapirCdkStack'); 7 | -------------------------------------------------------------------------------- /serverless/aws/cdk/src/main/resources/app-template/gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | !jest.config.js 3 | *.d.ts 4 | node_modules 5 | package-lock.json 6 | 7 | # CDK asset staging directory 8 | .cdk.staging 9 | cdk.out 10 | -------------------------------------------------------------------------------- /serverless/aws/cdk/src/main/resources/app-template/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /serverless/aws/cdk/src/main/resources/app-template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tapir-cdk", 3 | "version": "0.1.0", 4 | "bin": { 5 | "tapir-cdk": "bin/tapir-cdk-stack.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^27.5.2", 15 | "@types/node": "10.17.27", 16 | "@types/prettier": "2.6.0", 17 | "aws-cdk": "2.36.0", 18 | "jest": "^27.5.1", 19 | "ts-jest": "^27.1.4", 20 | "ts-node": "^10.9.1", 21 | "typescript": "~3.9.7" 22 | }, 23 | "dependencies": { 24 | "aws-cdk-lib": "2.189.1", 25 | "constructs": "^10.0.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /serverless/aws/cdk/src/main/resources/app-template/readme.md: -------------------------------------------------------------------------------- 1 | # AWS CDK Stack 2 | 3 | ## Prerequisites 4 | 5 | - Install [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 6 | - Install [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) 7 | - Install [AWS CDK Toolkit](https://docs.aws.amazon.com/cdk/v2/guide/cli.html) 8 | 9 | ## Run service locally 10 | 11 | ``` 12 | npm install 13 | cdk synth 14 | sam local start-api -t cdk.out/TapirCdkStack.template.json 15 | ``` 16 | 17 | ## Deploy to production 18 | 19 | - Configure your account ```aws configure``` 20 | - ```cdk deploy``` -------------------------------------------------------------------------------- /serverless/aws/cdk/src/main/scala/sttp/tapir/serverless/aws/cdk/internal/Request.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.serverless.aws.cdk.internal 2 | 3 | import sttp.tapir.AnyEndpoint 4 | 5 | private[cdk] case class Request(method: Method, path: List[Segment]) 6 | 7 | private[cdk] object Request { 8 | def fromEndpoint(endpoint: AnyEndpoint): Option[Request] = { 9 | for { 10 | e <- endpoint.method 11 | method <- Method(e.toString()) 12 | path = endpoint.showPathTemplate(showQueryParam = None).substring(1) 13 | } yield Request(method, path.split("/").flatMap(Segment.apply).toList) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /serverless/aws/cdk/src/main/scala/sttp/tapir/serverless/aws/cdk/internal/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.serverless.aws.cdk 2 | 3 | package object internal { 4 | type Tree = List[Node] 5 | 6 | implicit final class ListStringOps(private val input: List[String]) { 7 | def toRequest(method: Method): Request = Request(method, input.flatMap(Segment.apply)) 8 | } 9 | 10 | implicit final class ListSegmentOps(private val input: List[Segment]) { 11 | def stringify: String = input.map(_.toString).mkString("/") 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /serverless/aws/lambda-core/src/main/scala/sttp/tapir/serverless/aws/lambda/AwsBodyListener.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.serverless.aws.lambda 2 | 3 | import sttp.monad.MonadError 4 | import sttp.monad.syntax._ 5 | import sttp.tapir.server.interpreter.BodyListener 6 | 7 | import scala.util.{Success, Try} 8 | 9 | private[lambda] class AwsBodyListener[F[_]: MonadError] extends BodyListener[F, LambdaResponseBody] { 10 | override def onComplete(body: LambdaResponseBody)(cb: Try[Unit] => F[Unit]): F[LambdaResponseBody] = cb(Success(())).map(_ => body) 11 | } 12 | -------------------------------------------------------------------------------- /serverless/aws/lambda-core/src/main/scala/sttp/tapir/serverless/aws/lambda/AwsServerOptions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.serverless.aws.lambda 2 | 3 | import sttp.tapir.server.interceptor.Interceptor 4 | 5 | case class AwsServerOptions[F[_]](encodeResponseBody: Boolean = true, interceptors: List[Interceptor[F]]) { 6 | def prependInterceptor(i: Interceptor[F]): AwsServerOptions[F] = copy(interceptors = i :: interceptors) 7 | def appendInterceptor(i: Interceptor[F]): AwsServerOptions[F] = copy(interceptors = interceptors :+ i) 8 | } 9 | -------------------------------------------------------------------------------- /serverless/aws/lambda-core/src/main/scala/sttp/tapir/serverless/aws/lambda/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.serverless.aws 2 | 3 | import scala.language.implicitConversions 4 | 5 | package object lambda { 6 | private[lambda] type LambdaResponseBody = (String, Option[Long]) 7 | 8 | type Route[F[_]] = AwsRequest => F[AwsResponse] 9 | } 10 | -------------------------------------------------------------------------------- /serverless/aws/lambda-core/src/main/scalajs/sttp/tapir/serverless/aws/lambda/js/AwsJsResponse.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.serverless.aws.lambda.js 2 | 3 | import sttp.tapir.serverless.aws.lambda.AwsResponse 4 | 5 | import scala.scalajs.js 6 | 7 | class AwsJsResponse( 8 | val statusCode: Int, 9 | val body: String, 10 | val isBase64Encoded: Boolean, 11 | val headers: js.Dictionary[String] = js.Dictionary(), 12 | val cookies: js.Array[String] = js.Array() 13 | ) extends js.Object 14 | 15 | object AwsJsResponse { 16 | def fromAwsResponse(r: AwsResponse): AwsJsResponse = { 17 | new AwsJsResponse( 18 | r.statusCode, 19 | r.body, 20 | r.isBase64Encoded, 21 | js.Dictionary(r.headers.toList: _*) 22 | ) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /serverless/aws/lambda-core/src/main/scalajs/sttp/tapir/serverless/aws/lambda/js/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.serverless.aws.lambda 2 | 3 | package object js { 4 | type JsRoute[F[_]] = AwsJsRequest => F[AwsJsResponse] 5 | } 6 | -------------------------------------------------------------------------------- /serverless/aws/terraform/src/main/scala/sttp/tapir/serverless/aws/terraform/model.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.serverless.aws.terraform 2 | 3 | import io.circe.Printer 4 | import io.circe.syntax._ 5 | import sttp.model.Method 6 | 7 | case class AwsTerraformApiGateway(routes: Seq[AwsApiGatewayRoute]) { 8 | def toJson(options: AwsTerraformOptions): String = { 9 | val encoders = AwsTerraformEncoders(options) 10 | import encoders._ 11 | Printer.spaces2.print(this.asJson) 12 | } 13 | } 14 | 15 | case class AwsApiGatewayRoute( 16 | name: String, 17 | path: String, 18 | httpMethod: Method 19 | ) 20 | -------------------------------------------------------------------------------- /tests/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %date [%thread] %-5level %logger{36} - %msg%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/src/main/scala/sttp/tapir/tests/Files.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.tests 2 | 3 | import sttp.tapir._ 4 | 5 | object Files { 6 | val in_file_out_file: PublicEndpoint[TapirFile, Unit, TapirFile, Any] = 7 | endpoint.post.in("api" / "echo").in(fileBody).out(fileBody).name("echo file") 8 | } 9 | -------------------------------------------------------------------------------- /tests/src/main/scala/sttp/tapir/tests/Test.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.tests 2 | 3 | import org.scalactic.source.Position 4 | import org.scalatest.Assertion 5 | 6 | import scala.concurrent.Future 7 | 8 | class Test(val name: String, val f: () => Future[Assertion], val pos: Position) 9 | object Test { 10 | def apply(name: String)(f: => Future[Assertion])(implicit pos: Position): Test = new Test(name, () => f, pos) 11 | } 12 | -------------------------------------------------------------------------------- /tests/src/main/scala/sttp/tapir/tests/TestUtil.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.tests 2 | 3 | import java.io.InputStream 4 | 5 | trait TestUtil extends TestUtilExtensions { 6 | def inputStreamToByteArray(is: InputStream): Array[Byte] = Iterator.continually(is.read).takeWhile(_ != -1).map(_.toByte).toArray 7 | } 8 | 9 | object TestUtil extends TestUtil 10 | -------------------------------------------------------------------------------- /tests/src/main/scala/sttp/tapir/tests/data/BasketOfFruits.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.tests.data 2 | 3 | import com.softwaremill.tagging.@@ 4 | import sttp.tapir.tests.data.BasketOfFruits._ 5 | 6 | case class BasketOfFruits(fruits: ValidatedList[ValidFruitAmount]) 7 | 8 | object BasketOfFruits { 9 | type ValidatedList[A] = List[A] @@ BasketOfFruits 10 | } 11 | -------------------------------------------------------------------------------- /tests/src/main/scala/sttp/tapir/tests/data/Color.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.tests.data 2 | 3 | sealed trait Color 4 | case object Blue extends Color 5 | case object Red extends Color 6 | 7 | case class ColorValue(color: Color, value: Int) 8 | 9 | case class ColorWrapper(color: Color) 10 | -------------------------------------------------------------------------------- /tests/src/main/scala/sttp/tapir/tests/data/CustomError.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.tests.data 2 | 3 | sealed trait CustomError 4 | object CustomError { 5 | case class Default(msg: String) extends CustomError 6 | object NotFound extends CustomError 7 | } 8 | -------------------------------------------------------------------------------- /tests/src/main/scala/sttp/tapir/tests/data/Entity.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.tests.data 2 | 3 | sealed trait Entity { 4 | def name: String 5 | } 6 | case class Person(name: String, age: Int) extends Entity 7 | case class Organization(name: String) extends Entity 8 | -------------------------------------------------------------------------------- /tests/src/main/scala/sttp/tapir/tests/data/Fruit.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.tests.data 2 | 3 | case class Fruit(f: String) 4 | -------------------------------------------------------------------------------- /tests/src/main/scala/sttp/tapir/tests/data/FruitAmount.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.tests.data 2 | 3 | case class FruitAmount(fruit: String, amount: Int) 4 | 5 | case class DoubleFruit(fruitA: String, fruitB: String) 6 | 7 | case class IntWrapper(v: Int) extends AnyVal 8 | 9 | case class StringWrapper(v: String) extends AnyVal 10 | 11 | case class ValidFruitAmount(fruit: StringWrapper, amount: IntWrapper) 12 | -------------------------------------------------------------------------------- /tests/src/main/scala/sttp/tapir/tests/data/FruitAmountWrapper.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.tests.data 2 | 3 | case class FruitAmountWrapper(fruitAmount: FruitAmount, notes: String) 4 | -------------------------------------------------------------------------------- /tests/src/main/scala/sttp/tapir/tests/data/FruitData.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.tests.data 2 | 3 | import sttp.model.Part 4 | import sttp.tapir.TapirFile 5 | 6 | case class FruitData(data: Part[TapirFile]) 7 | -------------------------------------------------------------------------------- /tests/src/main/scala/sttp/tapir/tests/data/FruitError.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.tests.data 2 | 3 | case class FruitError(msg: String, code: Int) extends RuntimeException 4 | -------------------------------------------------------------------------------- /tests/src/main/scala/sttp/tapir/tests/data/FruitErrorDetail.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.tests.data 2 | 3 | trait FruitErrorDetail 4 | object FruitErrorDetail { 5 | case class NameTooShort(length: Int) extends FruitErrorDetail 6 | case class Unknown(availableFruit: List[String]) extends FruitErrorDetail 7 | 8 | trait HarvestError extends FruitErrorDetail 9 | case class NotYetGrown(availableInDays: Int) extends HarvestError 10 | case class AlreadyPicked(name: String) extends HarvestError 11 | } 12 | -------------------------------------------------------------------------------- /tests/src/main/scala/sttp/tapir/tests/package.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir 2 | 3 | import cats.effect.IO 4 | 5 | package object tests { 6 | type Port = Int 7 | type KillSwitch = IO[Unit] 8 | } 9 | -------------------------------------------------------------------------------- /tests/src/main/scalajvm/sttp/tapir/tests/TestUtilExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.tests 2 | 3 | import java.io.{File, PrintWriter} 4 | 5 | import scala.concurrent.Future 6 | import scala.io.Source 7 | 8 | trait TestUtilExtensions { 9 | def writeToFile(s: String): File = { 10 | val f = File.createTempFile("test", "tapir") 11 | new PrintWriter(f) { write(s); close() } 12 | f.deleteOnExit() 13 | f 14 | } 15 | 16 | def readFromFile(f: File): Future[String] = { 17 | val s = Source.fromFile(f) 18 | try { 19 | Future.successful(s.mkString) 20 | } finally { 21 | s.close() 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/src/main/scalanative/sttp/tapir/tests/TestUtilExtensions.scala: -------------------------------------------------------------------------------- 1 | package sttp.tapir.tests 2 | 3 | import java.io.{File, PrintWriter} 4 | 5 | import scala.concurrent.Future 6 | import scala.io.Source 7 | 8 | trait TestUtilExtensions { 9 | def writeToFile(s: String): File = { 10 | val f = File.createTempFile("test", "tapir") 11 | new PrintWriter(f) { write(s); close() } 12 | f.deleteOnExit() 13 | f 14 | } 15 | 16 | def readFromFile(f: File): Future[String] = { 17 | val s = Source.fromFile(f) 18 | try { 19 | Future.successful(s.mkString) 20 | } finally { 21 | s.close() 22 | } 23 | } 24 | } 25 | --------------------------------------------------------------------------------