Skip to content

Commit

Permalink
Implement search by birth year (#265)
Browse files Browse the repository at this point in the history
  • Loading branch information
lenguyenthanh authored Feb 1, 2025
2 parents 01d10f6 + 572d15d commit 96e112e
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 13 deletions.
9 changes: 6 additions & 3 deletions modules/api/src/main/scala/providers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ object providers:
given RefinementProvider[PageFormat, String, PageNumber] =
Refinement.drivenBy(PageNumber.fromString, _.toString)

given RefinementProvider.Simple[smithy.api.Range, fide.types.PageSize] =
given RefinementProvider.Simple[smithy.api.Range, PageSize] =
RefinementProvider.rangeConstraint(_.toInt)

given RefinementProvider[PageSizeFormat, Int, fide.types.PageSize] =
given RefinementProvider[PageSizeFormat, Int, PageSize] =
Refinement.drivenBy(PageSize.either, _.toInt)

given RefinementProvider[PlayerIdFormat, Int, fide.types.PlayerId] =
Refinement.drivenBy(fide.types.PlayerId.either, _.value)

given RefinementProvider[BirthYearFormat, Int, fide.types.BirthYear] =
Refinement.drivenBy(fide.types.BirthYear.either, _.value)

given RefinementProvider[RatingFormat, Int, fide.types.Rating] =
Refinement.drivenBy(fide.types.Rating.either, _.value)

Expand All @@ -29,5 +32,5 @@ object providers:
(b: NonEmptySet[A]) => b.value
)

given [A]: RefinementProvider.Simple[smithy.api.Length, fide.types.NonEmptySet[A]] =
given [A]: RefinementProvider.Simple[smithy.api.Length, NonEmptySet[A]] =
RefinementProvider.lengthConstraint(_.value.size)
15 changes: 15 additions & 0 deletions modules/api/src/main/smithy/_global.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ structure PlayerIdFormat {}
@unwrap
integer PlayerId

@BirthYearFormat
@unwrap
integer BirthYear

@trait(selector: "string")
@refinement(
targetType: "fide.types.FederationId"
Expand Down Expand Up @@ -73,6 +77,13 @@ structure PageFormat {}
)
structure PageSizeFormat { }

@trait(selector: "integer")
@refinement(
targetType: "fide.types.BirthYear"
providerImport: "fide.spec.providers.given"
)
structure BirthYearFormat { }

@trait(selector: "list")
@refinement(
targetType: "fide.types.NonEmptySet",
Expand Down Expand Up @@ -210,4 +221,8 @@ structure FilterMixin {
otherTitles: OtherTitles
@httpQuery("gender")
gender: Gender
@httpQuery("birth_year[gte]")
birthYearMin: BirthYear
@httpQuery("birth_year[lte]")
birthYearMax: BirthYear
}
18 changes: 15 additions & 3 deletions modules/backend/src/main/scala/service.federation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ import cats.syntax.all.*
import fide.db.Db
import fide.domain.Models.Pagination
import fide.domain.{ FederationSummary, Models }
import fide.spec.{ FederationId as _, PageNumber as _, PageSize as _, PlayerId as _, Rating as _, * }
import fide.spec.{
BirthYear as _,
FederationId as _,
PageNumber as _,
PageSize as _,
PlayerId as _,
Rating as _,
*
}
import fide.types.*
import io.github.arainko.ducktape.*
import org.typelevel.log4cats.Logger
Expand All @@ -32,7 +40,9 @@ class FederationServiceImpl(db: Db)(using Logger[IO]) extends FederationService[
name: Option[String],
titles: Option[List[Title]],
otherTitles: Option[List[OtherTitle]],
gender: Option[Gender]
gender: Option[Gender],
birthYearMin: Option[BirthYear],
birthYearMax: Option[BirthYear]
): IO[GetFederationPlayersByIdOutput] =

val paging = Models.Pagination(page, pageSize)
Expand All @@ -46,7 +56,9 @@ class FederationServiceImpl(db: Db)(using Logger[IO]) extends FederationService[
id.some,
titles.map(_.map(_.to[domain.Title])),
otherTitles.map(_.map(_.to[domain.OtherTitle])),
gender.map(_.to[domain.Gender])
gender.map(_.to[domain.Gender]),
birthYearMin,
birthYearMax
)
db.allPlayers(sorting, paging, filter)
.handleErrorWith: e =>
Expand Down
18 changes: 15 additions & 3 deletions modules/backend/src/main/scala/service.player.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ import cats.effect.*
import cats.syntax.all.*
import fide.db.Db
import fide.domain.Models
import fide.spec.{ FederationId as _, PageNumber as _, PageSize as _, PlayerId as _, Rating as _, * }
import fide.spec.{
BirthYear as _,
FederationId as _,
PageNumber as _,
PageSize as _,
PlayerId as _,
Rating as _,
*
}
import fide.types.*
import io.github.arainko.ducktape.*
import org.typelevel.log4cats.Logger
Expand Down Expand Up @@ -32,7 +40,9 @@ class PlayerServiceImpl(db: Db)(using Logger[IO]) extends PlayerService[IO]:
name: Option[String],
titles: Option[List[Title]],
otherTitles: Option[List[OtherTitle]],
gender: Option[Gender]
gender: Option[Gender],
birthYearMin: Option[BirthYear],
birthYearMax: Option[BirthYear]
): IO[GetPlayersOutput] =
val paging = Models.Pagination(page, pageSize)
val sorting = Models.Sorting.fromOption(sortBy.map(_.to[Models.SortBy]), order.map(_.to[Models.Order]))
Expand All @@ -45,7 +55,9 @@ class PlayerServiceImpl(db: Db)(using Logger[IO]) extends PlayerService[IO]:
None,
titles.map(_.map(_.to[domain.Title])),
otherTitles.map(_.map(_.to[domain.OtherTitle])),
gender.map(_.to[domain.Gender])
gender.map(_.to[domain.Gender]),
birthYearMin,
birthYearMax
)
db.allPlayers(sorting, paging, filter)
.handleErrorWith: e =>
Expand Down
15 changes: 14 additions & 1 deletion modules/backend/src/test/scala/Arbitraries.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ object Arbitraries:
given Arbitrary[PlayerId] = Arbitrary:
Gen.posNum[Int].map(PlayerId.applyUnsafe(_))

given Arbitrary[BirthYear] = Arbitrary:
Gen.posNum[Int].map(BirthYear.applyUnsafe(_))

given Arbitrary[Title] = Arbitrary:
Gen.oneOf(Title.values.toSeq)

Expand All @@ -63,6 +66,12 @@ object Arbitraries:
max <- genMaxRating
yield RatingRange(min, max)

lazy val genBirthYear: Gen[Option[BirthYear]] =
Gen.frequency(
200 -> Gen.const(none),
1 -> Gen.choose(1900, 2025).map(BirthYear.option(_))
)

given Arbitrary[FederationId] = Arbitrary:
Gen.oneOf(Federation.all.keys.toSeq)

Expand Down Expand Up @@ -124,6 +133,8 @@ object Arbitraries:
titles <- genTitles
otherTitles <- genOtherTitles
gender <- genGender
minBirthYear <- genBirthYear
maxBirthYear <- genBirthYear
yield PlayerFilter(
name,
isActive,
Expand All @@ -133,5 +144,7 @@ object Arbitraries:
federationId,
titles,
otherTitles,
gender
gender,
minBirthYear,
maxBirthYear
)
8 changes: 6 additions & 2 deletions modules/db/src/main/scala/Db.scala
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,11 @@ private object Sql:
private val where: AppliedFragment = sql"WHERE ".apply(Void)

private def between(column: String, range: RatingRange): Option[AppliedFragment] =
between(column, range.min, range.max)

private def between[A <: Int](column: String, min: Option[A], max: Option[A]): Option[AppliedFragment] =
val _column = s"p.$column"
(range.min, range.max) match
(min, max) match
case (Some(min), Some(max)) =>
sql"""
#$_column BETWEEN ${int4} AND ${int4}""".apply(min, max).some
Expand All @@ -219,7 +222,8 @@ private object Sql:
filter.federationId.map(federationIdFragment),
filter.titles.map(xs => playersByTitles(xs.size)(xs, xs)),
filter.otherTitles.map(xs => playersByOtherTitles(xs.size)(xs)),
filter.gender.map(filterGender)
filter.gender.map(filterGender),
between("birth_year", filter.birthYearMin, filter.birthYearMax)
).flatten.match
case Nil => none
case xs => xs.intercalate(and).some
Expand Down
6 changes: 5 additions & 1 deletion modules/domain/src/main/scala/Models.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ object Models:
federationId: Option[FederationId],
titles: Option[List[Title]],
otherTitles: Option[List[OtherTitle]],
gender: Option[Gender]
gender: Option[Gender],
birthYearMin: Option[BirthYear],
birthYearMax: Option[BirthYear]
)

object PlayerFilter:
Expand All @@ -64,6 +66,8 @@ object Models:
None,
None,
None,
None,
None,
None
)

Expand Down
3 changes: 3 additions & 0 deletions modules/types/src/main/scala/PositiveInt.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ object PageNumber extends RefinedTypeOps[Int, Positive, PageNumber]:

opaque type PlayerId <: Int = PositiveInt
object PlayerId extends RefinedTypeOps[Int, Positive, PlayerId]

opaque type BirthYear <: Int = PositiveInt
object BirthYear extends RefinedTypeOps[Int, Positive, BirthYear]

0 comments on commit 96e112e

Please sign in to comment.