Skip to content

Commit

Permalink
Add support for scala 3
Browse files Browse the repository at this point in the history
  • Loading branch information
NarekDW committed Apr 18, 2024
1 parent de38ccb commit b71b8c7
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import cats.Show
import magnolia1.{CaseClass, Magnolia, SealedTrait}
import derevo.Derivation
import derevo.NewTypeDerivation
import tofu.logging.derivation.MaskMode.Custom

trait LoggingDerivationImpl extends Derivation[Loggable] with NewTypeDerivation[Loggable] {
type Typeclass[A] = Loggable[A]
Expand All @@ -30,11 +29,8 @@ trait LoggingDerivationImpl extends Derivation[Loggable] with NewTypeDerivation[
case _ if annotations.contains(unembed()) =>
receiver.combine(acc, typeclass.fields(value, input))
case _ =>
lazy val combine =
(f: String => String) => receiver.combine(acc, typeclass.putMaskedField(value, label, input)(f))
annotations.collectFirst {
case masked(Custom(f)) => combine(f(_))
case masked(mode) => combine(masking.defaultMasker(_, mode))
annotations.collectFirst { case masked(mode) =>
receiver.combine(acc, typeclass.putMaskedField(value, label, input)(masking.string(_, mode)))
}.getOrElse(receiver.combine(acc, typeclass.putField(value, label, input)))
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package tofu.logging.derivation

import tofu.logging.derivation.MaskMode.Custom

import scala.annotation.tailrec
import scala.util.matching.Regex

import tofu.magnolia.compat
import tofu.magnolia.compat.Param

Expand All @@ -20,6 +21,9 @@ object MaskMode {
def this(length: Int) = this(0, length)
}
case class Regexp(pattern: Regex) extends MaskMode

/** Allows to define a custom masker function */
case class Custom(f: String => String) extends MaskMode
}

object masking {
Expand Down Expand Up @@ -54,6 +58,8 @@ object masking {
.getOrElse(shown)
case MaskMode.ForLength(offset, maxLength) =>
loop(shown.toCharArray, shown.length min (offset max 0), if (maxLength == -1) shown.length else maxLength)
case Custom(f) =>
f(shown)
}
}

Expand Down Expand Up @@ -88,6 +94,9 @@ object masking {
implicit final class Ops(private val value: String) extends AnyVal {
def mask: String = mask(MaskMode.Full)

def mask(mode: MaskMode): String = string(value, mode)
def mask(mode: MaskMode): String = mode match {
case Custom(f) => f(value)
case _ => string(value, mode)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package derivation

import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import tofu.logging.derivation.MaskMode.Custom

class DerivedLoggableSuite extends AnyFlatSpec with Matchers {

Expand Down Expand Up @@ -67,6 +68,35 @@ class DerivedLoggableSuite extends AnyFlatSpec with Matchers {
Loggable[MaskedBaz].logShow(MaskedBaz(None)) shouldBe "MaskedBaz{kek=<none>}"
}

it should "show mask Option values in logShow" in {
val maybeMasked = MaskedOptBaz(
maybeStr = Some("str"),
maybeInt = Some(123),
maybeBool = Some(true),
maybeDouble = Some(100.001),
maybeStr2 = None
)
Loggable[MaskedOptBaz].logShow(maybeMasked) shouldBe
"MaskedOptBaz{" +
"maybeStr=Some(***)," +
"maybeInt=Some(###)," +
"maybeBool=Some(****)," +
"maybeDouble=Some(###.###)," +
"maybeStr2=<none>" +
"}"
}

it should "show mask fields with custom masker function" in {
val maskedCustom = MaskedCustom(
sensitiveField = "som sensitive data",
firstName = Some("John"),
age = 42
)

json(maskedCustom) shouldBe """{"sensitiveField":"*","firstName":"J***","age":"**"}"""
Loggable[MaskedCustom].logShow(maskedCustom) shouldBe
"MaskedCustom{sensitiveField=*,firstName=Some(J***),age=**}"
}
}

object DerivedLoggableSuite {
Expand All @@ -89,4 +119,25 @@ object DerivedLoggableSuite {
derives Loggable

final case class MaskedBaz(@masked kek: Option[String], @ignoreOpt a: Option[String] = None) derives Loggable

final case class MaskedOptBaz(
@masked maybeStr: Option[String],
@masked maybeInt: Option[Int],
@masked maybeBool: Option[Boolean],
@masked maybeDouble: Option[Double],
@masked maybeStr2: Option[String]
) derives Loggable

final case class MaskedCustom(
@masked(Custom(_ => "*")) sensitiveField: String,
@masked(Custom(maskName)) firstName: Option[String],
@masked(Custom(maskAge)) age: Int
) derives Loggable

def maskName(name: String): String =
name.take(1) + "***"

def maskAge(i: String): String =
"*" * i.length

}

0 comments on commit b71b8c7

Please sign in to comment.