Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/ajozwik/sbt-protoquill-crud-generic


https://github.com/ajozwik/sbt-protoquill-crud-generic

Last synced: about 2 months ago
JSON representation

Awesome Lists containing this project

README

        

# sbt-protoquill-crud-generic

Library of generic CRUD operation for [protoquill](https://github.com/zio/zio-protoquill) library. Only dynamic queries are supported.

[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.ajozwik/sbt-protoquill-crud-generic/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.ajozwik/sbt-protoquill-crud-generic)
[![Scala CI](https://github.com/ajozwik/sbt-protoquill-crud-generic/actions/workflows/ci.yml/badge.svg)](https://github.com/ajozwik/sbt-protoquill-crud-generic/actions/workflows/ci.yml)
[![Coverage Status](https://coveralls.io/repos/github/ajozwik/sbt-protoquill-crud-generic/badge.svg?branch=master)](https://coveralls.io/github/ajozwik/sbt-protoquill-crud-generic?branch=master)
[![codecov](https://codecov.io/gh/ajozwik/sbt-protoquill-crud-generic/branch/master/graph/badge.svg?token=ZGHJ344TLO)](https://codecov.io/gh/ajozwik/sbt-protoquill-crud-generic)

To activate the plugins add to project/plugins.sbt:

```scala
addSbtPlugin("com.github.ajozwik" % "sbt-protoquill-crud-generic" % quillGenericVersion)
```
And to build.sbt
```scala
enablePlugins(QuillRepositoryPlugin)
```

For simple usage you need to add:

```scala
generateTryRepositories += RepositoryDescription(
"pl.jozwik.example.model.Person", // model object
"pl.jozwik.example.model.PersonId", // model key
"pl.jozwik.example.monad.repository.PersonRepositoryGen", // repository implementation
true, // generated id , false for not generated id
Option("pl.jozwik.example.monad.impl.MyPersonRepository[Dialect, Naming, C]") // custom trait repository implementation or domain trait, or None
)
```

where:
```sql
CREATE TABLE IF NOT EXISTS PERSON
(
ID INT NOT NULL,
FIRST_NAME VARCHAR(50) NOT NULL,
LAST_NAME VARCHAR(20) NOT NULL,
BIRTH_DATE DATE,
ADDRESS_ID INT DEFAULT NULL
)

```
[Person.scala](/src/sbt-test/all/all/common/src/main/scala/pl/jozwik/example/domain/model/Person.scala)
```scala
package pl.jozwik.example.domain.model

import java.time.LocalDate

import pl.jozwik.quillgeneric.repository.WithId

object PersonId {
val empty: PersonId = PersonId(0)
}

final case class PersonId(value: Int) extends AnyVal

final case class Person(id: PersonId, firstName: String, lastName: String, birthDate: LocalDate, addressId: Option[AddressId] = None) extends WithId[PersonId]

```
[PersonRepository.scala](/src/sbt-test/all/all/common/src/main/scala/pl/jozwik/example/domain/repository/PersonRepository.scala)
```scala
trait PersonRepository[F[_], UP] extends RepositoryWithGeneratedId[F, PersonId, Person, UP] {

def count: F[Long]

def searchByFirstName(name: String)(offset: Int, limit: Int): F[Seq[Person]]

def maxBirthDate: F[Option[LocalDate]]

def youngerThan(date: LocalDate)(offset: Int, limit: Int): F[Seq[Person]]

}
```

[MyPersonRepository.scala](/src/sbt-test/all/all/monad/src/main/scala/pl/jozwik/example/monad/impl/PersonRepositoryImpl.scala) for scala.util.Try Monad

```scala
package pl.jozwik.example.monad.impl

import io.getquill.*
import io.getquill.context.sql.idiom.SqlIdiom
import io.getquill.extras.LocalDateOps
import pl.jozwik.quillgeneric.monad.*
import pl.jozwik.example.domain.model.{ Person, PersonId }
import pl.jozwik.example.domain.repository.PersonRepository

import java.time.LocalDate
import scala.util.Try
trait PersonRepositoryImpl[+Dialect <: SqlIdiom, +Naming <: NamingStrategy, C <: TryJdbcContextWithDateQuotes[Dialect, Naming]]
extends TryJdbcRepositoryWithGeneratedId[PersonId, Person, C, Dialect, Naming]
with PersonRepository[Try, Long] {
import context.*
def searchByFirstName(name: String)(offset: Int, limit: Int): Try[Seq[Person]] = {
val q = quoteQuery.filter(_.firstName == lift(name)).filter(_.lastName != lift("")).drop(lift(offset)).take(lift(limit))
Try { run(q) }
}

def maxBirthDate: Try[Option[LocalDate]] = {
val r = quoteQuery.map(p => p.birthDate)
Try { run(r.max) }
}

def youngerThan(date: LocalDate)(offset: Int, limit: Int): Try[Seq[Person]] = {
val q = quoteQuery.filter(_.birthDate > lift(date)).drop(lift(offset)).take(lift(limit))
Try { run(q) }
}

def count: Try[Long] =
val q = quoteQuery.size
Try { run(q) }

}

```

Auto generated class will look like:

```scala
//------------------------------------------------------------------------------
//
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
//
//------------------------------------------------------------------------------
package pl.jozwik.example.monad.repository

import io.getquill.*
import io.getquill.context.sql.idiom.SqlIdiom
import pl.jozwik.example.domain.model.Person
import pl.jozwik.example.domain.model.PersonId
import pl.jozwik.example.monad.impl.PersonRepositoryImpl
import scala.util.Try
import pl.jozwik.quillgeneric.monad.*
import cats.implicits.*

final class PersonRepositoryGen[+Dialect <: SqlIdiom, +Naming <: NamingStrategy, C <: TryJdbcContextWithDateQuotes[Dialect, Naming]](
protected val context: C)(
implicit meta: SchemaMeta[Person]
) extends PersonRepositoryImpl[Dialect, Naming, C] {

import context.*

protected def quoteQuery = quote {
query[Person]
}

protected inline def find(id: PersonId): Quoted[EntityQuery[Person]] = quote {
quoteQuery.filter(_.id == lift(id))
}

override def all: Try[Seq[Person]] =
for {
all <- Try {run(quoteQuery)}
} yield {
all
}

override def create(entity: Person, generateId: Boolean = true): Try[PersonId] =
Try {if (generateId) {
run(quoteQuery.insertValue(lift(entity)).returningGenerated(_.id))
} else {
run(quoteQuery.insertValue(lift(entity)).returning(_.id))
}}

override def createOrUpdate(entity: Person, generateId: Boolean = true): Try[PersonId] = {
inTransaction {
for {
el <- Try {run(find(entity.id).updateValue(lift(entity)))}
id <- el match
case 0 =>
create(entity, generateId)
case _ =>
pure(entity.id)
} yield {
id
}
}
}

override def read(id: PersonId): Try[Option[Person]] =
for {
seq <- Try {run(find(id))}
} yield {
seq.headOption
}

override def update(entity: Person): Try[Long] =
Try {run(find(entity.id).updateValue(lift(entity)))}

override def delete(id: PersonId): Try[Long] =
Try {run(find(id).delete)}

override def deleteAll(): Try[Long] =
Try {run(quoteQuery.delete)}

}

```
See [build.sbt](/src/sbt-test/all/all/build.sbt) for custom example of usage.

To initialize object for sql model:
```sql
CREATE TABLE IF NOT EXISTS PERSON3
(
ID INT NOT NULL AUTO_INCREMENT,
FIRST_NAME VARCHAR(50) NOT NULL,
LAST_NAME VARCHAR(20) NOT NULL,
DOB DATE,
ADDRESS_ID INT DEFAULT NULL
)
```

use:

```scala
val ctx = new H2JdbcContext(Literal, "h2")
implicit val meta: SchemaMeta[Person] = schemaMeta[Person]("Person3", columns => columns.birthDate -> "dob")
val repository = new PersonRepositoryGen(ctx)
```

Config file for this example is:

[application.conf](/src/sbt-test/all/all/common/src/test/resources/application.conf)