From 8bc4e400ddbd7b672a440ba22623284576c0c72c Mon Sep 17 00:00:00 2001 From: xelahalo Date: Mon, 11 Mar 2024 18:45:57 +0100 Subject: [PATCH] Add `Website` primary device --- .../common/application/devices/Website.kt | 78 +++++++++++++++++++ .../serialization/Serialization.kt | 2 + .../carp/common/application/TestInstances.kt | 5 ++ .../common/application/devices/WebsiteTest.kt | 24 ++++++ docs/carp-common.md | 1 + .../common/devices/DeviceRegistration.json | 4 + .../devices/PrimaryDeviceConfiguration.json | 4 + rpc/schemas/common/devices/Website.json | 15 ++++ .../devices/WebsiteDeviceRegistration.json | 11 +++ 9 files changed, 144 insertions(+) create mode 100644 carp.common/src/commonMain/kotlin/dk/cachet/carp/common/application/devices/Website.kt create mode 100644 carp.common/src/commonTest/kotlin/dk/cachet/carp/common/application/devices/WebsiteTest.kt create mode 100644 rpc/schemas/common/devices/Website.json create mode 100644 rpc/schemas/common/devices/WebsiteDeviceRegistration.json diff --git a/carp.common/src/commonMain/kotlin/dk/cachet/carp/common/application/devices/Website.kt b/carp.common/src/commonMain/kotlin/dk/cachet/carp/common/application/devices/Website.kt new file mode 100644 index 000000000..28b0f5716 --- /dev/null +++ b/carp.common/src/commonMain/kotlin/dk/cachet/carp/common/application/devices/Website.kt @@ -0,0 +1,78 @@ +@file:Suppress( "NON_EXPORTABLE_TYPE" ) + +package dk.cachet.carp.common.application.devices + +import dk.cachet.carp.common.application.Trilean +import dk.cachet.carp.common.application.data.DataType +import dk.cachet.carp.common.application.sampling.DataTypeSamplingSchemeMap +import dk.cachet.carp.common.application.sampling.SamplingConfiguration +import dk.cachet.carp.common.application.tasks.TaskConfigurationList +import dk.cachet.carp.common.infrastructure.serialization.NotSerializable +import kotlinx.serialization.Required +import kotlinx.serialization.Serializable +import kotlin.js.JsExport +import kotlin.reflect.KClass + + +/** + * A website which participates in a study as a primary device. + */ +@Serializable +@JsExport +data class Website( + override val roleName: String, + override val isOptional: Boolean = false +) : PrimaryDeviceConfiguration() +{ + object Sensors : DataTypeSamplingSchemeMap() + object Tasks : TaskConfigurationList() + + override fun getSupportedDataTypes(): Set = Sensors.keys + override fun getDataTypeSamplingSchemes(): DataTypeSamplingSchemeMap = Sensors + + override val defaultSamplingConfiguration: Map = emptyMap() + + override fun createDeviceRegistrationBuilder(): WebsiteDeviceRegistrationBuilder = + WebsiteDeviceRegistrationBuilder() + override fun getRegistrationClass(): KClass = WebsiteDeviceRegistration::class + override fun isValidRegistration( registration: WebsiteDeviceRegistration ): Trilean = Trilean.TRUE +} + + +/** + * A [DeviceRegistration] for a [Website], specifying the [url] where the study runs. + */ +@Serializable +@JsExport +data class WebsiteDeviceRegistration( + val url: String, + /** + * The HTTP User-Agent header of the user agent which made the HTTP request to [url]. + */ + val userAgent: String, + @Required + override val deviceDisplayName: String? = userAgent +) : DeviceRegistration() +{ + @Required + override val deviceId: String = url +} + + +@Suppress( "SERIALIZER_TYPE_INCOMPATIBLE" ) +@Serializable( NotSerializable::class ) +@JsExport +class WebsiteDeviceRegistrationBuilder : DeviceRegistrationBuilder() +{ + /** + * The web URL from which the [Website] is accessed. + */ + var url: String = "" + + /** + * The HTTP User-Agent header of the user agent which made the HTTP request to [url]. + */ + var userAgent: String = "" + + override fun build(): WebsiteDeviceRegistration = WebsiteDeviceRegistration( url, userAgent, deviceDisplayName ) +} diff --git a/carp.common/src/commonMain/kotlin/dk/cachet/carp/common/infrastructure/serialization/Serialization.kt b/carp.common/src/commonMain/kotlin/dk/cachet/carp/common/infrastructure/serialization/Serialization.kt index d1e70a131..26faf6643 100644 --- a/carp.common/src/commonMain/kotlin/dk/cachet/carp/common/infrastructure/serialization/Serialization.kt +++ b/carp.common/src/commonMain/kotlin/dk/cachet/carp/common/infrastructure/serialization/Serialization.kt @@ -63,6 +63,7 @@ val COMMON_SERIAL_MODULE = SerializersModule { { subclass( CustomProtocolDevice::class ) subclass( Smartphone::class ) + subclass( Website::class ) subclass( CustomPrimaryDeviceConfiguration::class ) } @@ -87,6 +88,7 @@ val COMMON_SERIAL_MODULE = SerializersModule { subclass( BLESerialNumberDeviceRegistration::class ) subclass( DefaultDeviceRegistration::class ) subclass( MACAddressDeviceRegistration::class ) + subclass( WebsiteDeviceRegistration::class ) subclass( CustomDeviceRegistration::class ) defaultDeserializer { DeviceRegistrationSerializer } diff --git a/carp.common/src/commonTest/kotlin/dk/cachet/carp/common/application/TestInstances.kt b/carp.common/src/commonTest/kotlin/dk/cachet/carp/common/application/TestInstances.kt index a699a0a5d..192f73e58 100644 --- a/carp.common/src/commonTest/kotlin/dk/cachet/carp/common/application/TestInstances.kt +++ b/carp.common/src/commonTest/kotlin/dk/cachet/carp/common/application/TestInstances.kt @@ -48,6 +48,11 @@ val commonInstances = listOf( BLESerialNumberDeviceRegistration( "123456789" ), CustomProtocolDevice( "User's phone" ), Smartphone( "User's phone" ), + Website( "Study website" ), + WebsiteDeviceRegistration( + "https://www.examplestudy.com?studyId=42", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" + ), // Shared device registrations in `devices` namespace. DefaultDeviceRegistration(), diff --git a/carp.common/src/commonTest/kotlin/dk/cachet/carp/common/application/devices/WebsiteTest.kt b/carp.common/src/commonTest/kotlin/dk/cachet/carp/common/application/devices/WebsiteTest.kt new file mode 100644 index 000000000..2b285ab65 --- /dev/null +++ b/carp.common/src/commonTest/kotlin/dk/cachet/carp/common/application/devices/WebsiteTest.kt @@ -0,0 +1,24 @@ +package dk.cachet.carp.common.application.devices + +import kotlin.test.Test +import kotlin.test.assertEquals + + +class WebsiteTest +{ + @Test + fun registration_builder_sets_properties() + { + val expectedUrl = "https://www.example.com" + val expectedUserAgent = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" + + val registration = WebsiteDeviceRegistrationBuilder().apply { + url = expectedUrl + userAgent = expectedUserAgent + }.build() + + assertEquals( expectedUrl, registration.url ) + assertEquals( expectedUserAgent, registration.userAgent ) + } +} diff --git a/docs/carp-common.md b/docs/carp-common.md index 792ca4a1b..93e04cd23 100644 --- a/docs/carp-common.md +++ b/docs/carp-common.md @@ -73,6 +73,7 @@ act as a hub to aggregate, synchronize, and upload incoming data received from o | Class | Primary | Description | |--------------------------------------------------------------------------------------------------------------------------------|:-------:|----------------------------------------------------------------------------------------------------------| | [Smartphone](../carp.common/src/commonMain/kotlin/dk/cachet/carp/common/application/devices/Smartphone.kt) | Yes | An internet-connected phone with built-in sensors. | +| [Website](../carp.common/src/commonMain/kotlin/dk/cachet/carp/common/application/devices/Website.kt) | Yes | A website which participates in a study as a primary device. | | [AltBeacon](../carp.common/src/commonMain/kotlin/dk/cachet/carp/common/application/devices/AltBeacon.kt) | | A beacon meeting the open AltBeacon standard. | | [BLEHeartRateDevice](../carp.common/src/commonMain/kotlin/dk/cachet/carp/common/application/devices/BLEHeartRateDevice.kt) | | A Bluetooth device which implements a Heart Rate service. | | [CustomProtocolDevice](../carp.common/src/commonMain/kotlin/dk/cachet/carp/common/application/devices/CustomProtocolDevice.kt) | Yes | A primary device which uses a single `CustomProtocolTask` to determine how to run a study on the device. | diff --git a/rpc/schemas/common/devices/DeviceRegistration.json b/rpc/schemas/common/devices/DeviceRegistration.json index a5544ad50..b9a1cdc7f 100644 --- a/rpc/schemas/common/devices/DeviceRegistration.json +++ b/rpc/schemas/common/devices/DeviceRegistration.json @@ -17,6 +17,10 @@ { "if": { "properties": { "__type": { "const": "dk.cachet.carp.common.application.devices.MACAddressDeviceRegistration" } } }, "then": { "$ref": "MACAddressDeviceRegistration.json" } + }, + { + "if": { "properties": { "__type": { "const": "dk.cachet.carp.common.application.devices.WebsiteDeviceRegistration" } } }, + "then": { "$ref": "WebsiteDeviceRegistration.json" } } ], "$defs": { diff --git a/rpc/schemas/common/devices/PrimaryDeviceConfiguration.json b/rpc/schemas/common/devices/PrimaryDeviceConfiguration.json index a472b5de3..ef4de9950 100644 --- a/rpc/schemas/common/devices/PrimaryDeviceConfiguration.json +++ b/rpc/schemas/common/devices/PrimaryDeviceConfiguration.json @@ -5,6 +5,10 @@ { "if": { "properties": { "__type": { "const": "dk.cachet.carp.common.application.devices.Smartphone" } } }, "then": { "$ref": "Smartphone.json" } + }, + { + "if": { "properties": { "__type": { "const": "dk.cachet.carp.common.application.devices.Website" } } }, + "then": { "$ref": "Website.json" } } ], "$defs": { diff --git a/rpc/schemas/common/devices/Website.json b/rpc/schemas/common/devices/Website.json new file mode 100644 index 000000000..41666b08a --- /dev/null +++ b/rpc/schemas/common/devices/Website.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "type": "object", + "allOf": [ { "$ref": "PrimaryDeviceConfiguration.json#PrimaryDeviceConfiguration" } ], + "properties": { + "__type": { "const": "dk.cachet.carp.common.application.devices.Website" } + }, + "unevaluatedProperties": false, + "$defs": { + "DeviceRegistration": { + "$anchor": "DeviceRegistration", + "$ref": "WebsiteDeviceRegistration.json" + } + } +} \ No newline at end of file diff --git a/rpc/schemas/common/devices/WebsiteDeviceRegistration.json b/rpc/schemas/common/devices/WebsiteDeviceRegistration.json new file mode 100644 index 000000000..984265510 --- /dev/null +++ b/rpc/schemas/common/devices/WebsiteDeviceRegistration.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "type": "object", + "allOf": [ { "$ref": "DeviceRegistration.json#DeviceRegistration" } ], + "properties": { + "url": { "type": "string", "format": "uri" }, + "userAgent": { "type": "string" } + }, + "required": [ "url", "userAgent" ], + "unevaluatedProperties": false +}