aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/xyz/driver/restquery/db/SlickPostgresQueryBuilder.scala
blob: 0b3fece92616a412d23847c80d84b148ea15fad6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package xyz.driver.restquery.db

import java.time.{LocalDateTime, ZoneOffset}

import org.slf4j.LoggerFactory
import slick.jdbc.{GetResult, JdbcProfile}
import xyz.driver.core.database.SlickDal
import xyz.driver.restquery.query.{Pagination, SearchFilterExpr, Sorting}

import scala.collection.breakOut
import scala.concurrent.ExecutionContext

object SlickPostgresQueryBuilder {
  private val logger = LoggerFactory.getLogger(this.getClass)

  import xyz.driver.restquery.db.SlickQueryBuilder._

  def apply[T](
      databaseName: String,
      tableName: String,
      lastUpdateFieldName: Option[String],
      nullableFields: Set[String],
      links: Set[SlickTableLink],
      runner: Runner[T],
      countRunner: CountRunner)(
      implicit sqlContext: SlickDal,
      profile: JdbcProfile,
      getResult: GetResult[T],
      ec: ExecutionContext): SlickPostgresQueryBuilder[T] = {
    val parameters = SlickPostgresQueryBuilderParameters(
      databaseName = databaseName,
      tableData = TableData(tableName, lastUpdateFieldName, nullableFields),
      links = links.map(x => x.foreignTableName -> x)(breakOut)
    )
    new SlickPostgresQueryBuilder[T](parameters)(runner, countRunner)
  }

  def apply[T](
      databaseName: String,
      tableName: String,
      lastUpdateFieldName: Option[String],
      nullableFields: Set[String],
      links: Set[SlickTableLink])(
      implicit sqlContext: SlickDal,
      profile: JdbcProfile,
      getResult: GetResult[T],
      ec: ExecutionContext): SlickPostgresQueryBuilder[T] = {
    apply[T](databaseName, tableName, SlickQueryBuilderParameters.AllFields, lastUpdateFieldName, nullableFields, links)
  }

  def apply[T](
      databaseName: String,
      tableName: String,
      fields: Set[String],
      lastUpdateFieldName: Option[String],
      nullableFields: Set[String],
      links: Set[SlickTableLink])(
      implicit sqlContext: SlickDal,
      profile: JdbcProfile,
      getResult: GetResult[T],
      ec: ExecutionContext): SlickPostgresQueryBuilder[T] = {

    val runner: Runner[T] = { parameters =>
      val sql = parameters.toSql(countQuery = false, fields = fields).as[T]
      logger.debug(s"Built an SQL query: $sql")
      sqlContext.execute(sql)
    }

    val countRunner: CountRunner = { parameters =>
      implicit val getCountResult: GetResult[(Int, Option[LocalDateTime])] = GetResult({ r =>
        val count = r.rs.getInt(1)
        val lastUpdate = if (parameters.tableData.lastUpdateFieldName.isDefined) {
          Option(r.rs.getTimestamp(2)).map(timestampToLocalDateTime)
        } else None
        (count, lastUpdate)
      })
      val sql = parameters.toSql(countQuery = true).as[(Int, Option[LocalDateTime])]
      logger.debug(s"Built an SQL query returning count: $sql")
      sqlContext.execute(sql).map(_.head)
    }

    apply[T](
      databaseName = databaseName,
      tableName = tableName,
      lastUpdateFieldName = lastUpdateFieldName,
      nullableFields = nullableFields,
      links = links,
      runner = runner,
      countRunner = countRunner
    )
  }

  def timestampToLocalDateTime(timestamp: java.sql.Timestamp): LocalDateTime = {
    LocalDateTime.ofInstant(timestamp.toInstant, ZoneOffset.UTC)
  }
}

class SlickPostgresQueryBuilder[T](parameters: SlickPostgresQueryBuilderParameters)(
    implicit runner: SlickQueryBuilder.Runner[T],
    countRunner: SlickQueryBuilder.CountRunner)
    extends SlickQueryBuilder[T](parameters) {

  def withFilter(newFilter: SearchFilterExpr): SlickQueryBuilder[T] = {
    new SlickPostgresQueryBuilder[T](parameters.copy(filter = newFilter))
  }

  def withSorting(newSorting: Sorting): SlickQueryBuilder[T] = {
    new SlickPostgresQueryBuilder[T](parameters.copy(sorting = newSorting))
  }

  def withPagination(newPagination: Pagination): SlickQueryBuilder[T] = {
    new SlickPostgresQueryBuilder[T](parameters.copy(pagination = Some(newPagination)))
  }

  def resetPagination: SlickQueryBuilder[T] = {
    new SlickPostgresQueryBuilder[T](parameters.copy(pagination = None))
  }
}