Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add additionalSpecifications field to DeviceRegistration #484

Merged
merged 2 commits into from
Oct 6, 2024

Conversation

yuanchen233
Copy link
Collaborator

As discussed in #431, adding a additionalSpecifications field to DeviceRegistration. This allows to keep additional specification data when register device and access them later on in StudyDeploymentStatus.deviceStatusList

@yuanchen233 yuanchen233 force-pushed the extra-information-for-device-registration branch 2 times, most recently from a9db9bd to 7f1572f Compare September 26, 2024 11:17
@yuanchen233
Copy link
Collaborator Author

For DeviceRegistrationTest, original test cases were kept to cover situations when additionalSpecifications is null; same for GenerateExampleRequests for rpc, additionalSpecifications is only added to one of the deviceRegistration.

@yuanchen233 yuanchen233 marked this pull request as ready for review September 27, 2024 16:18
@Whathecode Whathecode force-pushed the extra-information-for-device-registration branch from 0b57685 to 4c78321 Compare September 30, 2024 19:26
Copy link
Member

@Whathecode Whathecode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments; I already fixed those (you can resolve them after reading them).

In addition, I squashed three of the commits into one. Each commit ideally should pass all tests/be a cohesive whole. The last commit was a correct separate commit. 👍 I hope/presume you didn't have other changes on this branch, so I force pushed. Apologies if you did (as that can cause conflicts). Make sure to use git pull --rebase.

But, I see two other concerns before this can be merged:

  • I don't recall 100% whether or not we need @Required on the field. I vaguely remember it may be needed for some scenario, maybe "unknown polymorphic serialization". I need to dig up some old issues/documentation.
  • You didn't test whether additionalSpecification actually gets serialized as a JSON object (instead as a string with escaped \" chars, etc.). I put a breakpoint and apparently it doesn't. I suspect that is because the serializer used for that field is not "inherited" as you would expect it. I need to dig in to kotlinx serialization documentation to refresh my memory.

@Whathecode
Copy link
Member

Whathecode commented Sep 30, 2024

Regarding @Required, I found something relevant here in context of a fix I applied.

Missing @Required attributes on fields which should always be serialized but had values assigned by initializers. These fields are needed since the backend does not necessarily have the concrete types available, and these fields are hence not optional. Applying @Required solves that.

That makes sense for fields like DeviceRegistration.deviceId, and definitely PrimaryDeviceConfiguration.isPrimaryDevice, but it seems less relevant for something like additionalSpecifications, which I don't expect to ever have type-specific defaults.

Now I'm actually wondering whether DeviceRegistration.deviceDisplayName even ever needed @Required. It didn't seem like I considered/questioned that in the related PR. I'm thinking not applying @Required (as you did here) is correct, and adding it for deviceDisplayName may have been erroneous due to a naive mimicking of surrounding code.

And, regarding the ApplicationDataSerializer not kicking in, my intuition seems right:

I suspect that is because the serializer used for that field is not "inherited" as you would expect it.

For this to work as expected, you would have to apply @Serializable( ApplicationDataSerializer::class ) to all extending classes of DeviceRegistration. Try applying it to DefaultDeviceRegistration which is used in the test, and see the difference in the serialized output.

@Serializable
@JsExport
data class DefaultDeviceRegistration(
    @Required
    override val deviceDisplayName: String? = null,
    @Serializable( ApplicationDataSerializer::class )
    override val additionalSpecifications: String? = null,
    @Required
    override val deviceId: String = UUID.randomUUID().toString(),
) : DeviceRegistration()

This gives ... "additionalSpecifications":{"OS":"Android 42"} ... as expected, instead of the erroneous ... "additionalSpecifications":"{\"OS\":\"Android 42\"}" ...

That would be a bit unfortunate if there is no way around this. It would be worthwhile checking the kotlinx serialization documentation to see whether they have any (new) features which can avoid the need for doing this.

@yuanchen233
Copy link
Collaborator Author

In addition, I squashed three of the commits into one. Each commit ideally should pass all tests/be a cohesive whole. The last commit was a correct separate commit.

Got it, I was expecting a 'squash and merge' when this branch is done, but saw a TODO warning when pushing. I didn't give much thought if those changes should be applied here after having fun reading that serialization issue

Now I'm actually wondering whether DeviceRegistration.deviceDisplayName even ever needed @Required. It didn't seem like I considered/questioned that in the related PR. I'm thinking not applying @Required (as you did here) is correct, and adding it for deviceDisplayName may have been erroneous due to a naive mimicking of surrounding code.

You mentioned in this PR deviceDisplayName was initially not optional, then the @Required annotation makes sense there, initially at least. I tested with and with out the annotation, both passes all the tests we currently have.

