Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Jul 28 18:57
    mbore assigned #1357
  • Jul 28 18:14
    adamw commented #1399
  • Jul 28 18:13
    adamw opened #1399
  • Jul 28 18:13

    adamw on infer-validator-encode-to-raw

    #1398: infer the encode-to-raw … (compare)

  • Jul 28 14:01

    mergify[bot] on master

    Update client3:akka-http-backen… Merge pull request #1397 from s… (compare)

  • Jul 28 14:01
    mergify[bot] closed #1397
  • Jul 28 13:40
    kamilkloch opened #1398
  • Jul 28 13:16
    mergify[bot] labeled #1397
  • Jul 28 13:15
    scala-steward opened #1397
  • Jul 28 08:52
    geirolz commented #1395
  • Jul 28 08:52
    geirolz closed #1395
  • Jul 28 08:52
    geirolz commented #1395
  • Jul 28 07:50
    geirolz closed #1383
  • Jul 28 07:49
    geirolz commented #1383
  • Jul 27 19:54

    mergify[bot] on master

    Update zio, zio-streams, zio-te… Merge pull request #1396 from s… (compare)

  • Jul 27 19:54
    mergify[bot] closed #1396
  • Jul 27 19:09
    mergify[bot] labeled #1396
  • Jul 27 19:08
    scala-steward opened #1396
  • Jul 27 15:59
    geirolz edited #1395
  • Jul 27 15:57
    geirolz opened #1395
Denis Novac
@DenisNovac

Is it possible to create schema for Map type?

This code says there is no implicit Schema:

val endpoint
      : Endpoint[Unit, (StatusCode, EndpointError), Map[MyIdClass, List[Description]], Any] =
    baseEndpoint.get
      .in("test")
      .out(jsonBody[Map[MyIdClass, List[Description]]])

Every custom type has Schema in companion. For example, this type works good:

List[(MyIdClass, List[Description])]
1 reply
Vladimir Bodnartchouk
@Fraer
Hello, i would like to use tapir with https://github.com/RustedBones/akka-http-metrics
is there a custom way to specify the path ? In my case i would like to declare an Endpoint but use pathLabeled directive to let akka-http-metrics create prometheus metrics.
1 reply
Denis Novac
@DenisNovac

Hi everyone. Is it possible to get schemaType for some T from ADT with schema? Something like this kind function perhaps (this one does not work)?

https://scastie.scala-lang.org/XtuqptggQ36yJ9Dt4gSuvA

import sttp.tapir.{Schema, SchemaType}
import sttp.tapir.generic.{Derived, SchemaDerivation}


sealed trait Animal extends SchemaDerivation

object Animal extends SchemaDerivation {


  implicit val schema = Schema.derived[Animal]

  final case class Cat(name: String) extends Animal
  final case class Dog(name: String) extends Animal
  final case class Horse(name: String, speed: Float) extends Animal

}


def kind[T <: Animal](t: T): SchemaType = Schema.derived[T].schemaType
3 replies
Denis Novac
@DenisNovac

Hi everyone. Is it possible to get schemaType for some T from ADT with schema? Something like this kind function perhaps (this one does not work)?

Also it seems that Schema.derived is enough. I though it takes some implicits from SchemaDerivation. What is the difference between Schema.derived and SchemaDerivation mixing?

5 replies
mvillafuertem
@mvillafuertem

Hi, my errors code looks like this.

sealed trait ApiError { self =>


  val code = self match {
    case ApiError.UnauthorizedError(_) => 401
    case ApiError.NotFoundError(_) => 404
    case ApiError.BadRequestError(_) => 400
  }

}

object ApiError {

  case class UnauthorizedError(reason: String) extends ApiError

  case class NotFoundError(message: String) extends ApiError

  case class BadRequestError(reason: String) extends ApiError

}

I'm getting this result

HTTP/1.1 400 BadRequest
Content-Length: 14
Content-Type: application/json
Date: Thu, 14 Jan 2021 18:03:45 GMT
Server: akka-http/10.2.2

{
    "reason": ""
}

Is there a way to properly serialize inherited parameters? Do I have to create a custom decoder and encoder?

31 replies
Denis Novac
@DenisNovac
Hi everyone. Getting this error while migrating to 0.17:
java.lang.ExceptionInInitializerError:
Cause: java.lang.IllegalArgumentException: Validator reference is already set to Product(Map(filter -> sttp.tapir.generic.internal.SchemaMagnoliaDerivation$$anon$1@7442d39b))!
at sttp.tapir.Validator$Ref.set(Validator.scala:228)
at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.$anonfun$combine$2(SchemaMagnoliaDerivation.scala:33)
at scala.Option.foreach(Option.scala:407)
at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.$anonfun$combine$1(SchemaMagnoliaDerivation.scala:33)
at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.withProgressCache(SchemaMagnoliaDerivation.scala:76)
at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.combine(SchemaMagnoliaDerivation.scala:16)
at sttp.tapir.generic.internal.SchemaMagnoliaDerivation.combine$(SchemaMagnoliaDerivation.scala:15)
at sttp.tapir.Schema$.combine(Schema.scala:99)
I guess somewhere i have two schemas for my type or something?
2 replies
I just write val x = MyType(...) and this happens in runtime. No compile errors
Denis Novac
@DenisNovac
Actually MyType does not even have a schema, it is only for internal usage.
Denis Novac
@DenisNovac

Hi everyone. I just noticed that all my docs routes have index.html page with Swagger Petstore (default Swagger example). E.g.:

My OpenAPI documentation: localhost:8080/api/v1/docs
Swagger Petstore: localhost:8080/api/v1/docs/index.html

And so on every controller.

How do you disable it?

1 reply
jjcapper
@jjcapper
Morning all, just trying to use tapir for the first time (with http4s) and I've come a bit unstuck handling errors. In my current code base, I have a function along the lines of def handleResponse[Resp: Encoder](resp: Either[MyError, Resp]): IO[Response[IO]] = ... which I use to handle different instances of MyError (it's a trait with some case classes) and transform them into Responses. I can't figure out how to achieve this in tapir. I've had a look at the documentation, but neither errorOut nor the approach to just recover from the errors quite fits. Any advice very much appreciated
2 replies
(The problem appears to be that Http4sServerInterpreter.toRoutes handles the mapping from Either[MyError, Resp] to IO[Response[IO]] automatically)
Josip Grgurica
@jkobejs
8 replies
Matthew Tovbin
@tovbinm
Hi folks. Is there a way to attach documentation to request/response types for the API, e.g. case class Book(/*This is a title of the book*/ title: String)?
6 replies
Gopal Shah
@gopalsaob

Hi all, I am looking to have an api with query parameters having defaults values, similar to https://doc.akka.io/docs/akka-http/current/routing-dsl/directives/parameter-directives/parameters.html

"color".withDefault("red")

Does Tapir support this?

5 replies
Thijs Broersen
@ThijsBroersen
I have an issue with using and endpoint as a client. The endpoint returns a 307 http status code (server encoding works fine) but I get a decoding failure. When I remove the 307 status, this line .out(statusCode(StatusCode.TemporaryRedirect)), the client can correctly decode. Should I do this differently? Is this a bug?
Thijs Broersen
@ThijsBroersen
I suspect the client is following the redirect ..
Thijs Broersen
@ThijsBroersen
Tried set false to any redirect, does not help. I only see exhausted input as decode result error. Doing a manual request on the API I do get what I expect.
Thijs Broersen
@ThijsBroersen
building the request by hand (disabling redirects) does work basicRequest.followRedirects(false)
2 replies
Srepfler Srdan
@schrepfler
where are we with the asyncapi work? I think I've found an interesting use case in Lagom, for example they have some servicec endpoints which are stream on websocket but they also generate a kafka stream for the events that are generated. It would be really cool if one Tapir definition would generate both the Play endpoints (stream and non stream) but also the kafka events. In the Lagom API also there's this compositional step Command -> Event which is also interesting
2 replies
Assen Kolov
@kolov_twitter
How can I access the hashtag value in a request like GET /cognito/login#token=123&type=Bearer ? I don't have much control over the request, I want to handle it as it is. The hashtag value seems to be lost in the ServerRequest instance.
Assen Kolov
@kolov_twitter
Aha: fragment (#) is not sent to the server at all, must be handled in browser :-)
Pouria Mellati
@pmellati
Hi all, is there a way to specify a namespaced name for schema objects? For instance, if endpoint one returns my.scala.packageone.User and endpoint two returns my.scala.packagetwo.User, how can I ensure that the two versions of User are represented with correct package / namespace names?
3 replies
Pascal Danek
@pascaldanek

Hello guys - bravo for this extraordinary project it has changed my life in coding Rest APIs in Scala :) I come here because I have a problem with having Scala built in enumerations schema in my openapi doc.

Here is a code snippet that reproduces the issue (mainly coming fro the documentation):

import sttp.tapir._
import sttp.tapir.generic.auto._
import sttp.tapir.json.circe.jsonBody
import io.circe.generic.auto._
import sttp.tapir.docs.openapi.OpenAPIDocsInterpreter
import sttp.tapir.openapi.circe.yaml._

trait EnumHelper { e: Enumeration =>
  import io.circe._

  implicit val enumDecoder: Decoder[e.Value] = Decoder.decodeEnumeration(e)
  implicit val enumEncoder: Encoder[e.Value] = Encoder.encodeEnumeration(e)

  implicit val schemaForEnum: Schema[e.Value]       = Schema.string
  implicit def validatorForEnum: Validator[e.Value] = Validator.`enum`(e.values.toList, v => Option(v))
}

object Color extends Enumeration with EnumHelper {
  type Color = Value
  val Blue = Value("blue")
  val Red  = Value("red")
}

case class Drawing(color: Color.Value)

val ep = endpoint.in(jsonBody[Drawing])

Now I generate the openapi documentation with: println(OpenAPIDocsInterpreter.toOpenAPI(ep, "My Project", "1.0").toYaml)

And here is what I get:

openapi: 3.0.3
info:
  title: My Project
  version: '1.0'
paths:
  /:
    get:
      operationId: getRoot
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Drawing'
        required: true
      responses:
        '200':
          description: ''
components:
  schemas:
    Drawing:
      required:
      - color
      type: object
      properties:
        color:
          type: string

So as you can see, no enumeration values.

Do you have an idea about what's going on here ?

Thanks in advance for your help.

7 replies
Pascal Danek
@pascaldanek

Also I have a second problem with the use of serverLogic on my endpoints. The compiler complains about a type issue, but I guess it is a problem of inference maybe you can help me there. Here is how to reproduce:

import io.circe.generic.auto._
import monix.eval.Task
import sttp.tapir._
import sttp.tapir.docs.openapi.OpenAPIDocsInterpreter
import sttp.tapir.generic.auto._
import sttp.tapir.json.circe.jsonBody

case class Foo(value: String)

val ep1 = endpoint.out(stringBody).in(jsonBody[Foo]).serverLogic[Task] { _ =>
  Task(Right("Ok"))
}

val ep2 = endpoint.out(stringBody).in("foo")

val eps = Seq(ep1, ep2)

println(OpenAPIDocsInterpreter.toOpenAPI(eps, "My Project", "1.0").toYaml)
overloaded method toOpenAPI with alternatives:
(es: Iterable[sttp.tapir.Endpoint[_, _, _, _]],title: String,version: String)(implicit options: sttp.tapir.docs.openapi.OpenAPIDocsOptions): sttp.tapir.openapi.OpenAPI <and>
[I, E, O, S, F[_]](se: sttp.tapir.server.ServerEndpoint[I,E,O,S,F], title: String, version: String)(implicit options: sttp.tapir.docs.openapi.OpenAPIDocsOptions): sttp.tapir.openapi.OpenAPI <and>
[I, E, O, S](e: sttp.tapir.Endpoint[I,E,O,S], title: String, version: String)(implicit options: sttp.tapir.docs.openapi.OpenAPIDocsOptions): sttp.tapir.openapi.OpenAPI
cannot be applied to (Seq[Product with sttp.tapir.EndpointInfoOps[_ >: Unit with Foo, Unit, String, Any] with sttp.tapir.EndpointMetaOps[_ >: Unit with Foo, Unit, String, Any] with java.io.Serializable], String, String)
println(OpenAPIDocsInterpreter.toOpenAPI(eps, "My Project", "1.0").toYaml)

Note that as soon as I am starting to use serverLogic I cannot concatenate anymore my endpoints. The compiler complains:

type mismatch;
 found   : Seq[sttp.tapir.Endpoint[_ >: Unit with String, (ilogyou.web.endpoints.EndpointError, sttp.model.StatusCode), _ >: Unit with ilogyou.modules.LoginResponse, Any]]
 required: scala.collection.IterableOnce[Product with sttp.tapir.EndpointInfoOps[...

Thanks again for your help.

3 replies
Oleksandr Shtykhno
@Yyukan

hi, I need to use

Endpoint[SomeCaseClass, SomeErrorInfo, ZioStreams.BinaryStream, ZioStreams]

but it looks like integration for ZIO streams and Http4s is not implemented yet, I would appreciate any tip to the right direction

4 replies
Pascal Mengelt
@pme123

Hi, I use Tapir to generate Open API. I could not figure out how to set a default value for a path variable.
This is my path:

...
.in(("process-definition" / "key" / path[String]("processDefinitionKey") / "tenant-id" / path[String]("tenant-id") / "execute"))

The output should be like:

      - name: tenant-id
        in: path
        required: true
        schema:
          type: string
          default: myTenant
4 replies
Timofey
@GusevTimofey

Hi there! I have such a situation (input data is in url-encoded format):

case class Foo(foo: Option[String])
case class Bar(bar: Option[Foo])

implicit def fooCodec: Codec[Option[String], Foo, CodecFormat.TextPlain] =
  Codec.option[String, String, CodecFormat.TextPlain]
    .map(Foo(_))(_.foo)

def func: Endpoint[Bar, Unit, String, Any] = endpoint
  .post
  .in("bar")
  .in(formBody[Bar])

And compiler says to me the next:


exception during macro expansion:
scala.reflect.macros.TypecheckException: Cannot find a codec between types: List[String] and Option[Foo], formatted as: sttp.tapir.CodecFormat.TextPlain.
Did you define a codec for: Option[Foo]?
Did you import the codecs for: sttp.tapir.CodecFormat.TextPlain?


What did I do wrong?

2 replies
bauerflo
@bauerflo

bauerflo @bauerflo 11:34
Hi! I believe there might be something wrong with the tapir backend stub tests
my endpoint looks like this

val destinationEndpoint = endpoint
.in(bearerToken)
.in(traceId)

val postDestination = destinationEndpoint.post
.in("companies" / path[TenantId] / "destinations")
.in(jsonBody[Destination])
.out(jsonBody[DestinationResponse])

and my stubs:

SttpBackendStub
.apply[Future, Capabilities]
.whenInputMatches(Endpoints.postDestination)(_._4 == destination)
.thenSuccess(DestinationResponse(IDs.destinationId))

my destination definitely goes in here (in debugging)

SttpClientInterpreter
.toRequest(
postDestination,
uri"https://${settings.baseUri}".some
)
.apply((token, traceId, tenantId, destination))

the input in the stub however takes all parameters correctly but the destination is null (so, the stub works if I match on _._4 == null)

I noticed in debugging that req below contains all input parameters and the last one is a StringBody(...) containing my (correctly serialized) destination object as json

def whenInputMatchesI, E, O(inputMatcher: I => Boolean): TypeAwareWhenRequest[I, E, O] = {
new TypeAwareWhenRequest(
endpoint,
new stub.WhenRequest(req =>
DecodeInputs(endpoint.input, new SttpDecodeInputs(req)) match {
case _: DecodeInputsResult.Failure => false

however it checks out in DecodeInputsResult.Failure

4 replies
Pascal Danek
@pascaldanek
Hello guys, I am desperately trying to build a ServerEndpoint returning a fs2 stream of json objects on http4s. Can someone point me to some examples based on the latest tapir version ? The string based doc examples does not allow me to understand how I am supposed to proceed. There are also conflicting examples on the internet giving different streamBody definitions. Lets say I have a case class Person(name: String), a service def getPersons: fs2.Stream[Task, Person]. What is the corresponding .out(streamBody(???)) and how in the serverLogic i can transform this type into something accepted by Tapir on http4s ? Thanks for any help !
5 replies
tayvs
@tayvs
Hi @adamw Do you plan some work regarding to processing multipart files in stream matter?
My case is next, I tring to process big csv file in reactive-like manner and now I oparsing multipart body manually. So it would be great to add such possibility. What do you think about this?
1 reply
Henry
@hygt

Hello, I'm trying to define a custom schema mostly by hand, but the generated OpenAPI doesn't show the enum values for my field:

implicit val myResponseSchema: Schema[MyResponse] = Schema(
  SProduct(
    SObjectInfo("MyResponse"),
    List(
      FieldName("someEnum") -> schemaForEnumEntry[SomeEnum]
        .description("A description of the enum")
        .encodedExample(SomeEnum.Foo),
      FieldName("someNumber") -> Schema.schemaForDouble
    )
  ),
  description = Some("Custom description of the response")
)

I'm using the tapir-enumeratum integration. If I stick to auto derivation, it works. If I compare both auto and my own schema objects, they look similar (save for the description and example). I'm a bit puzzled.

Mark
@Fristi
Hi!
Impressive work with tapir
Was wondering if Schema works well with polymorphic and higher kinded case classes?
1 reply
Mateusz Wójcik
@matwojcik

Hi guys, I'm playing with custom schema, I wanted to add additional validations to some nested NonEmptyList (e.g. max size):

case class Wrapper(items: NonEmptyList[String])

    implicit val customWrapperSchema: Schema[Wrapper] = implicitly[Derived[Schema[Wrapper]]]
      .value
      .modify(_.items)(_.validate(Validator.maxSize[String, List](1).contramap(_.toList)).description("Some description"))

    val yaml = OpenAPIDocsInterpreter.toOpenAPI(endpoint.out(jsonBody[Wrapper]), Info("Fruits", "1.0")).toYaml

but this max size gets ignored:

  schemas:
    Wrapper:
      required:
      - items
      type: object
      properties:
        items:
          type: array
          items:
            type: string
          description: Some description
          minItems: 1

am I doing something wrong or is it some kind of bug?

3 replies
Roman Makurin
@Ravenow
hello guys, i have a strange question, but is possible on tapir level to modify response(put headers for example) - something like .in(extractFromRequest()) ?
Roman Makurin
@Ravenow

i have code something like this

  endpoint
    .in("api" / "v1" / "do")
    .in(auth.apiKey(TokenHeader).securitySchemeName(TokenHeaderName))
    .in(headers)
    .serverLogicForCurrentRecoverErrors((authBridge.auth _).tupled.andThen(handleError[F, AuthInfo[F]]))
    .post
    .in(query[String]("id"))
    .in(jsonBody[Json])
    .out(jsonBody[Json])
    .serverLogicRecoverErrors(service.doWork.andThen(handleError[F, Json]))

but now i need to add headers to response back, what is the prefered way of doing this? I understand how to extract all needed info from request, but how to put back any calculated header?

Im in stuck now :)

1 reply
Mateusz Wójcik
@matwojcik
Is there any way to add a section in OpenAPI for 400 Bad Request error which is by default returned for validation errors? My endpoint by itself does not have any error output, and I'd like to document in OpenAPI that 400 is returned when this and that validation on schema fails.
12 replies
Gabriel Asman
@Qthunder

Hello, I've been working a Tapir API solution for my API and I find myself having a small issue. I had to define a custom schema for a few of my classes where auto-generation wouldn't suffice. But then I noticed that the validations my class's fields (in my case 1 field, an enum) was not showing up in the documentation. After going in deep with the debugger I found the place where that information is "lost"

On this line, rather the recursive call to apply being passed fieldSchema.validator as I might've expected, it instead uses the fieldValidatormethod to construct a new validator from the "parent"'s (current object's) validator. My first question is why is this so? My next question is what do I need to change in the parent validator in order to get my validator where I want it to. Looking at the logic of fieldValidator it looks like an Enum validator like the one I did would get discarded during the collect{ ... } pattern match.

                fieldName.encodedName -> apply(TypeData(fieldSchema, fieldValidator(typeData.validator, fieldName.name)))

https://github.com/softwaremill/tapir/blob/856570a33c82cf68b8b63e7e0592913bbd547943/docs/apispec-docs/src/main/scala/sttp/tapir/docs/apispec/schema/TSchemaToASchema.scala#L27

Let me know if this makes sense and thanks in advance for your time!

Ooh, just scrolled up a bit and might be the same issue as @matwojcik 's softwaremill/tapir#1047
Thijs Broersen
@ThijsBroersen
Is there any way I can capture paths (arbitrary dept) until a certain path/pattern matches?
Thijs Broersen
@ThijsBroersen
e.g. endpoint.in(paths).in("fixed-path-key") client request builds a correct path from an arbitrary list of strings and ends with 'fixed-path-key'. But the backend endpoint never matches the request.
10 replies
Liara
@kiritsuku

Hello, is it possible to upload (stream) files of any size with arbitrary content type? The example on the website (https://tapir.softwaremill.com/en/latest/endpoint/streaming.html) doesn't work for me:

endpoint.out(streamBody(AkkaStreams)(Schema(Schema.derived[List[Person]].schemaType), CodecFormat.Json()))

In play2, there is the HttpEntity.Chunked type:

      def contentType = file.contentType.getOrElse("application/octet-stream")
      def chunks = GridFSStreams(gfs).source(file).map(HttpChunk.Chunk(_))

      Result(
        header = ResponseHeader(OK),
        body = HttpEntity.Chunked(chunks, Some(contentType))
      ).as(contentType)

But I can not replicate the functionality on tapir side since streamBody requires a fixed codec format. Can I somehow avoid specifying the codec format or maybe at least specify a list of codec types?

13 replies
Gabriel Asman
@Qthunder
MatchType derivation seems to not work for Sets ? Rather confusing as it seems like it should be based on Magnolia examples and stuff? Tried to dive into the Magnolia code to understand where it's supposed to be derived (or where the List equivalent is) but it was too complicated for me. Anyone encountered this/have a work around (other than just switching to list, which I might end up doing)
  final case class X(a: Set[String])
  val w                                        = MatchType.gen[X]
2 replies
magnolia: could not find MatchType.Typeclass for type Set[String]
    in parameter 'a' of product type TapirRoutes.this.X

  val w                                        = MatchType.gen[X]
Gabriel Asman
@Qthunder
Another one from me - is there any way documentation for built for stream (input) bodies?
This is what I have right now to set up the streaming endpoint (using fs2-circe), I think it "works" but the documentation produced is not very useful. Is there a way to incorporate the actual schema of MyType in the documentation too?
      .in(streamBody(Fs2Streams[IO])(Schema.binary, CodecFormat.Json(), StandardCharsets.UTF_8.some))
      .serverLogicForCurrent(
        _.through(byteStreamParser)
          .through(decoder[IO, MyType])
          .asRight
          .pure[IO]
      )
2 replies
Liara
@kiritsuku
In play, there exists RemoteConnection, as part as the RequestHeader class. Is there a comparable way to get access to the raw request in tapir? I would need the IP address and the protocol (http, https) of the request for example.
2 replies
Blaž Marinović
@bmarinovic
hi, is it possible to render Cats' NonEmptyList[T] as regular List[T] in OpenApi generator? Currently, for Nel[Int], I'm getting it as:
'#/components/schemas/NonEmptyList_Int'

    NonEmptyList_Int:
      required:
      - head
      type: object
      properties:
        head:
          type: integer
        tail:
          type: array
          items:
            type: integer
1 reply
ybasket
@ybasket:matrix.org
[m]
@bmarinovic: The following snippet should do that (have no good setup for testing right now, but works fine with very similar code for NonEmptyMap):
  implicit def schemaForNonEmptyList[V](implicit s: Schema[List[V]]): Schema[NonEmptyList[V]] =
    s.map(NonEmptyList.fromList)(_.toList)