Skip to content

KSP processor + Gradle plugin to check consistency between Room DB versions

License

Notifications You must be signed in to change notification settings

alecarnevale/diplomatico

Repository files navigation

🥃 DIPLOMATICO

You drank a lot of rum and forgot to update DB versions of your Room.

diplomatico provides a KSP processor and a Gradle plugin to check consistency between different Room DB versions.

If you forgot to update a database's version, because some entities changed, diplomatico is capable to spot this oversight and alert you.

🎬 Scenario

Prerequisite: Android project using Room as local database;


Your database's version = 1 is:

@Entity
data class Drink(
  @PrimaryKey val name: String,
)

@Database(
  entities = [Drink::class],
  version = 1,
)
abstract class DrinkDatabase : RoomDatabase()

Lat's say that eventually, you update your entities with a new field:

@Entity
data class Drink(
  @PrimaryKey val name: String,
  val receipe: String,
)

then you need to remember to raise up the database's version = 2.

🧙 Annotation + Gradle plugin

Report generation

With diplomatico you can annotate the database with @HashingRoomDBVersion.

Doing so, diplomatico's KSP processor will generate a report for you in the build directory:

// generated report
<DB fully qualified name, hashing of DB's entities>

The report list an hash for each annotated database, where the hash is computed by using database's entities as function input.

Report checking

Then, if you keep track of the generated report in your VCS, diplomatico's Gradle plugin registers a Gradle task to be executed when the Android assemble<buildVariant> task is executed.

This way everytime you build your app, checkRoomVersions<buildVariant> task is executed to check if the new generated report matches the one stored in your project. If not, the build fails.

// versioned report
DrinkDatabase, hash_without_receipe_field

// generated report
DrinkDatabase, hash_with_receipe_field

Purpose of this checking phase is:

  • warning developers that something have changed in one entity used by an annotated database (because hash value changed);
  • so developers can update Room Database's version, if needed;
  • then accept the new generated report (you can use updateRoomVersions<buildVariant> Gradle task).

Support for nested classes

diplomatico is able to spot any changes even in a class that's referenced as field for an Entity.

For example, in a scenario like this:

@Entity
data class Cocktail(
  @PrimaryKey val name: String,
  val baseSpirit: BaseSpirit,
)

data class BaseSpirit(
  val name: String,
  val distilled: Distilled,
)

data class Distilled(
  val name: String,
)

any changes in BaseSpirit or Distilled would let the build fails.

Support for non-Entity classes

diplomatico can also spot any changes in a class that is neither an Entity nor a nested class of an Entity.

Indeed you can annotate any class with ContributesRoomDBVersion annotation and specify for which database that class must be considered part of the hashing function.

For example, in a scenario like this:

@Entity
data class BeverageEntity(
  @PrimaryKey val name: String,
  val anything: String,  // likely here you're serializing Beverage
)

@ContributesRoomDBVersion(roomDB = DrinkDatabase::class)
data class Beverage(
  val name: String,
  val isAlcoholic: Boolean,
  val brand: String,
)

even if BeverageEntity doesn't know Beverage (and viceversa) any changes to Beverage let the build fails because now Beverage is considered as an input for the hash of DrinkDatabase.

Another approach to achieve the same goal is through HashingRoomDBVersion.contributes.

It is specially useful when the class that contributes to the database belongs to a modules that cannot depend on to the module where the database live.

For example, in a scenario like this:

// :demo:contributes --> :demo:core-entities

// this is in :demo:core-entities
data class Soda(
  val name: String,
  // any change here must trigger a new change in the report
)

// this is in :demo:contributes
@HashingRoomDBVersion(contributes = [Soda::class])
@Database(
  entities = [...],
  version = 1,
)
abstract class BeverageDatabase : RoomDatabase()

🎮 Demo

Take a look at:

  • :demo module for a sample usage;
  • Red PR that is failing because a change has been introduce for an entity and so the versioned report is outdated;
  • Green PR that is ready to be merged because after changing the entity it also updated the versioned report.
  • Red PR - nested classes that is failing because a change has been introduce for a class that is nested in a field of an entity and so the versioned report is outdated;
  • Green PR - nested classes that is ready to be merged because after changing a class that is nested in a field of an entity it also updated the versioned report;
  • Red PR - contributing classes that is failing because a change has been introduce for a class contributing to a database and so the versioned report is outdated;
  • Green PR - nested classes that is ready to be merged because after changing a class contributing to a database it also updated the versioned report;
  • Red PR - contributing classes with contributes property that is failing because a change has been introduce for a class contributing to a database, through HashingRoomDBVersion.contributes, and so the versioned report is outdated;
  • Green PR - nested classes with contributes property that is ready to be merged because after changing a class contributing to a database, through HashingRoomDBVersion.contributes, it also updated the versioned report.

🛠️ Installation

WIP

To start tracking the report in your project, you can use the updateRoomVersions<buildVariant> Gradle task. It will make a copy of the last generated report in the asset folder of your project. Take a look at this PR for an example.

🙏 Thanks to

About

KSP processor + Gradle plugin to check consistency between Room DB versions

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages