Skip to content

Commit

Permalink
Release 1.2.1
Browse files Browse the repository at this point in the history
Merge pull request #475 from cph-cachet/develop
  • Loading branch information
Whathecode authored Apr 16, 2024
2 parents 0a465e9 + f4c3994 commit 9b82f17
Show file tree
Hide file tree
Showing 15 changed files with 67 additions and 34 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ buildscript {
ext {
// Version used for submodule artifacts.
// Snapshot publishing changes (or adds) the suffix after '-' with 'SNAPSHOT' prior to publishing.
globalVersion = '1.2.0'
clientsVersion = '1.2.0-alpha.1' // The clients subsystem is still expected to change drastically.
globalVersion = '1.2.1'
clientsVersion = '1.2.1-alpha.1' // The clients subsystem is still expected to change drastically.

versions = [
// Kotlin multiplatform versions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package dk.cachet.carp.common.infrastructure.reflect

/**
* Provide access to runtime internals which cannot be accessed at compile time.
*
* Warning: on JavaScript targets, this only works for non-minified sources. Variables which aren't exported are mangled
* completely. So this should not be used in production code! It may still be useful for test automation.
*/
internal expect object AccessInternals
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package dk.cachet.carp.common.infrastructure.serialization

import dk.cachet.carp.common.infrastructure.reflect.AccessInternals
import dk.cachet.carp.common.infrastructure.reflect.reflectIfAvailable
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
Expand Down Expand Up @@ -58,18 +57,30 @@ abstract class UnknownPolymorphicSerializer<P : Any, W : P>(
{
throw unsupportedException
}
getClassDiscriminator( encoder.json ) // Throws error in case array polymorphism is used.
val classDiscriminator = getClassDiscriminator( encoder.json ) // Throws error in case of array polymorphism.

// Get the unknown JSON object.
check( value is UnknownPolymorphicWrapper )
val unknown = Json.parseToJsonElement( value.jsonSource ) as JsonObject
val unknownTypeFields = unknown.filter { it.key != classDiscriminator }

// Create a serial descriptor which contains all elements of the unknown JSON object, except type discriminator.
// If the encoder is in polymorphic writing mode, a class discriminator field will automatically be added using
// the serial name of the descriptor.
val unknownType = checkNotNull( unknown[ classDiscriminator ]?.jsonPrimitive?.content )
val jsonSerializer = JsonElement.serializer()
val overrideDescriptor = buildClassSerialDescriptor( unknownType )
{
unknownTypeFields.keys.forEach { element( it, jsonSerializer.descriptor ) }
}

// HACK: Modify kotlinx.serialization internals to ensure the encoder is not in polymorphic mode.
// Otherwise, `encoder.encodeJsonElement` encodes type information, but this is already represented in the wrapped unknown object.
AccessInternals.setField( encoder, "polymorphicDiscriminator", null )

// Output the originally wrapped JSON.
encoder.encodeJsonElement( unknown )
// Write the JSON object.
encoder.encodeStructure( overrideDescriptor )
{
var id = 0
for ( field in unknownTypeFields.values )
encodeSerializableElement( overrideDescriptor, id++, JsonElement.serializer(), field )
}
}

override fun deserialize( decoder: Decoder ): P
Expand All @@ -85,7 +96,8 @@ abstract class UnknownPolymorphicSerializer<P : Any, W : P>(
// Get raw JSON for the unknown type.
val jsonElement = decoder.decodeJsonElement()
val jsonSource = jsonElement.toString()
val className = jsonElement.jsonObject[ classDiscriminator ]!!.jsonPrimitive.content
val className = requireNotNull( jsonElement.jsonObject[ classDiscriminator ]?.jsonPrimitive?.content )
{ "Can't deserialize type which was serialized non-polymorphically." }

return createWrapper( className, jsonSource, decoder.json )
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,13 @@ abstract class ConcreteTypesSerializationTest(
for ( toSerialize in instancesToSerialize )
{
// Get serializer.
// Polymorphic serializer is used to ensure type information is always embedded,
// which some deserializers may depend upon.
val type = toSerialize::class
val registration = polymorphicSerializers[ type ]
assertNotNull( registration, "No serializer registered for type '$type'" )
@Suppress( "UNCHECKED_CAST" )
val serializer = polymorphicSerializers[ type ] as? KSerializer<Any>
assertNotNull( serializer, "No serializer registered for type '$type'" )
val serializer = PolymorphicSerializer( registration.baseKlass ) as KSerializer<Any>

// Verify whether serializing and deserializing the instance results in the same object.
val serialized = json.encodeToString( serializer, toSerialize )
Expand All @@ -67,12 +70,12 @@ abstract class ConcreteTypesSerializationTest(
* Get a map which holds the [KSerializer] for each [KClass] registered for polymorphic serialization in [serialModule].
*/
@ExperimentalSerializationApi
fun getPolymorphicSerializers( serialModule: SerializersModule ): Map<KClass<*>, KSerializer<*>>
fun getPolymorphicSerializers( serialModule: SerializersModule ): Map<KClass<*>, PolymorphicRegistration>
{
val collector =
object : SerializersModuleCollector
{
val serializers: MutableMap<KClass<*>, KSerializer<*>> = mutableMapOf()
val serializers: MutableMap<KClass<*>, PolymorphicRegistration> = mutableMapOf()

override fun <T : Any> contextual(
kClass: KClass<T>,
Expand All @@ -85,7 +88,7 @@ fun getPolymorphicSerializers( serialModule: SerializersModule ): Map<KClass<*>,
actualSerializer: KSerializer<Sub>
)
{
serializers[ actualClass ] = actualSerializer
serializers[ actualClass ] = PolymorphicRegistration( baseClass, actualSerializer )
}

@Suppress( "REDUNDANT_PROJECTION", "KotlinRedundantDiagnosticSuppress" ) // Exact override needed.
Expand All @@ -109,3 +112,6 @@ fun getPolymorphicSerializers( serialModule: SerializersModule ): Map<KClass<*>,
serialModule.dumpTo( collector )
return collector.serializers
}


data class PolymorphicRegistration( val baseKlass: KClass<*>, val serializer: KSerializer<*> )
2 changes: 1 addition & 1 deletion typescript-declarations/carp-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"name": "@cachet/carp-common",
"type": "module",
"main": "index.js",
"version": "1.1.2"
"version": "1.2.1"
}
2 changes: 1 addition & 1 deletion typescript-declarations/carp-data-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"name": "@cachet/carp-data-core",
"type": "module",
"main": "index.js",
"version": "1.1.2"
"version": "1.2.1"
}
2 changes: 1 addition & 1 deletion typescript-declarations/carp-deployments-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"name": "@cachet/carp-deployments-core",
"type": "module",
"main": "index.js",
"version": "1.1.2"
"version": "1.2.1"
}
2 changes: 1 addition & 1 deletion typescript-declarations/carp-kotlin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"name": "@cachet/carp-kotlin",
"type": "module",
"main": "index.js",
"version": "1.1.2"
"version": "1.2.1"
}
2 changes: 1 addition & 1 deletion typescript-declarations/carp-kotlinx-datetime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"name": "@cachet/carp-kotlinx-datetime",
"type": "module",
"main": "index.js",
"version": "1.1.2"
"version": "1.2.1"
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import extendJson from "@cachet/kotlinx-serialization-kotlinx-serialization-json
// Facade with better method names and type conversions for internal types.
export namespace kotlinx.serialization
{
export function getSerializer( type: any ) { return type.Companion.t16() }
export function getSerializer( type: any ) { return type.Companion.m16() }
}
export namespace kotlinx.serialization.json
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"name": "@cachet/carp-kotlinx-serialization",
"type": "module",
"main": "index.js",
"version": "1.1.2"
"version": "1.2.1"
}
2 changes: 1 addition & 1 deletion typescript-declarations/carp-protocols-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"name": "@cachet/carp-protocols-core",
"type": "module",
"main": "index.js",
"version": "1.1.2"
"version": "1.2.1"
}
2 changes: 1 addition & 1 deletion typescript-declarations/carp-studies-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"name": "@cachet/carp-studies-core",
"type": "module",
"main": "index.js",
"version": "1.1.2"
"version": "1.2.1"
}
16 changes: 8 additions & 8 deletions typescript-declarations/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion typescript-declarations/tests/carp-protocols-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import ProtocolServiceRequest = protocols.infrastructure.ProtocolServiceRequest

const serializedSnapshot = `{"id":"ad4ca03a-6f69-4a95-8701-488dc511925b","createdOn":"2023-01-30T21:02:56.068710100Z","version":0,"ownerId":"27879e75-ccc1-4866-9ab3-4ece1b735052","name":"Test protocol","description":"Test description","primaryDevices":[{"__type":"dk.cachet.carp.common.infrastructure.test.StubPrimaryDeviceConfiguration","isPrimaryDevice":true,"roleName":"Stub primary device"}],"connectedDevices":[{"__type":"dk.cachet.carp.common.infrastructure.test.StubDeviceConfiguration","roleName":"Stub device"},{"__type":"dk.cachet.carp.common.infrastructure.test.StubPrimaryDeviceConfiguration","isPrimaryDevice":true,"roleName":"Chained primary"},{"__type":"dk.cachet.carp.common.infrastructure.test.StubDeviceConfiguration","roleName":"Chained connected"}],"connections":[{"roleName":"Stub device","connectedToRoleName":"Stub primary device"},{"roleName":"Chained primary","connectedToRoleName":"Stub primary device"},{"roleName":"Chained connected","connectedToRoleName":"Chained primary"}],"tasks":[{"__type":"dk.cachet.carp.common.infrastructure.test.StubTaskConfiguration","name":"Task","measures":[{"__type":"dk.cachet.carp.common.application.tasks.Measure.DataStream","type":"dk.cachet.carp.stubpoint"}]}],"triggers":{"0":{"__type":"dk.cachet.carp.common.infrastructure.test.StubTriggerConfiguration","sourceDeviceRoleName":"Stub device"}},"taskControls":[{"triggerId":0,"taskName":"Task","destinationDeviceRoleName":"Stub primary device","control":"Start"}],"participantRoles":[{"role":"Role","isOptional":false},{"role":"Optional role","isOptional":true}],"assignedDevices":{"Stub primary device":["Role"]},"expectedParticipantData":[{"attribute":{"__type":"dk.cachet.carp.common.application.users.ParticipantAttribute.DefaultParticipantAttribute","inputDataType":"some.type"}},{"attribute":{"__type":"dk.cachet.carp.common.application.users.ParticipantAttribute.DefaultParticipantAttribute","inputDataType":"dk.cachet.carp.input.sex"},"assignedTo":{"__type":"dk.cachet.carp.common.application.users.AssignedTo.Roles","roleNames":["Role"]}}]}`


describe( "carp-protocols-core", () => {
describe( "StudyProtocolSnapshot", () => {
it( "can deserialize", () => {
Expand All @@ -28,6 +27,19 @@ describe( "carp-protocols-core", () => {
expect( parsed ).is.instanceOf( StudyProtocolSnapshot )
} )

it( "can deserialize and serialize unknown types", () => {
const snapshotWithUnknownTypes = serializedSnapshot.replace(
"dk.cachet.carp.common.infrastructure.test.StubTaskConfiguration",
"com.unknown.CustomTaskConfiguration"
)
const serializer = getSerializer( StudyProtocolSnapshot )

const parsed = JSON.decodeFromString( serializer, snapshotWithUnknownTypes )
const serialized = JSON.encodeToString( serializer, parsed )

expect( snapshotWithUnknownTypes ).equals( serialized )
} )

it( "can access snapshot properties", () => {
const id = UUID.Companion.randomUUID()
const now = Clock.System.now()
Expand Down

0 comments on commit 9b82f17

Please sign in to comment.