Skip to content

Commit c224f73

Browse files
authored
Export database schema in the engine and knowledge modules and add documentation (#2628)
* Export Knowledge Manager Room database schema * WIP generate schema * Fix the build files and add documentation
1 parent 1391a04 commit c224f73

File tree

5 files changed

+261
-9
lines changed

5 files changed

+261
-9
lines changed

docs/contrib/index.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,3 +324,18 @@ This section defines the process one goes through when making changes to any of
324324
* Update your/any dependent PR (PR using the library) with the new _Artifact ID_ and make/trigger the CI
325325

326326
**NB:** For a specific example on working with FHIR SDK's Common Library during development, see [Common Library](#common-library).
327+
328+
# Database migration
329+
330+
If you are making changes to the database schema (in the `engine` or the `knowledge` module), you
331+
need to consider how applications with Android FHIR SDK dependencies can upgrade to the new schema
332+
without losing or corrupting existing data already on device. This can be done with [Room database
333+
migration](https://developer.android.com/training/data-storage/room/migrating-db-versions).
334+
335+
> [!TIP]
336+
> A new JSON schema file will be generated under the `schemas` folder in the module when you
337+
update the database version. If you are having trouble with this, make sure you run the gradle
338+
> command with `--rerun-tasks`:
339+
> ```
340+
> ./gradlew :<module>:build --rerun-tasks
341+
> ```

engine/build.gradle.kts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ publishArtifact(Releases.Engine)
1414

1515
createJacocoTestReportTask()
1616

17+
// Generate database schema in the schemas folder
18+
ksp { arg("room.schemaLocation", "$projectDir/schemas") }
19+
1720
val generateSearchParamsTask =
1821
project.tasks.register("generateSearchParamsTask", GenerateSearchParamsTask::class) {
1922
srcOutputDir.set(project.layout.buildDirectory.dir("gen/main"))
@@ -39,12 +42,6 @@ android {
3942
// need to specify this to prevent junit runner from going deep into our dependencies
4043
testInstrumentationRunnerArguments["package"] = "com.google.android.fhir"
4144
consumerProguardFile("proguard-rules.pro")
42-
43-
javaCompileOptions {
44-
annotationProcessorOptions {
45-
compilerArgumentProviders(RoomSchemaArgProvider(File(projectDir, "schemas")))
46-
}
47-
}
4845
}
4946

5047
sourceSets {

knowledge/build.gradle.kts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ publishArtifact(Releases.Knowledge)
1414

1515
createJacocoTestReportTask()
1616

17+
// Generate database schema in the schemas folder
18+
ksp { arg("room.schemaLocation", "$projectDir/schemas") }
19+
1720
android {
1821
namespace = "com.google.android.fhir.knowledge"
1922
compileSdk = Sdk.COMPILE_SDK
@@ -25,7 +28,10 @@ android {
2528
}
2629

2730
sourceSets {
28-
getByName("androidTest").apply { resources.setSrcDirs(listOf("testdata")) }
31+
getByName("androidTest").apply {
32+
resources.setSrcDirs(listOf("testdata"))
33+
assets.srcDirs("$projectDir/schemas")
34+
}
2935

3036
getByName("test").apply { resources.setSrcDirs(listOf("testdata")) }
3137
}
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
{
2+
"formatVersion": 1,
3+
"database": {
4+
"version": 1,
5+
"identityHash": "41dc6411ce57c1eeba3300592f302b07",
6+
"entities": [
7+
{
8+
"tableName": "ImplementationGuideEntity",
9+
"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)",
10+
"fields": [
11+
{
12+
"fieldPath": "implementationGuideId",
13+
"columnName": "implementationGuideId",
14+
"affinity": "INTEGER",
15+
"notNull": true
16+
},
17+
{
18+
"fieldPath": "url",
19+
"columnName": "url",
20+
"affinity": "TEXT",
21+
"notNull": true
22+
},
23+
{
24+
"fieldPath": "packageId",
25+
"columnName": "packageId",
26+
"affinity": "TEXT",
27+
"notNull": true
28+
},
29+
{
30+
"fieldPath": "version",
31+
"columnName": "version",
32+
"affinity": "TEXT",
33+
"notNull": false
34+
},
35+
{
36+
"fieldPath": "rootDirectory",
37+
"columnName": "rootDirectory",
38+
"affinity": "TEXT",
39+
"notNull": true
40+
}
41+
],
42+
"primaryKey": {
43+
"autoGenerate": true,
44+
"columnNames": [
45+
"implementationGuideId"
46+
]
47+
},
48+
"indices": [
49+
{
50+
"name": "index_ImplementationGuideEntity_implementationGuideId",
51+
"unique": false,
52+
"columnNames": [
53+
"implementationGuideId"
54+
],
55+
"orders": [],
56+
"createSql": "CREATE INDEX IF NOT EXISTS `index_ImplementationGuideEntity_implementationGuideId` ON `${TABLE_NAME}` (`implementationGuideId`)"
57+
},
58+
{
59+
"name": "index_ImplementationGuideEntity_packageId_url_version",
60+
"unique": true,
61+
"columnNames": [
62+
"packageId",
63+
"url",
64+
"version"
65+
],
66+
"orders": [],
67+
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ImplementationGuideEntity_packageId_url_version` ON `${TABLE_NAME}` (`packageId`, `url`, `version`)"
68+
}
69+
],
70+
"foreignKeys": []
71+
},
72+
{
73+
"tableName": "ResourceMetadataEntity",
74+
"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)",
75+
"fields": [
76+
{
77+
"fieldPath": "resourceMetadataId",
78+
"columnName": "resourceMetadataId",
79+
"affinity": "INTEGER",
80+
"notNull": true
81+
},
82+
{
83+
"fieldPath": "resourceType",
84+
"columnName": "resourceType",
85+
"affinity": "TEXT",
86+
"notNull": true
87+
},
88+
{
89+
"fieldPath": "url",
90+
"columnName": "url",
91+
"affinity": "TEXT",
92+
"notNull": false
93+
},
94+
{
95+
"fieldPath": "name",
96+
"columnName": "name",
97+
"affinity": "TEXT",
98+
"notNull": false
99+
},
100+
{
101+
"fieldPath": "version",
102+
"columnName": "version",
103+
"affinity": "TEXT",
104+
"notNull": false
105+
},
106+
{
107+
"fieldPath": "resourceFile",
108+
"columnName": "resourceFile",
109+
"affinity": "TEXT",
110+
"notNull": true
111+
}
112+
],
113+
"primaryKey": {
114+
"autoGenerate": true,
115+
"columnNames": [
116+
"resourceMetadataId"
117+
]
118+
},
119+
"indices": [
120+
{
121+
"name": "index_ResourceMetadataEntity_resourceMetadataId",
122+
"unique": false,
123+
"columnNames": [
124+
"resourceMetadataId"
125+
],
126+
"orders": [],
127+
"createSql": "CREATE INDEX IF NOT EXISTS `index_ResourceMetadataEntity_resourceMetadataId` ON `${TABLE_NAME}` (`resourceMetadataId`)"
128+
},
129+
{
130+
"name": "index_ResourceMetadataEntity_url_version_resourceFile",
131+
"unique": true,
132+
"columnNames": [
133+
"url",
134+
"version",
135+
"resourceFile"
136+
],
137+
"orders": [],
138+
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ResourceMetadataEntity_url_version_resourceFile` ON `${TABLE_NAME}` (`url`, `version`, `resourceFile`)"
139+
}
140+
],
141+
"foreignKeys": []
142+
},
143+
{
144+
"tableName": "ImplementationGuideResourceMetadataEntity",
145+
"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 )",
146+
"fields": [
147+
{
148+
"fieldPath": "id",
149+
"columnName": "id",
150+
"affinity": "INTEGER",
151+
"notNull": true
152+
},
153+
{
154+
"fieldPath": "implementationGuideId",
155+
"columnName": "implementationGuideId",
156+
"affinity": "INTEGER",
157+
"notNull": false
158+
},
159+
{
160+
"fieldPath": "resourceMetadataId",
161+
"columnName": "resourceMetadataId",
162+
"affinity": "INTEGER",
163+
"notNull": true
164+
}
165+
],
166+
"primaryKey": {
167+
"autoGenerate": true,
168+
"columnNames": [
169+
"id"
170+
]
171+
},
172+
"indices": [
173+
{
174+
"name": "index_ImplementationGuideResourceMetadataEntity_implementationGuideId",
175+
"unique": false,
176+
"columnNames": [
177+
"implementationGuideId"
178+
],
179+
"orders": [],
180+
"createSql": "CREATE INDEX IF NOT EXISTS `index_ImplementationGuideResourceMetadataEntity_implementationGuideId` ON `${TABLE_NAME}` (`implementationGuideId`)"
181+
},
182+
{
183+
"name": "index_ImplementationGuideResourceMetadataEntity_resourceMetadataId",
184+
"unique": false,
185+
"columnNames": [
186+
"resourceMetadataId"
187+
],
188+
"orders": [],
189+
"createSql": "CREATE INDEX IF NOT EXISTS `index_ImplementationGuideResourceMetadataEntity_resourceMetadataId` ON `${TABLE_NAME}` (`resourceMetadataId`)"
190+
},
191+
{
192+
"name": "index_ImplementationGuideResourceMetadataEntity_implementationGuideId_resourceMetadataId",
193+
"unique": true,
194+
"columnNames": [
195+
"implementationGuideId",
196+
"resourceMetadataId"
197+
],
198+
"orders": [],
199+
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ImplementationGuideResourceMetadataEntity_implementationGuideId_resourceMetadataId` ON `${TABLE_NAME}` (`implementationGuideId`, `resourceMetadataId`)"
200+
}
201+
],
202+
"foreignKeys": [
203+
{
204+
"table": "ImplementationGuideEntity",
205+
"onDelete": "CASCADE",
206+
"onUpdate": "NO ACTION",
207+
"columns": [
208+
"implementationGuideId"
209+
],
210+
"referencedColumns": [
211+
"implementationGuideId"
212+
]
213+
},
214+
{
215+
"table": "ResourceMetadataEntity",
216+
"onDelete": "CASCADE",
217+
"onUpdate": "NO ACTION",
218+
"columns": [
219+
"resourceMetadataId"
220+
],
221+
"referencedColumns": [
222+
"resourceMetadataId"
223+
]
224+
}
225+
]
226+
}
227+
],
228+
"views": [],
229+
"setupQueries": [
230+
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
231+
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '41dc6411ce57c1eeba3300592f302b07')"
232+
]
233+
}
234+
}

knowledge/src/main/java/com/google/android/fhir/knowledge/db/KnowledgeDatabase.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022-2023 Google LLC
2+
* Copyright 2022-2024 Google LLC
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -42,7 +42,7 @@ import com.google.android.fhir.knowledge.db.entities.ResourceMetadataEntity
4242
ImplementationGuideResourceMetadataEntity::class,
4343
],
4444
version = 1,
45-
exportSchema = false,
45+
exportSchema = true,
4646
)
4747
@TypeConverters(DbTypeConverters::class)
4848
internal abstract class KnowledgeDatabase : RoomDatabase() {

0 commit comments

Comments
 (0)