Skip to content

Commit

Permalink
Export database schema in the engine and knowledge modules and add do…
Browse files Browse the repository at this point in the history
…cumentation (google#2628)

* Export Knowledge Manager Room database schema

* WIP generate schema

* Fix the build files and add documentation
  • Loading branch information
jingtang10 authored Jul 19, 2024
1 parent 1391a04 commit c224f73
Show file tree
Hide file tree
Showing 5 changed files with 261 additions and 9 deletions.
15 changes: 15 additions & 0 deletions docs/contrib/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,3 +324,18 @@ This section defines the process one goes through when making changes to any of
* Update your/any dependent PR (PR using the library) with the new _Artifact ID_ and make/trigger the CI

**NB:** For a specific example on working with FHIR SDK's Common Library during development, see [Common Library](#common-library).

# Database migration

If you are making changes to the database schema (in the `engine` or the `knowledge` module), you
need to consider how applications with Android FHIR SDK dependencies can upgrade to the new schema
without losing or corrupting existing data already on device. This can be done with [Room database
migration](https://developer.android.com/training/data-storage/room/migrating-db-versions).

> [!TIP]
> A new JSON schema file will be generated under the `schemas` folder in the module when you
update the database version. If you are having trouble with this, make sure you run the gradle
> command with `--rerun-tasks`:
> ```
> ./gradlew :<module>:build --rerun-tasks
> ```
9 changes: 3 additions & 6 deletions engine/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ publishArtifact(Releases.Engine)

createJacocoTestReportTask()

// Generate database schema in the schemas folder
ksp { arg("room.schemaLocation", "$projectDir/schemas") }

val generateSearchParamsTask =
project.tasks.register("generateSearchParamsTask", GenerateSearchParamsTask::class) {
srcOutputDir.set(project.layout.buildDirectory.dir("gen/main"))
Expand All @@ -39,12 +42,6 @@ android {
// need to specify this to prevent junit runner from going deep into our dependencies
testInstrumentationRunnerArguments["package"] = "com.google.android.fhir"
consumerProguardFile("proguard-rules.pro")

javaCompileOptions {
annotationProcessorOptions {
compilerArgumentProviders(RoomSchemaArgProvider(File(projectDir, "schemas")))
}
}
}

sourceSets {
Expand Down
8 changes: 7 additions & 1 deletion knowledge/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ publishArtifact(Releases.Knowledge)

createJacocoTestReportTask()

// Generate database schema in the schemas folder
ksp { arg("room.schemaLocation", "$projectDir/schemas") }

android {
namespace = "com.google.android.fhir.knowledge"
compileSdk = Sdk.COMPILE_SDK
Expand All @@ -25,7 +28,10 @@ android {
}

sourceSets {
getByName("androidTest").apply { resources.setSrcDirs(listOf("testdata")) }
getByName("androidTest").apply {
resources.setSrcDirs(listOf("testdata"))
assets.srcDirs("$projectDir/schemas")
}

getByName("test").apply { resources.setSrcDirs(listOf("testdata")) }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "41dc6411ce57c1eeba3300592f302b07",
"entities": [
{
"tableName": "ImplementationGuideEntity",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`implementationGuideId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `packageId` TEXT NOT NULL, `version` TEXT, `rootDirectory` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "implementationGuideId",
"columnName": "implementationGuideId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "packageId",
"columnName": "packageId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rootDirectory",
"columnName": "rootDirectory",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"implementationGuideId"
]
},
"indices": [
{
"name": "index_ImplementationGuideEntity_implementationGuideId",
"unique": false,
"columnNames": [
"implementationGuideId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_ImplementationGuideEntity_implementationGuideId` ON `${TABLE_NAME}` (`implementationGuideId`)"
},
{
"name": "index_ImplementationGuideEntity_packageId_url_version",
"unique": true,
"columnNames": [
"packageId",
"url",
"version"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ImplementationGuideEntity_packageId_url_version` ON `${TABLE_NAME}` (`packageId`, `url`, `version`)"
}
],
"foreignKeys": []
},
{
"tableName": "ResourceMetadataEntity",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`resourceMetadataId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `resourceType` TEXT NOT NULL, `url` TEXT, `name` TEXT, `version` TEXT, `resourceFile` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "resourceMetadataId",
"columnName": "resourceMetadataId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "resourceType",
"columnName": "resourceType",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "resourceFile",
"columnName": "resourceFile",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"resourceMetadataId"
]
},
"indices": [
{
"name": "index_ResourceMetadataEntity_resourceMetadataId",
"unique": false,
"columnNames": [
"resourceMetadataId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_ResourceMetadataEntity_resourceMetadataId` ON `${TABLE_NAME}` (`resourceMetadataId`)"
},
{
"name": "index_ResourceMetadataEntity_url_version_resourceFile",
"unique": true,
"columnNames": [
"url",
"version",
"resourceFile"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ResourceMetadataEntity_url_version_resourceFile` ON `${TABLE_NAME}` (`url`, `version`, `resourceFile`)"
}
],
"foreignKeys": []
},
{
"tableName": "ImplementationGuideResourceMetadataEntity",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `implementationGuideId` INTEGER, `resourceMetadataId` INTEGER NOT NULL, FOREIGN KEY(`implementationGuideId`) REFERENCES `ImplementationGuideEntity`(`implementationGuideId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`resourceMetadataId`) REFERENCES `ResourceMetadataEntity`(`resourceMetadataId`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "implementationGuideId",
"columnName": "implementationGuideId",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "resourceMetadataId",
"columnName": "resourceMetadataId",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_ImplementationGuideResourceMetadataEntity_implementationGuideId",
"unique": false,
"columnNames": [
"implementationGuideId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_ImplementationGuideResourceMetadataEntity_implementationGuideId` ON `${TABLE_NAME}` (`implementationGuideId`)"
},
{
"name": "index_ImplementationGuideResourceMetadataEntity_resourceMetadataId",
"unique": false,
"columnNames": [
"resourceMetadataId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_ImplementationGuideResourceMetadataEntity_resourceMetadataId` ON `${TABLE_NAME}` (`resourceMetadataId`)"
},
{
"name": "index_ImplementationGuideResourceMetadataEntity_implementationGuideId_resourceMetadataId",
"unique": true,
"columnNames": [
"implementationGuideId",
"resourceMetadataId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ImplementationGuideResourceMetadataEntity_implementationGuideId_resourceMetadataId` ON `${TABLE_NAME}` (`implementationGuideId`, `resourceMetadataId`)"
}
],
"foreignKeys": [
{
"table": "ImplementationGuideEntity",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"implementationGuideId"
],
"referencedColumns": [
"implementationGuideId"
]
},
{
"table": "ResourceMetadataEntity",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"resourceMetadataId"
],
"referencedColumns": [
"resourceMetadataId"
]
}
]
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '41dc6411ce57c1eeba3300592f302b07')"
]
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022-2023 Google LLC
* Copyright 2022-2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -42,7 +42,7 @@ import com.google.android.fhir.knowledge.db.entities.ResourceMetadataEntity
ImplementationGuideResourceMetadataEntity::class,
],
version = 1,
exportSchema = false,
exportSchema = true,
)
@TypeConverters(DbTypeConverters::class)
internal abstract class KnowledgeDatabase : RoomDatabase() {
Expand Down

0 comments on commit c224f73

Please sign in to comment.