Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • 23:41
    scala-steward opened #1241
  • 23:41
    scala-steward opened #1240
  • 19:25
    mbore edited #1239
  • 19:25
    mbore opened #1239
  • 19:24

    mbore on adapt-schema-derivation

    modify schema macro for Scala3 (compare)

  • 19:08

    mergify[bot] on master

    Update upickle to 1.3.13 Merge pull request #1236 from s… (compare)

  • 19:08
    mergify[bot] closed #1236
  • 19:08

    mergify[bot] on master

    Update shared:akka, shared:core… Merge pull request #1237 from s… (compare)

  • 19:08
    mergify[bot] closed #1237
  • 18:39
    scala-steward opened #1238
  • 18:39
    scala-steward opened #1237
  • 18:39
    scala-steward opened #1236
  • 15:50
    kubinio123 synchronize #1226
  • 15:49

    kubinio123 on tapir-aws

    adjustments and docs (compare)

  • 14:28

    mergify[bot] on master

    Update refined to 0.9.25 Merge pull request #1235 from s… (compare)

  • 14:28
    mergify[bot] closed #1235
  • 13:51
    scala-steward opened #1235
  • 11:17
    scala-steward opened #1234
  • 10:11
    kubinio123 synchronize #1226
  • 10:11

    kubinio123 on tapir-aws

    runnable example (compare)

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 de Jong
@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
SpaceRaven
@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()) ?
SpaceRaven
@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)
Łukasz Biały
@lbialy
hi, I'm trying to add tracing and metrics to an akka-http based server app that uses tapir. is there any established way to do this? I've tried wrapping resulting Route but that doesn't really work if the route doesn't match (timers start before route is evaluated). I have therefore moved my monitoring code to wrap the function passed to serverLogic method and this provides useful metrics but I can't gather data such as the resulting http code or the actual http path from hostname. Any ideas?
8 replies
Blaž Marinović
@bmarinovic
@ybasket:matrix.org thanks, just what I needed! it works :)
Anders Schwartz
@adschwartz
Hi. Is there a way to turn off the "try it out” feature that the Swagger UI launches with through tapir? I’m serving it on http4s.
Looks like the Swagger UI itself supports this feature but I wasn’t able to find a way to configure that trough tapir: https://github.com/swagger-api/swagger-ui/issues/3725#issuecomment-654284336
1 reply
Adam Warski
@adamw

A proposal to add endpoint / request interceptors and more flexible server interpreter callbacks: softwaremill/tapir#1065

Opinions and suggestions welcome - thanks! :)

Pascal Danek
@pascaldanek
Hello, I have an email case class in my application: case class EmailAddress(value: String). I use it in my endpoint as a simple string. While I was able to do it using custom tapir codecs, i cannot get the right schema in the documentation. For now I just did val emailSchema = Schema.string but in the swagger I stil see "email": {"value": "email"}. Any idea ?
2 replies
perok
@perok:matrix.org
[m]
@pascaldanek: Use case class EmailAddress(value: String) extends AnyVal https://scastie.scala-lang.org/IncOWYTPS22Mb8srSeF75Q
1 reply
julbarg
@julbarg
Hi everyone what is the best way to use ADTs on Tapir to define enums?
1 reply
Marek Kadek
@KadekM
in(jsonBody[Option[Json]) fails on no body present with Invalid value for: body (exhausted input), is there way to achieve it ?
3 replies
Michał Kobzik
@mkobzik
Hi, is there any way to transform ServerEndpoint[Unit, Nothing, Unit, Fs2Streams[F] with WebSockets, F] into ServerEndpoint[Unit, Nothing, Unit, Fs2Streams[G] with WebSockets, G]? I've tried using imapK(fToG)(gToF), but it changes only effect type, leaving Fs2Streams untouched ServerEndpoint[Unit, Nothing, Unit, Fs2Streams[F] with WebSockets, G]
1 reply
Jonathan Neufeld
@jonathan-neufeld-asurion
Hi, I am new to both Tapir and Gitter, is this the right place to ask questions and is there a good set of working examples I can use to better-understand how the declarative syntax works?
1 reply
Jens Grassel
@jan0sch
Hi all, if we have a list of endpoints, would it be possible for the compiler to issue warning if some of them trigger on the same path and method?
Theoretically we could parse them and check it ourselfes but having this automatically would be awesome. Especially if projects grow over time.
2 replies
johnhungerford
@johnhungerford

I have found that adding .validate(Validator.Min(0, true)) to a field when defining a schema does not propagate to the openapi spec that I generate. Example:

// Code
case class TestClass( @validate( Validator.Min(0, false ) ) intVal : Int )

val myEndpoint = endpoint.get.in( jsonBody[ TestClass ] )

// Resulting open api spec
   TestClass:
      required:
      - intVal
      type: object
      properties:
        intVal:
          type: integer

Is this expected?

I get the same result when I define a schema manually
johnhungerford
@johnhungerford
Looks like my issue is addressed here: softwaremill/tapir#1051
3 replies
Dmitriy Kovalenko
@dkovalenko
Hi all. Is there any way to map input from query params into case class with more than 22 fields?
1 reply
Maybe some kind of intermediate map/list from tuple? I don't want to split my domain class to 2-3 classes just to make tapir work.
Jonathan Neufeld
@jonathan-neufeld-asurion
Hello, how does one express documentation for specific status return codes that have no body? I made a helper like this: EndpointIO.Empty(Codec.idPlain(), EndpointIO.Info.empty).map(_ => obj)(_ => ()).description("my description") but it doesn't work and isn't documented.
1 reply
heksesang
@heksenlied:matrix.org
[m]
Is it possible to define multiple CodecFormat for the output of an endpoint, in the case that it might return one of multiple content-types depending on some query parameters or headers?
1 reply
Gilad Hoch
@hochgi

Hi,
I'm using tapir, and really liking it so far. But there's something I find a bit odd in the default behaviour of endpoint input, but maybe I'm not using it correctly?
Anyway, the usecase is simple. consider some simple endpoint like:

val getUsers: Endpoint[Int, String, String, Any] = {
  val userId = path[Int].description("The user ID is a 32-bit signed integer")
  endpoint
    .name("get user")
    .get
    .in("api" / "users" / userId)
    .errorOut(stringBody)
    .out(stringBody)
}

I would expect that a call like: GET <host>/api/users/foo would result with something like:
400 BadRequest : 'foo' is not a valid Int
instead we get:
500 Internal Server Error : There was an internal server error.

Looking at the code, I see that path has a context bound for a codec, which I guess is always this:

implicit val int: Codec[String, Int, TextPlain] = stringCodec[Int](_.toInt).schema(Schema.schemaForInt)

and in the implementation, it's just string codec mapped with _.toInt, leaving the validator as Validator.pass.

So this seems a bit weird to me, since clearly what we want in such a case is to validate the primitives properly, thus returning 400, and not 500 in case of invalid input.

  1. Is there anything I missed? Is there an easy fix?
  2. If not, would you like me to open an issue for that?

Thanks!

13 replies
Doug Clinton
@DougC
I have a case class for which I have a custom circe encoder that adds an extra field to the generated json. How can I modify the schema to add a description of the extra field?
5 replies
Thijs Broersen
@ThijsBroersen
I upgraded Tapir from 0.17.9 to 0.17.19 and now RedocHttp4s is broken. I get a 308 when I want to view the rendered docs. Could not find any related (bug-)ticket in github. I see some changes in the source-code but cannot find any migration instructions. Anyone got the same issue?
Thijs Broersen
@ThijsBroersen
turns out contextPath cannot be Nil or an empty String
6 replies
also RedocHttp4s and SwaggerHttp4s have different implementations (e.g. contextPath is a List[String] for the first and String for the latter)
sken
@sken77
would adding a server interpreter with zio-http be considered?