fix: support upgrading apps from CAMS 1.x to 2.x#579
Merged
Conversation
Protocols and deployments created with CAMS 1.x (protocol API level < 2.0) used the carp_core device namespace and could no longer be deserialized after the rename to the dk.carp.cams.devices namespace. - register 1.x device type names as fromJson aliases for the 2.x classes (Smartphone, BLEHeartRateDevice, SmartphoneDeviceRegistration, and the devices in the context, health, polar, movesense, esense, and movisens packages) - tolerate missing BLE scan fields (serviceUuids, allowDuplicates) when deserializing BLEDevice configurations - restore the 1.x stepcount measure type and probe
b41728c to
d7c2b9e
Compare
The map may be the unmodifiable const {} default when no registrations
were deserialized from a 1.x deployment, in which case registering a
connected device throws an UnsupportedError.
Deployments saved by CAMS 1.x omit the deployed timestamp (it was nullable) and use the 1.x StudyStatus enum names, which made SmartphoneDeployment.fromJson throw. Default deployed to now when missing and map the 1.x status names onto StudyDeploymentStatusTypes.
Both 1.x and 2.x opened carp.db with database version 1, so devices upgrading from a 1.x app kept the old schema: studies were stored in a 'deployment' table and task_queue had no device_role_name column. All study and task persistence then failed with 'no such table: studies' and 'no such column: device_role_name'. Bump the database to version 2 and add an onUpgrade migration which creates the studies table, adds and backfills the device_role_name column from the saved task snapshots, and copies the 1.x deployment table into the studies table. The actual schema is inspected first, so a 2.x database created at version 1 passes through unchanged.
bardram
approved these changes
Jun 11, 2026
bardram
left a comment
Contributor
There was a problem hiding this comment.
This looks very good, incl. the tests.
Contributor
|
Note, however, that you did not touch the
packages. Are these not affected? |
Contributor
Author
That was my first intuition as well. We have two namespaces, one for the device namespace and one for data types. The 1.x break was specifically the device-namespace rename (dk.cachet.carp.common.application.devices.* → dk.carp.cams.devices.*) plus the removed stepcount type. None of apps/audio/communication/connectivity/survey define a device or service configuration, they all sample on the Smartphone primary device |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Apps upgrading from CAMS 1.x to 2.x hit three independent blockers:
dk.cachet.carp.common.application.devices.*todk.carp.cams.devices.*. Old JSON throws aSerializationExceptionand the protocol fails to load entirely.carp.dbwith database version 1, but 2.x renamed thedeploymenttable tostudiesand added adevice_role_namecolumn totask_queue. Since the version never changed, neitheronCreatenor any upgrade hook runs on a device with an existing 1.x database — every study save fails withno such table: studiesand every task save throws an unhandledno such column: device_role_nameexception.deployedtimestamp (it was nullable) and uses the 1.xStudyStatusenum names, both of which madeSmartphoneDeployment.fromJsonthrow even with the type aliases in place.Changes
Deserialization
fromJsonaliases for the 2.x classes —Smartphone,BLEHeartRateDevice,SmartphoneDeviceRegistration, and the devices in the context, health, polar, movesense, esense, and movisens packages.serviceUuids,allowDuplicates) when deserializingBLEDeviceconfigurations, which 1.x JSON does not contain.stepcountmeasure type and probe, removed in 2.x in favor ofstepevent.SmartphoneDeployment.fromJsonnow defaultsdeployedto now when missing and maps the eleven 1.xStudyStatusnames ontoStudyDeploymentStatusTypes(e.g.Deployed→Running, deployment-in-progress states →DeployingDevices).Database migration
PersistenceServiceto database version 2 and add anonUpgrademigration which:studiestable,device_role_namecolumn totask_queueand backfills it from the saved task snapshots,deploymenttable intostudiesand drops the old table. The 1.xdeployment_statuscolumn (an enum index) has no 2.x equivalent and is not migrated.Runtime
SmartphoneStudyControllernow reassignsconnectedDeviceRegistrationsinstead of mutating it in place — the map may be the unmodifiableconst {}default when no registrations were deserialized from a 1.x deployment.Testing
api_level_1_compatibility_test.dartin carp_mobile_sensing and the context/polar/movesense packages, deserializing real 1.x JSON.persistence_service_migration_test.dartwhich builds a real 1.x database (old schema, a genuine 1.x deployment JSON fixture, a queued task) and verifies that initializing thePersistenceServicemigrates it: studies and tasks load, the role name is backfilled, and the old table is dropped. Runs onsqflite_common_ffi(new dev dependency).