Skip to content
Snippets Groups Projects
Unverified Commit d848d7b4 authored by Kolya Opahle's avatar Kolya Opahle Committed by GitHub
Browse files

PPA: Collect Client Metadata (EXPOSUREAPP-5147) (#2373)


* Implemented ClientMetadataDonor which uses ClientVersionParser to extract the major minor and patch version from the app VERSION_CODE

Signed-off-by: default avatarKolya Opahle <k.opahle@sap.com>

* Restructured ClientMetadataDonor and added ClientMetadataDonorTests

Signed-off-by: default avatarKolya Opahle <k.opahle@sap.com>

* Fixed comment in ClientMetadataDonor

Signed-off-by: default avatarKolya Opahle <k.opahle@sap.com>

* Got rid of ClientVersionWrapper, switched all usages to ApiLevel
Added ability to generate version string to ClientVersionParser
Added ClientVersionParserTest that compares generated string with real one to make sure the parser is working
Changed ClientMetadataDonor to use currentConfig instead of getAppConfig

Signed-off-by: default avatarKolya Opahle <k.opahle@sap.com>

* Removed ClientVersionParser and put MAJOR, MINOR and PATCH version directly in the BuildConfig

Signed-off-by: default avatarKolya Opahle <k.opahle@sap.com>
parent fefae56d
No related branches found
Tags 1.6.0-SNAPSHOT-RC1
No related merge requests found
...@@ -64,6 +64,10 @@ android { ...@@ -64,6 +64,10 @@ android {
arguments += ["room.schemaLocation": "$projectDir/schemas".toString()] arguments += ["room.schemaLocation": "$projectDir/schemas".toString()]
} }
} }
buildConfigField "int", "VERSION_MAJOR", VERSION_MAJOR
buildConfigField "int", "VERSION_MINOR", VERSION_MINOR
buildConfigField "int", "VERSION_PATCH", VERSION_PATCH
} }
def signingPropFile = file("../keystore.properties") def signingPropFile = file("../keystore.properties")
......
...@@ -5,6 +5,7 @@ import dagger.Provides ...@@ -5,6 +5,7 @@ import dagger.Provides
import dagger.Reusable import dagger.Reusable
import dagger.multibindings.IntoSet import dagger.multibindings.IntoSet
import de.rki.coronawarnapp.datadonation.analytics.modules.DonorModule import de.rki.coronawarnapp.datadonation.analytics.modules.DonorModule
import de.rki.coronawarnapp.datadonation.analytics.modules.clientmetadata.ClientMetadataDonor
import de.rki.coronawarnapp.datadonation.analytics.modules.exposureriskmetadata.ExposureRiskMetadataDonor import de.rki.coronawarnapp.datadonation.analytics.modules.exposureriskmetadata.ExposureRiskMetadataDonor
import de.rki.coronawarnapp.datadonation.analytics.modules.usermetadata.UserMetadataDonor import de.rki.coronawarnapp.datadonation.analytics.modules.usermetadata.UserMetadataDonor
import de.rki.coronawarnapp.datadonation.analytics.server.DataDonationAnalyticsApiV1 import de.rki.coronawarnapp.datadonation.analytics.server.DataDonationAnalyticsApiV1
...@@ -60,6 +61,10 @@ class AnalyticsModule { ...@@ -60,6 +61,10 @@ class AnalyticsModule {
@Provides @Provides
fun userMetadata(module: UserMetadataDonor): DonorModule = module fun userMetadata(module: UserMetadataDonor): DonorModule = module
@IntoSet
@Provides
fun clientMetadata(module: ClientMetadataDonor): DonorModule = module
@Provides @Provides
@Singleton @Singleton
fun analyticsLogger(logger: DefaultLastAnalyticsSubmissionLogger): LastAnalyticsSubmissionLogger = logger fun analyticsLogger(logger: DefaultLastAnalyticsSubmissionLogger): LastAnalyticsSubmissionLogger = logger
......
package de.rki.coronawarnapp.datadonation.analytics.modules.clientmetadata
import de.rki.coronawarnapp.appconfig.AppConfigProvider
import de.rki.coronawarnapp.datadonation.analytics.modules.DonorModule
import de.rki.coronawarnapp.environment.BuildConfigWrap
import de.rki.coronawarnapp.nearby.ENFClient
import de.rki.coronawarnapp.server.protocols.internal.ppdd.PpaData
import de.rki.coronawarnapp.util.ApiLevel
import kotlinx.coroutines.flow.first
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ClientMetadataDonor @Inject constructor(
private val apiLevel: ApiLevel,
private val appConfigProvider: AppConfigProvider,
private val enfClient: ENFClient
) : DonorModule {
override suspend fun beginDonation(request: DonorModule.Request): DonorModule.Contribution {
val config = appConfigProvider.currentConfig.first()
val version = ClientVersion()
val clientMetadataBuilder = PpaData.PPAClientMetadataAndroid.newBuilder()
.setCwaVersion(version.toPPASemanticVersion())
.setAndroidApiLevel(apiLevel.currentLevel.toLong())
.setAppConfigETag(config.identifier)
enfClient.getENFClientVersion()?.let {
clientMetadataBuilder.setEnfVersion(it)
}
return ClientMetadataContribution(
contributionProto = clientMetadataBuilder.build()
)
}
override suspend fun deleteData() {
// Nothing to be deleted
}
data class ClientMetadataContribution(
val contributionProto: PpaData.PPAClientMetadataAndroid
) : DonorModule.Contribution {
override suspend fun injectData(protobufContainer: PpaData.PPADataAndroid.Builder) {
protobufContainer.clientMetadata = contributionProto
}
override suspend fun finishDonation(successful: Boolean) {
// No post processing needed for Client Metadata
}
}
data class ClientVersion(val major: Int, val minor: Int, val patch: Int) {
constructor() : this(
BuildConfigWrap.VERSION_MAJOR,
BuildConfigWrap.VERSION_MINOR,
BuildConfigWrap.VERSION_PATCH
)
fun toPPASemanticVersion(): PpaData.PPASemanticVersion =
PpaData.PPASemanticVersion.newBuilder()
.setMajor(major)
.setMinor(minor)
.setPatch(patch)
.build()
}
}
...@@ -10,4 +10,8 @@ object BuildConfigWrap { ...@@ -10,4 +10,8 @@ object BuildConfigWrap {
val ENVIRONMENT_TYPE_DEFAULT = BuildConfig.ENVIRONMENT_TYPE_DEFAULT val ENVIRONMENT_TYPE_DEFAULT = BuildConfig.ENVIRONMENT_TYPE_DEFAULT
val VERSION_CODE: Long = BuildConfig.VERSION_CODE.toLong() val VERSION_CODE: Long = BuildConfig.VERSION_CODE.toLong()
val VERSION_MAJOR: Int = BuildConfig.VERSION_MAJOR
val VERSION_MINOR: Int = BuildConfig.VERSION_MINOR
val VERSION_PATCH: Int = BuildConfig.VERSION_PATCH
} }
package de.rki.coronawarnapp.datadonation.analytics.modules.clientmetadata
import de.rki.coronawarnapp.appconfig.AppConfigProvider
import de.rki.coronawarnapp.appconfig.ConfigData
import de.rki.coronawarnapp.datadonation.analytics.modules.DonorModule
import de.rki.coronawarnapp.environment.BuildConfigWrap
import de.rki.coronawarnapp.nearby.ENFClient
import de.rki.coronawarnapp.server.protocols.internal.ppdd.PpaData
import de.rki.coronawarnapp.util.ApiLevel
import io.kotest.matchers.shouldBe
import io.mockk.MockKAnnotations
import io.mockk.clearAllMocks
import io.mockk.coEvery
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.mockkObject
import kotlinx.coroutines.flow.flowOf
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import testhelpers.BaseTest
import testhelpers.coroutines.runBlockingTest2
class ClientMetadataDonorTest : BaseTest() {
@MockK lateinit var apiLevel: ApiLevel
@MockK lateinit var appConfigProvider: AppConfigProvider
@MockK lateinit var configData: ConfigData
@MockK lateinit var enfClient: ENFClient
private val eTag = "testETag"
private val enfVersion = 1611L
private val androidVersionCode = 42L
private val versionMajor = 1
private val versionMinor = 11
private val versionPatch = 1
@BeforeEach
fun setup() {
MockKAnnotations.init(this)
mockkObject(BuildConfigWrap)
every { BuildConfigWrap.VERSION_MAJOR } returns versionMajor
every { BuildConfigWrap.VERSION_MINOR } returns versionMinor
every { BuildConfigWrap.VERSION_PATCH } returns versionPatch
every { apiLevel.currentLevel } returns androidVersionCode.toInt()
every { configData.identifier } returns eTag
coEvery { appConfigProvider.currentConfig } returns flowOf(configData)
coEvery { enfClient.getENFClientVersion() } returns enfVersion
}
@AfterEach
fun tearDown() {
clearAllMocks()
}
private fun createInstance() = ClientMetadataDonor(
apiLevel = apiLevel,
appConfigProvider = appConfigProvider,
enfClient = enfClient
)
@Test
fun `client metadata is properly collected`() {
val version = ClientMetadataDonor.ClientVersion().toPPASemanticVersion()
val expectedMetadata = PpaData.PPAClientMetadataAndroid.newBuilder()
.setAppConfigETag(eTag)
.setEnfVersion(enfVersion)
.setCwaVersion(version)
.setAndroidApiLevel(androidVersionCode)
.build()
val parentBuilder = PpaData.PPADataAndroid.newBuilder()
runBlockingTest2 {
val contribution = createInstance().beginDonation(object : DonorModule.Request {})
contribution.injectData(parentBuilder)
contribution.finishDonation(true)
}
val parentProto = parentBuilder.build()
parentProto.clientMetadata shouldBe expectedMetadata
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment