softwaremill / tapir

Rapid development of self-documenting APIs
https://tapir.softwaremill.com
Apache License 2.0
1.36k stars 419 forks source link

[BUG] No implicit arguments of type: Codec[List[String], Provider.Value, CodecFormat.TextPlain] #2572

Closed abteilung6 closed 1 year ago

abteilung6 commented 1 year ago

Having an enum called Provider which is quite the same like in the docs

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.enumeration(e.values.toList, v => Option(v))
}

object Provider extends Enumeration with EnumHelper {
  type ProviderValue = Value
  val Credentials: dto.Provider.Value = Value("credentials")
  val Ldap: dto.Provider.Value = Value("ldap")

  object Implicits {
    implicit val providerDecoder: Decoder[Provider.Value] = Decoder.decodeEnumeration(Provider)
    implicit val providerEncoder: Encoder[Provider.Value] = Encoder.encodeEnumeration(Provider)
  }
}

I want to have an endpoint

  val signInEndpoint: PublicEndpoint[(ProviderValue, SignInRequest), ResponseError, AuthResponse, Any] =
    endpoint.post
      .tag(tag)
      .description("Sign in with a provider")
      .in(basePath / "signin")
      .in(query[ProviderValue]("provider")).  // <---- Typing error
      .in(jsonBody[SignInRequest])
      .errorOut(jsonBody[ResponseError])
      .out(jsonBody[AuthResponse])

But get following error: No implicit arguments of type: Codec[List[String], Provider.Value, CodecFormat.TextPlain]

abteilung6 commented 1 year ago

I just moved to enumeratum and it works.

adamw commented 1 year ago

FYI, if you are using latest tapir, as per https://tapir.softwaremill.com/en/latest/endpoint/enumerations.html when using an enumeration inside a query param, this works:

import sttp.tapir._

object Provider extends Enumeration {
    type ProviderValue = Value
    val Credentials: Provider.Value = Value("credentials")
    val Ldap: Provider.Value = Value("ldap")
}

val signInEndpoint: PublicEndpoint[Provider.ProviderValue, Unit, Unit, Any] =
    endpoint.post.in(query[Provider.ProviderValue]("provider"))
abteilung6 commented 1 year ago

Alternative with enumeratum package

package org.abteilung6.ocean
package repositories.dto

import enumeratum._
import sttp.tapir.codec.enumeratum.TapirCodecEnumeratum

sealed abstract class AuthenticatorType(override val entryName: String) extends EnumEntry

object AuthenticatorType extends Enum[AuthenticatorType] with TapirCodecEnumeratum with CirceEnum[AuthenticatorType] {

  val values: IndexedSeq[AuthenticatorType] = findValues

  case object Directory extends AuthenticatorType("directory")

  case object Credentials extends AuthenticatorType("credentials")
}

Endpoint could look like

  val signInEndpoint: PublicEndpoint[(AuthenticatorType, SignInRequest), ResponseError, AuthResponse, Any] =
    endpoint.post
      .tag(tag)
      .description("Sign in with an authenticator")
      .in(this.withSubEndpoint("signin"))
      .in(query[AuthenticatorType]("authenticator"))
      .in(jsonBody[SignInRequest])
      .errorOut(jsonBody[ResponseError])
      .out(jsonBody[AuthResponse])

  def signInLogic(
    authenticatorType: AuthenticatorType,
    signInRequest: SignInRequest
  ): Future[Either[ResponseError, AuthResponse]] = ???

But needs some other dependencies when working with slick.