Regarding removing @Required from .deviceDisplayName, My concern is backward compatibility, especially JS target. As Defaults are not encoded, default null value will become undefined.

And, regarding the ApplicationDataSerializer not kicking in, my intuition seems right:
I suspect that is because the serializer used for that field is not "inherited" as you would expect it.

For this to work as expected, you would have to apply @Serializable( ApplicationDataSerializer::class ) to all extending classes of DeviceRegistration.
...
That would be a bit unfortunate if there is no way around this. It would be worthwhile checking the kotlinx serialization documentation to see whether they have any (new) features which can avoid the need for doing this.

This actually makes sense, as how kotlinx.serialization works with generic type parameters/abstract class, a serializer is required for each type/subclass. Note we also have @Serializale annotation for each subclass of DeviceRegistration. If @Serializable(with=..) for a field can be inherited, I would expect subclass also inherite @Serializable form superclass (which is not the case).

One work around I can think of is creating a new datatype/class to handle ApplicationData instead of using String, then we can set and enforce serializer for that class. The annotation will then only needed once when we declare the class, and ApplicationDataSerializer will be used as the default serializer, instead of String.serializer().

@Whathecode
Copy link
Member

One work around I can think of is creating a new datatype/class to handle ApplicationData instead of using String, then we can set and enforce serializer for that class. The annotation will then only needed once when we declare the class, and ApplicationDataSerializer will be used as the default serializer, instead of String.serializer().

Good idea!

I think I considered it in the past. Maybe I didn't pursue it since without inheritance there was no clear benefit. I shortly looked for past considerations, but couldn't find any, only the PR where it was introduced.

So, definitely worthwhile giving this a go! You may want to do that as a separate PR preceding this one. But, also fine as separate commit within this one. Whichever you prefer. :) I won't force push anymore without checking in with you; the branch is yours.

@yuanchen233
Copy link
Collaborator Author

Regarding removing @Required from .deviceDisplayName, My concern is backward compatibility, especially JS target. As Defaults are not encoded, default null value will become undefined.

I ran some tests and it is the case:

carp.deployments.core/src/commonTest/resources/test-requests/DeploymentService/1.0/DeploymentServiceTest/
createStudyDeployment_registers_preregistered_devices.json
Request #3 returned the wrong response. ==> expected: 

<{"deviceConfiguration":{...},"registration":{"__type":"dk.cachet.carp.common.application.devices.DefaultDeviceRegistration","registrationCreatedOn":"2022-04-04T15:03:22.341209Z","deviceDisplayName":null,"deviceId":"17124c6e-c8e0-457e-9b6b-f66860c4b666"},"connectedDevices":[{"__type":"dk.cachet.carp.common.infrastructure.test.StubDeviceConfiguration","roleName":"Connected"}],"connectedDeviceRegistrations":{"Connected":{"__type":"dk.cachet.carp.common.application.devices.DefaultDeviceRegistration","registrationCreatedOn":"2022-04-04T15:03:22.340210600Z","deviceDisplayName":null,"deviceId":"26d56d0e-2d78-4c5a-a428-bfd85d05da77"}}}> 
but was: 
<{"deviceConfiguration":{...},"registration":{"__type":"dk.cachet.carp.common.application.devices.DefaultDeviceRegistration","registrationCreatedOn":"2022-04-04T15:03:22.341209Z","deviceId":"17124c6e-c8e0-457e-9b6b-f66860c4b666"},"connectedDevices":[{"__type":"dk.cachet.carp.common.infrastructure.test.StubDeviceConfiguration","roleName":"Connected"}],"connectedDeviceRegistrations":{"Connected":{"__type":"dk.cachet.carp.common.application.devices.DefaultDeviceRegistration","registrationCreatedOn":"2022-04-04T15:03:22.340210600Z","deviceId":"26d56d0e-2d78-4c5a-a428-bfd85d05da77"}}}>

"deviceDisplayName":null is missing due to kotlinx serializer not generating default values. If we want to change this, I think it's best to create another issue and plan to fix it for the next major release version? As far as I remember we don't support backward compatibility cross major versions.

@Whathecode
Copy link
Member

Whathecode commented Oct 5, 2024

That can easily be resolved with a version migration, so only a minor version bump is needed.

But, definitely out of scope here. 👍

@Whathecode Whathecode force-pushed the extra-information-for-device-registration branch from 4c78321 to 1de2e41 Compare October 6, 2024 15:36
@Whathecode Whathecode merged commit df5affe into develop Oct 6, 2024
3 checks passed
@Whathecode Whathecode deleted the extra-information-for-device-registration branch October 6, 2024 16:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants