Skip to content

Commit

Permalink
Readonly repositories (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
oyvindberg authored Nov 4, 2023
1 parent 9465a9e commit cabfa7f
Show file tree
Hide file tree
Showing 18 changed files with 77 additions and 362 deletions.
42 changes: 22 additions & 20 deletions site-in/customization/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,30 @@ val options = Options(

## All options

| Field Name | Effect |
|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------|
| `pkg` | Specifies the package name for the generated code. |
| `dbLib` | Defines the database library to use for code generation (anorm, doobie or none). |
| `jsonLibs` | JSON libraries to generate codecs for (default is empty). |
| `silentBanner` | Controls whether to suppress the Typo banner while running Typo (default is `false`). |
| `fileHeader` | Sets the header text that appears at the top of generated files. |
| `naming` | Configures naming conventions for generated code. See section below |
| `typeOverride` | Defines type overrides for specific database types See section below. |
| `nullabilityOverride` | Defines nullability overrides for specific columns See section below. |
| `generateMockRepos` | Specifies which repositories to generate mock versions for (default is all). |
| `enableFieldValue` | Controls whether to enable `FieldValue` code generation for specific repositories (default is disabled). |
| `enableTestInserts` | Controls whether to enable [test inserts](other-features/testing-with-random-values.md) for specific repositories (default is none). |
| `enableDsl` | Enables the [SQL DSL](what-is/dsl.md) for code generation (default is `false`). |
| `keepDependencies` | Specifies whether to retain table dependencies in generated code (default is `false`). |
| Field Name | Effect |
|-----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `pkg` | Specifies the package name for the generated code. |
| `dbLib` | Defines the database library to use for code generation (anorm, doobie or none). |
| `jsonLibs` | JSON libraries to generate codecs for (default is empty). |
| `silentBanner` | Controls whether to suppress the Typo banner while running Typo (default is `false`). |
| `fileHeader` | Sets the header text that appears at the top of generated files. |
| `naming` | Configures naming conventions for generated code. See section below |
| `typeOverride` | Defines type overrides for specific database types See section below. |
| `nullabilityOverride` | Defines nullability overrides for specific columns See section below. |
| `generateMockRepos` | Specifies which repositories to generate mock versions for (default is all). |
| `enableFieldValue` | Controls whether to enable `FieldValue` code generation for specific repositories (default is disabled). |
| `enableTestInserts` | Controls whether to enable [test inserts](other-features/testing-with-random-values.md) for specific repositories (default is none). |
| `readonlyRepo` | Specifies whether to generate read-only repositories for specific repositories. Useful when you're working on a part of the system where you only consume certain tables. (default is `false` - all mutable). |
| `enableDsl` | Enables the [SQL DSL](what-is/dsl.md) for code generation (default is `false`). |
| `keepDependencies` | Specifies whether to generate [table dependencies](type-safety/type-flow.md) in generated code even if you didn't select them (default is `false`). |

## Development options
| Field Name | Effect |
|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------|
| `debugTypes` | Enables debug mode for types during code generation (default is `false`). |
| `inlineImplicits` | Controls whether to inline implicits for generated code (default is `true`). |
| `logger` | Specifies the logger to use for logging during code generation (default is console logging). Useful for |

| Field Name | Effect |
|-------------------|---------------------------------------------------------------------------------------------------------|
| `debugTypes` | Enables debug mode for types during code generation (default is `false`). |
| `inlineImplicits` | Controls whether to inline implicits for generated code (default is `true`). |
| `logger` | Specifies the logger to use for logging during code generation (default is console logging). Useful for |



5 changes: 5 additions & 0 deletions site-in/what-is/relations.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ case class AddressRowUnsaved(
)
```

### Readonly repositories

If you have a bunch of tables you just want to read, you can [customize](customization/overview.md)
the repositories to only expose read methods.

## Views

Typo also excels at simplifying code generation for views.
Expand Down
3 changes: 2 additions & 1 deletion typo-scripts/src/scala/scripts/GeneratedAdventureWorks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ object GeneratedAdventureWorks {
case ("sales.creditcard", "creditcardid") => "adventureworks.userdefined.CustomCreditcardId"
},
enableDsl = true,
enableTestInserts = Selector.All
enableTestInserts = Selector.All,
readonlyRepo = Selector.relationNames("purchaseorderdetail")
)
val targetSources = buildDir.resolve(s"$projectPath/generated-and-checked-in")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,10 @@ package purchasing
package purchaseorderdetail

import java.sql.Connection
import typo.dsl.DeleteBuilder
import typo.dsl.SelectBuilder
import typo.dsl.UpdateBuilder

trait PurchaseorderdetailRepo {
def delete(compositeId: PurchaseorderdetailId)(implicit c: Connection): Boolean
def delete: DeleteBuilder[PurchaseorderdetailFields, PurchaseorderdetailRow]
def insert(unsaved: PurchaseorderdetailRow)(implicit c: Connection): PurchaseorderdetailRow
def insert(unsaved: PurchaseorderdetailRowUnsaved)(implicit c: Connection): PurchaseorderdetailRow
def select: SelectBuilder[PurchaseorderdetailFields, PurchaseorderdetailRow]
def selectAll(implicit c: Connection): List[PurchaseorderdetailRow]
def selectById(compositeId: PurchaseorderdetailId)(implicit c: Connection): Option[PurchaseorderdetailRow]
def update(row: PurchaseorderdetailRow)(implicit c: Connection): Boolean
def update: UpdateBuilder[PurchaseorderdetailFields, PurchaseorderdetailRow]
def upsert(unsaved: PurchaseorderdetailRow)(implicit c: Connection): PurchaseorderdetailRow
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,73 +7,15 @@ package adventureworks
package purchasing
package purchaseorderdetail

import adventureworks.customtypes.Defaulted
import adventureworks.customtypes.TypoLocalDateTime
import adventureworks.customtypes.TypoShort
import adventureworks.production.product.ProductId
import adventureworks.purchasing.purchaseorderheader.PurchaseorderheaderId
import anorm.NamedParameter
import anorm.ParameterValue
import anorm.RowParser
import anorm.SQL
import anorm.SimpleSql
import anorm.SqlStringInterpolation
import anorm.ToStatement
import java.sql.Connection
import typo.dsl.DeleteBuilder
import typo.dsl.SelectBuilder
import typo.dsl.SelectBuilderSql
import typo.dsl.UpdateBuilder

object PurchaseorderdetailRepoImpl extends PurchaseorderdetailRepo {
override def delete(compositeId: PurchaseorderdetailId)(implicit c: Connection): Boolean = {
SQL"""delete from purchasing.purchaseorderdetail where "purchaseorderid" = ${ParameterValue(compositeId.purchaseorderid, null, PurchaseorderheaderId.toStatement)} AND "purchaseorderdetailid" = ${ParameterValue(compositeId.purchaseorderdetailid, null, ToStatement.intToStatement)}""".executeUpdate() > 0
}
override def delete: DeleteBuilder[PurchaseorderdetailFields, PurchaseorderdetailRow] = {
DeleteBuilder("purchasing.purchaseorderdetail", PurchaseorderdetailFields)
}
override def insert(unsaved: PurchaseorderdetailRow)(implicit c: Connection): PurchaseorderdetailRow = {
SQL"""insert into purchasing.purchaseorderdetail("purchaseorderid", "purchaseorderdetailid", "duedate", "orderqty", "productid", "unitprice", "receivedqty", "rejectedqty", "modifieddate")
values (${ParameterValue(unsaved.purchaseorderid, null, PurchaseorderheaderId.toStatement)}::int4, ${ParameterValue(unsaved.purchaseorderdetailid, null, ToStatement.intToStatement)}::int4, ${ParameterValue(unsaved.duedate, null, TypoLocalDateTime.toStatement)}::timestamp, ${ParameterValue(unsaved.orderqty, null, TypoShort.toStatement)}::int2, ${ParameterValue(unsaved.productid, null, ProductId.toStatement)}::int4, ${ParameterValue(unsaved.unitprice, null, ToStatement.scalaBigDecimalToStatement)}::numeric, ${ParameterValue(unsaved.receivedqty, null, ToStatement.scalaBigDecimalToStatement)}::numeric, ${ParameterValue(unsaved.rejectedqty, null, ToStatement.scalaBigDecimalToStatement)}::numeric, ${ParameterValue(unsaved.modifieddate, null, TypoLocalDateTime.toStatement)}::timestamp)
returning "purchaseorderid", "purchaseorderdetailid", "duedate"::text, "orderqty", "productid", "unitprice", "receivedqty", "rejectedqty", "modifieddate"::text
"""
.executeInsert(PurchaseorderdetailRow.rowParser(1).single)

}
override def insert(unsaved: PurchaseorderdetailRowUnsaved)(implicit c: Connection): PurchaseorderdetailRow = {
val namedParameters = List(
Some((NamedParameter("purchaseorderid", ParameterValue(unsaved.purchaseorderid, null, PurchaseorderheaderId.toStatement)), "::int4")),
Some((NamedParameter("duedate", ParameterValue(unsaved.duedate, null, TypoLocalDateTime.toStatement)), "::timestamp")),
Some((NamedParameter("orderqty", ParameterValue(unsaved.orderqty, null, TypoShort.toStatement)), "::int2")),
Some((NamedParameter("productid", ParameterValue(unsaved.productid, null, ProductId.toStatement)), "::int4")),
Some((NamedParameter("unitprice", ParameterValue(unsaved.unitprice, null, ToStatement.scalaBigDecimalToStatement)), "::numeric")),
Some((NamedParameter("receivedqty", ParameterValue(unsaved.receivedqty, null, ToStatement.scalaBigDecimalToStatement)), "::numeric")),
Some((NamedParameter("rejectedqty", ParameterValue(unsaved.rejectedqty, null, ToStatement.scalaBigDecimalToStatement)), "::numeric")),
unsaved.purchaseorderdetailid match {
case Defaulted.UseDefault => None
case Defaulted.Provided(value) => Some((NamedParameter("purchaseorderdetailid", ParameterValue(value, null, ToStatement.intToStatement)), "::int4"))
},
unsaved.modifieddate match {
case Defaulted.UseDefault => None
case Defaulted.Provided(value) => Some((NamedParameter("modifieddate", ParameterValue(value, null, TypoLocalDateTime.toStatement)), "::timestamp"))
}
).flatten
val quote = '"'.toString
if (namedParameters.isEmpty) {
SQL"""insert into purchasing.purchaseorderdetail default values
returning "purchaseorderid", "purchaseorderdetailid", "duedate"::text, "orderqty", "productid", "unitprice", "receivedqty", "rejectedqty", "modifieddate"::text
"""
.executeInsert(PurchaseorderdetailRow.rowParser(1).single)
} else {
val q = s"""insert into purchasing.purchaseorderdetail(${namedParameters.map{case (x, _) => quote + x.name + quote}.mkString(", ")})
values (${namedParameters.map{ case (np, cast) => s"{${np.name}}$cast"}.mkString(", ")})
returning "purchaseorderid", "purchaseorderdetailid", "duedate"::text, "orderqty", "productid", "unitprice", "receivedqty", "rejectedqty", "modifieddate"::text
"""
SimpleSql(SQL(q), namedParameters.map { case (np, _) => np.tupled }.toMap, RowParser.successful)
.executeInsert(PurchaseorderdetailRow.rowParser(1).single)
}

}
override def select: SelectBuilder[PurchaseorderdetailFields, PurchaseorderdetailRow] = {
SelectBuilderSql("purchasing.purchaseorderdetail", PurchaseorderdetailFields, PurchaseorderdetailRow.rowParser)
}
Expand All @@ -88,47 +30,4 @@ object PurchaseorderdetailRepoImpl extends PurchaseorderdetailRepo {
where "purchaseorderid" = ${ParameterValue(compositeId.purchaseorderid, null, PurchaseorderheaderId.toStatement)} AND "purchaseorderdetailid" = ${ParameterValue(compositeId.purchaseorderdetailid, null, ToStatement.intToStatement)}
""".as(PurchaseorderdetailRow.rowParser(1).singleOpt)
}
override def update(row: PurchaseorderdetailRow)(implicit c: Connection): Boolean = {
val compositeId = row.compositeId
SQL"""update purchasing.purchaseorderdetail
set "duedate" = ${ParameterValue(row.duedate, null, TypoLocalDateTime.toStatement)}::timestamp,
"orderqty" = ${ParameterValue(row.orderqty, null, TypoShort.toStatement)}::int2,
"productid" = ${ParameterValue(row.productid, null, ProductId.toStatement)}::int4,
"unitprice" = ${ParameterValue(row.unitprice, null, ToStatement.scalaBigDecimalToStatement)}::numeric,
"receivedqty" = ${ParameterValue(row.receivedqty, null, ToStatement.scalaBigDecimalToStatement)}::numeric,
"rejectedqty" = ${ParameterValue(row.rejectedqty, null, ToStatement.scalaBigDecimalToStatement)}::numeric,
"modifieddate" = ${ParameterValue(row.modifieddate, null, TypoLocalDateTime.toStatement)}::timestamp
where "purchaseorderid" = ${ParameterValue(compositeId.purchaseorderid, null, PurchaseorderheaderId.toStatement)} AND "purchaseorderdetailid" = ${ParameterValue(compositeId.purchaseorderdetailid, null, ToStatement.intToStatement)}
""".executeUpdate() > 0
}
override def update: UpdateBuilder[PurchaseorderdetailFields, PurchaseorderdetailRow] = {
UpdateBuilder("purchasing.purchaseorderdetail", PurchaseorderdetailFields, PurchaseorderdetailRow.rowParser)
}
override def upsert(unsaved: PurchaseorderdetailRow)(implicit c: Connection): PurchaseorderdetailRow = {
SQL"""insert into purchasing.purchaseorderdetail("purchaseorderid", "purchaseorderdetailid", "duedate", "orderqty", "productid", "unitprice", "receivedqty", "rejectedqty", "modifieddate")
values (
${ParameterValue(unsaved.purchaseorderid, null, PurchaseorderheaderId.toStatement)}::int4,
${ParameterValue(unsaved.purchaseorderdetailid, null, ToStatement.intToStatement)}::int4,
${ParameterValue(unsaved.duedate, null, TypoLocalDateTime.toStatement)}::timestamp,
${ParameterValue(unsaved.orderqty, null, TypoShort.toStatement)}::int2,
${ParameterValue(unsaved.productid, null, ProductId.toStatement)}::int4,
${ParameterValue(unsaved.unitprice, null, ToStatement.scalaBigDecimalToStatement)}::numeric,
${ParameterValue(unsaved.receivedqty, null, ToStatement.scalaBigDecimalToStatement)}::numeric,
${ParameterValue(unsaved.rejectedqty, null, ToStatement.scalaBigDecimalToStatement)}::numeric,
${ParameterValue(unsaved.modifieddate, null, TypoLocalDateTime.toStatement)}::timestamp
)
on conflict ("purchaseorderid", "purchaseorderdetailid")
do update set
"duedate" = EXCLUDED."duedate",
"orderqty" = EXCLUDED."orderqty",
"productid" = EXCLUDED."productid",
"unitprice" = EXCLUDED."unitprice",
"receivedqty" = EXCLUDED."receivedqty",
"rejectedqty" = EXCLUDED."rejectedqty",
"modifieddate" = EXCLUDED."modifieddate"
returning "purchaseorderid", "purchaseorderdetailid", "duedate"::text, "orderqty", "productid", "unitprice", "receivedqty", "rejectedqty", "modifieddate"::text
"""
.executeInsert(PurchaseorderdetailRow.rowParser(1).single)

}
}
Loading

0 comments on commit cabfa7f

Please sign in to comment.