All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tamer.config.scala Maven / Gradle / Ivy

package tamer

import java.nio.charset.StandardCharsets
import java.time.{Duration => JDuration}
import java.util.Base64

import zio._

sealed trait RegistryAuthConfig extends Product with Serializable
object RegistryAuthConfig {
  final case class Basic(userInfo: String) extends RegistryAuthConfig
  object Basic {
    def apply(userInfo: String): Basic                   = new Basic(Base64.getEncoder.encodeToString(userInfo.getBytes(StandardCharsets.UTF_8)))
    def apply(username: String, password: String): Basic = new Basic(s"$username:$password")
  }
  final case class Bearer(token: String) extends RegistryAuthConfig
  val config: Config[Option[RegistryAuthConfig]] =
    (Config.secret("user_info").optional ++
      Config.secret("username").optional ++
      Config.secret("password").optional ++
      Config.secret("token").optional).mapOrFail {
      case (None, None, None, None)                     => Right(None)
      case (Some(userInfo), None, None, None)           => Right(Some(Basic(userInfo.value.asString)))
      case (None, Some(username), Some(password), None) => Right(Some(Basic(username.value.asString, password.value.asString)))
      case (None, None, None, Some(token))              => Right(Some(Bearer(token.value.asString)))
      case _ =>
        Left(
          Config.Error.InvalidData(message =
            "When auth is configured you must specify one of these three options (mutually exclusive): user_info (Basic auth), username and password pair (Basic auth) or token (Bearer auth)"
          )
        )
    }
}

final case class RegistryConfig(url: String, cacheSize: Int, expiration: JDuration, maybeRegistryAuth: Option[RegistryAuthConfig])
object RegistryConfig {
  def apply(url: String): RegistryConfig = RegistryConfig(
    url = url,
    cacheSize = 4,
    expiration = 1.hour,
    maybeRegistryAuth = None
  )
  val config: Config[Option[RegistryConfig]] =
    (Config.string("url") ++
      Config.int("cache_size").withDefault(4) ++
      Config.duration("expiration").withDefault(1.hour) ++
      RegistryAuthConfig.config.nested("auth")).map { case (url, cacheSize, expiration, maybeRegistryAuth) =>
      RegistryConfig(url, cacheSize, expiration, maybeRegistryAuth)
    }.optional
}

final case class TopicOptions(partitions: Int, replicas: Short, compaction: Boolean)
object TopicOptions {
  def config(compactionDefault: Boolean): Config[Option[TopicOptions]] =
    (Config.boolean("auto_create").withDefault(false) ++
      Config.int("partitions").withDefault(1) ++
      Config.int("replicas").map(_.toShort).withDefault(1.toShort) ++
      Config.boolean("compaction").withDefault(compactionDefault)).map {
      case (true, partitions, replicas, compaction) => Some(TopicOptions(partitions, replicas, compaction))
      case _                                        => None
    }
}

final case class TopicConfig(topicName: String, maybeTopicOptions: Option[TopicOptions])
object TopicConfig {
  def apply(topicName: String): TopicConfig = new TopicConfig(
    topicName = topicName,
    maybeTopicOptions = None
  )
  def config(compactionDefault: Boolean): Config[TopicConfig] =
    (Config.string("topic") ++ TopicOptions.config(compactionDefault)).map { case (topicName, maybeTopicOptions) =>
      TopicConfig(topicName, maybeTopicOptions)
    }
}

final case class KafkaConfig(
    brokers: List[String],
    maybeRegistry: Option[RegistryConfig],
    closeTimeout: Duration,
    bufferSize: Int,
    sink: TopicConfig,
    state: TopicConfig,
    groupId: String,
    clientId: String,
    transactionalId: String,
    properties: Map[String, AnyRef]
)
object KafkaConfig {
  def apply(
      brokers: List[String],
      maybeRegistry: Option[RegistryConfig],
      closeTimeout: Duration,
      bufferSize: Int,
      sink: TopicConfig,
      state: TopicConfig,
      groupId: String,
      clientId: String,
      transactionalId: String
  ): KafkaConfig = new KafkaConfig(
    brokers = brokers,
    maybeRegistry = maybeRegistry,
    closeTimeout = closeTimeout,
    bufferSize = bufferSize,
    sink = sink,
    state = state,
    groupId = groupId,
    clientId = clientId,
    transactionalId = transactionalId,
    properties = Map.empty
  )

  private[this] val kafkaConfigValue = (
    Config.listOf(Config.string("brokers")) ++
      RegistryConfig.config.nested("schema_registry") ++
      Config.duration("close_timeout") ++
      Config.int("buffer_size") ++
      TopicConfig.config(compactionDefault = false).nested("sink") ++
      TopicConfig.config(compactionDefault = true).nested("state") ++
      Config.string("group_id") ++
      Config.string("client_id") ++
      Config.string("transactional_id")
  ).map { case (brokers, maybeRegistry, closeTimeout, bufferSize, sink, state, groupId, clientId, transactionalId) =>
    KafkaConfig(brokers, maybeRegistry, closeTimeout, bufferSize, sink, state, groupId, clientId, transactionalId)
  }.nested("kafka")

  final val fromEnvironment: TaskLayer[KafkaConfig] = ZLayer {
    ZIO.config(kafkaConfigValue).mapError(ce => TamerError(ce.getMessage(), ce))
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy