feat(event-manager): Allow passing query params to event loop request

As per API docs, the "Unread counters" for messages and conversations
are not returned by default but must be explicitly requested (historical
performance reasons on the backend).
This allows passing optional query params to set them

MAILANDR-1093
This commit is contained in:
Marino Meneghel 2023-12-06 16:46:56 +01:00
parent 76282a65aa
commit 56a2cf01b4
11 changed files with 100 additions and 19 deletions

View File

@ -11,6 +11,7 @@ public final class me/proton/core/eventmanager/dagger/BuildConfig {
public abstract interface class me/proton/core/eventmanager/dagger/CoreEventManagerModule {
public static final field Companion Lme/proton/core/eventmanager/dagger/CoreEventManagerModule$Companion;
public abstract fun bindsEventManagerQueryMapProvider ()Lme/proton/core/eventmanager/data/EventManagerQueryMapProvider;
public abstract fun provideEventManagerConfigProvider (Lme/proton/core/eventmanager/data/EventManagerConfigProviderImpl;)Lme/proton/core/eventmanager/domain/EventManagerConfigProvider;
public abstract fun provideEventMetadataRepository (Lme/proton/core/eventmanager/data/repository/EventMetadataRepositoryImpl;)Lme/proton/core/eventmanager/domain/repository/EventMetadataRepository;
public abstract fun provideEventWorkManager (Lme/proton/core/eventmanager/data/work/EventWorkerManagerImpl;)Lme/proton/core/eventmanager/domain/work/EventWorkerManager;

View File

@ -19,6 +19,7 @@
package me.proton.core.eventmanager.dagger
import dagger.Binds
import dagger.BindsOptionalOf
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -26,6 +27,7 @@ import dagger.hilt.components.SingletonComponent
import me.proton.core.eventmanager.data.EventManagerConfigProviderImpl
import me.proton.core.eventmanager.data.EventManagerFactory
import me.proton.core.eventmanager.data.EventManagerProviderImpl
import me.proton.core.eventmanager.data.EventManagerQueryMapProvider
import me.proton.core.eventmanager.data.IsCoreEventManagerEnabledImpl
import me.proton.core.eventmanager.data.repository.EventMetadataRepositoryImpl
import me.proton.core.eventmanager.data.work.EventWorkerManagerImpl
@ -57,6 +59,9 @@ public interface CoreEventManagerModule {
@Singleton
public fun provideEventWorkManager(impl: EventWorkerManagerImpl): EventWorkerManager
@BindsOptionalOf
public fun bindsEventManagerQueryMapProvider(): EventManagerQueryMapProvider
public companion object {
@Provides
@Singleton
@ -68,4 +73,5 @@ public interface CoreEventManagerModule {
): EventManagerProvider =
EventManagerProviderImpl(eventManagerFactory, eventManagerConfigProvider, eventListeners)
}
}

View File

@ -87,6 +87,10 @@ public final class me/proton/core/eventmanager/data/EventManagerProviderImpl : m
public fun getAll (Lme/proton/core/domain/entity/UserId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public abstract interface class me/proton/core/eventmanager/data/EventManagerQueryMapProvider {
public abstract fun getQueryMap (Lme/proton/core/eventmanager/domain/EventManagerConfig;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class me/proton/core/eventmanager/data/IsCoreEventManagerEnabledImpl : me/proton/core/eventmanager/domain/IsCoreEventManagerEnabled {
public static final field Companion Lme/proton/core/eventmanager/data/IsCoreEventManagerEnabledImpl$Companion;
public fun <init> (Landroid/content/Context;Lme/proton/core/featureflag/domain/FeatureFlagManager;)V
@ -106,10 +110,14 @@ public final class me/proton/core/eventmanager/data/IsCoreEventManagerEnabledImp
}
public abstract interface class me/proton/core/eventmanager/data/api/EventApi : me/proton/core/network/data/protonApi/BaseRetrofitApi {
public abstract fun getEvents (Ljava/lang/String;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getEvents (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getLatestEventId (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class me/proton/core/eventmanager/data/api/EventApi$DefaultImpls {
public static synthetic fun getEvents$default (Lme/proton/core/eventmanager/data/api/EventApi;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}
public final class me/proton/core/eventmanager/data/api/response/GetCalendarEventsResponse {
public static final field Companion Lme/proton/core/eventmanager/data/api/response/GetCalendarEventsResponse$Companion;
public synthetic fun <init> (ILjava/lang/String;IILkotlinx/serialization/internal/SerializationConstructorMarker;)V
@ -372,7 +380,7 @@ public final class me/proton/core/eventmanager/data/extension/EventMetadataKt {
}
public class me/proton/core/eventmanager/data/repository/EventMetadataRepositoryImpl : me/proton/core/data/file/FileContext, me/proton/core/eventmanager/domain/repository/EventMetadataRepository {
public fun <init> (Landroid/content/Context;Lme/proton/core/eventmanager/data/db/EventMetadataDatabase;Lme/proton/core/network/data/ApiProvider;)V
public fun <init> (Landroid/content/Context;Lme/proton/core/eventmanager/data/db/EventMetadataDatabase;Lme/proton/core/network/data/ApiProvider;Ljava/util/Optional;)V
public fun delete (Lme/proton/core/eventmanager/domain/EventManagerConfig;Lme/proton/core/eventmanager/domain/entity/EventId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun deleteAll (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun deleteAll (Lme/proton/core/eventmanager/domain/EventManagerConfig;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@ -386,7 +394,7 @@ public class me/proton/core/eventmanager/data/repository/EventMetadataRepository
public fun get (Lme/proton/core/eventmanager/domain/EventManagerConfig;Lme/proton/core/eventmanager/domain/entity/EventId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun getAll (Lme/proton/core/domain/entity/UserId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun getBaseDir ()Ljava/lang/String;
public fun getEvents (Lme/proton/core/domain/entity/UserId;Lme/proton/core/eventmanager/domain/entity/EventId;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun getEvents (Lme/proton/core/eventmanager/domain/EventManagerConfig;Lme/proton/core/eventmanager/domain/entity/EventId;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun getEvents (Lme/proton/core/eventmanager/domain/EventManagerConfig;Lme/proton/core/eventmanager/domain/entity/EventId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public synthetic fun getFile (Lme/proton/core/domain/entity/UniqueId;Lme/proton/core/domain/entity/UniqueId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun getFile (Lme/proton/core/eventmanager/domain/EventManagerConfig;Lme/proton/core/eventmanager/domain/entity/EventId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@ -405,11 +413,11 @@ public class me/proton/core/eventmanager/data/repository/EventMetadataRepository
}
public final class me/proton/core/eventmanager/data/repository/EventMetadataRepositoryImpl_Factory : dagger/internal/Factory {
public fun <init> (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)V
public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lme/proton/core/eventmanager/data/repository/EventMetadataRepositoryImpl_Factory;
public fun <init> (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)V
public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lme/proton/core/eventmanager/data/repository/EventMetadataRepositoryImpl_Factory;
public synthetic fun get ()Ljava/lang/Object;
public fun get ()Lme/proton/core/eventmanager/data/repository/EventMetadataRepositoryImpl;
public static fun newInstance (Landroid/content/Context;Lme/proton/core/eventmanager/data/db/EventMetadataDatabase;Lme/proton/core/network/data/ApiProvider;)Lme/proton/core/eventmanager/data/repository/EventMetadataRepositoryImpl;
public static fun newInstance (Landroid/content/Context;Lme/proton/core/eventmanager/data/db/EventMetadataDatabase;Lme/proton/core/network/data/ApiProvider;Ljava/util/Optional;)Lme/proton/core/eventmanager/data/repository/EventMetadataRepositoryImpl;
}
public class me/proton/core/eventmanager/data/work/EventWorker : androidx/work/CoroutineWorker {

View File

@ -29,7 +29,7 @@ protonBuild {
}
protonCoverage {
minBranchCoveragePercentage.set(60)
minBranchCoveragePercentage.set(59)
minLineCoveragePercentage.set(77)
}
@ -76,6 +76,7 @@ dependencies {
project(Module.contactData),
project(Module.keyData),
project(Module.kotlinTest),
project(Module.androidTest),
junit,
`kotlin-test`,
mockk

View File

@ -441,7 +441,7 @@ class EventManagerImpl @AssistedInject constructor(
.let { deserializer.deserializeLatestEventId(it) }
override suspend fun getEventResponse(eventId: EventId): EventsResponse =
eventMetadataRepository.getEvents(config.userId, eventId, deserializer.endpoint)
eventMetadataRepository.getEvents(config, eventId, deserializer.endpoint)
override suspend fun deserializeEventMetadata(eventId: EventId, response: EventsResponse): EventMetadata =
deserializer.deserializeEventMetadata(eventId, response)

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2022 Proton Technologies AG
* This file is part of Proton Technologies AG and Proton Mail.
*
* Proton Mail is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Proton Mail is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Proton Mail. If not, see <https://www.gnu.org/licenses/>.
*/
package me.proton.core.eventmanager.data
import me.proton.core.eventmanager.domain.EventManagerConfig
interface EventManagerQueryMapProvider {
suspend fun getQueryMap(config: EventManagerConfig): Map<String, String>
}

View File

@ -22,6 +22,7 @@ import me.proton.core.network.data.protonApi.BaseRetrofitApi
import okhttp3.ResponseBody
import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.QueryMap
interface EventApi : BaseRetrofitApi {
@ -33,6 +34,7 @@ interface EventApi : BaseRetrofitApi {
@GET("{endpoint}/{eventId}")
suspend fun getEvents(
@Path("endpoint", encoded = true) endpoint: String,
@Path("eventId") eventId: String
@Path("eventId") eventId: String,
@QueryMap params: Map<String, String>? = null
): ResponseBody
}

View File

@ -24,6 +24,7 @@ import me.proton.core.data.file.AndroidFileContext
import me.proton.core.data.file.ExperimentalProtonFileContext
import me.proton.core.data.file.FileContext
import me.proton.core.domain.entity.UserId
import me.proton.core.eventmanager.data.EventManagerQueryMapProvider
import me.proton.core.eventmanager.data.api.EventApi
import me.proton.core.eventmanager.data.db.EventMetadataDatabase
import me.proton.core.eventmanager.data.entity.EventMetadataEntity
@ -37,14 +38,17 @@ import me.proton.core.eventmanager.domain.entity.EventsResponse
import me.proton.core.eventmanager.domain.entity.State
import me.proton.core.eventmanager.domain.repository.EventMetadataRepository
import me.proton.core.network.data.ApiProvider
import java.util.Optional
import javax.inject.Inject
import kotlin.jvm.optionals.getOrNull
@OptIn(ExperimentalProtonFileContext::class)
open class EventMetadataRepositoryImpl @Inject constructor(
@ApplicationContext
private val context: Context,
private val db: EventMetadataDatabase,
private val apiProvider: ApiProvider
private val apiProvider: ApiProvider,
private val eventManagerQueryMapProvider: Optional<EventManagerQueryMapProvider>
) : EventMetadataRepository,
FileContext<EventManagerConfig, EventId> by AndroidFileContext("events", context) {
@ -144,11 +148,11 @@ open class EventMetadataRepositoryImpl @Inject constructor(
}.valueOrThrow
override suspend fun getEvents(
userId: UserId,
config: EventManagerConfig,
eventId: EventId,
endpoint: String
): EventsResponse = apiProvider.get<EventApi>(userId).invoke {
val response = getEvents(endpoint, eventId.id)
): EventsResponse = apiProvider.get<EventApi>(config.userId).invoke {
val response = getEvents(endpoint, eventId.id, eventManagerQueryMapProvider.getOrNull()?.getQueryMap(config))
EventsResponse(response.string())
}.valueOrThrow
}

View File

@ -19,6 +19,7 @@
package me.proton.core.eventmanager.data.repository
import android.content.Context
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
@ -26,19 +27,28 @@ import io.mockk.spyk
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import me.proton.core.domain.entity.UserId
import me.proton.core.eventmanager.data.EventManagerQueryMapProvider
import me.proton.core.eventmanager.data.api.EventApi
import me.proton.core.eventmanager.data.db.EventMetadataDatabase
import me.proton.core.eventmanager.domain.EventManagerConfig
import me.proton.core.eventmanager.domain.entity.EventId
import me.proton.core.eventmanager.domain.entity.EventMetadata
import me.proton.core.eventmanager.domain.entity.EventsResponse
import me.proton.core.network.data.ApiManagerFactory
import me.proton.core.network.data.ApiProvider
import me.proton.core.network.domain.session.SessionId
import me.proton.core.network.domain.session.SessionProvider
import me.proton.core.test.android.api.TestApiManager
import me.proton.core.test.kotlin.CoroutinesTest
import me.proton.core.test.kotlin.UnconfinedCoroutinesTest
import org.junit.Test
import java.nio.file.Files
import java.util.Optional
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.assertEquals
class EventMetadataRepositoryImplTest {
class EventMetadataRepositoryImplTest : CoroutinesTest by UnconfinedCoroutinesTest() {
private val userId1 = UserId("id1")
private val userId2 = UserId("id2")
@ -55,9 +65,18 @@ class EventMetadataRepositoryImplTest {
every { getDir(any(), any()) } returns Files.createTempDirectory("EventMetadataRepositoryImplTest-").toFile()
}
private val db = mockk<EventMetadataDatabase>(relaxed = true)
private val apiProvider = mockk<ApiProvider>(relaxed = true)
private val eventApi = mockk<EventApi>(relaxed = true)
private val sessionProvider = mockk<SessionProvider> {
coEvery { this@mockk.getSessionId(userId1) } returns SessionId("userId1-sessionId")
}
private val apiManagerFactory = mockk<ApiManagerFactory> {
every { this@mockk.create( any(), interfaceClass = EventApi::class ) } returns TestApiManager(eventApi)
}
private val db = mockk<EventMetadataDatabase>(relaxed = true)
private val eventManagerQueryMapProvider = mockk<EventManagerQueryMapProvider>(relaxed = true)
private lateinit var apiProvider: ApiProvider
private lateinit var tested: EventMetadataRepositoryImpl
private fun newMetadata(
@ -71,7 +90,8 @@ class EventMetadataRepositoryImplTest {
@BeforeTest
fun before() {
tested = spyk(EventMetadataRepositoryImpl(context, db, apiProvider))
apiProvider = ApiProvider(apiManagerFactory, sessionProvider, dispatchers)
tested = spyk(EventMetadataRepositoryImpl(context, db, apiProvider, Optional.of(eventManagerQueryMapProvider)))
}
@AfterTest
@ -180,4 +200,17 @@ class EventMetadataRepositoryImplTest {
coVerify { tested.deleteDir(user1CoreConfig) }
coVerify { tested.writeText(user1CoreConfig, eventId, any()) }
}
@Test
fun getEventsUsesQueryMapFromProviderWhenAvailable() = runTest {
// GIVEN
val endpoint = "core/events"
coEvery { eventManagerQueryMapProvider.getQueryMap(user1CoreConfig) } returns mapOf("MessageCounts" to "1")
// WHEN
tested.getEvents(user1CoreConfig, eventId, endpoint)
// THEN
coVerify { eventManagerQueryMapProvider.getQueryMap(user1CoreConfig) }
}
}

View File

@ -386,7 +386,7 @@ public abstract interface class me/proton/core/eventmanager/domain/repository/Ev
public abstract fun get (Lme/proton/core/eventmanager/domain/EventManagerConfig;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun get (Lme/proton/core/eventmanager/domain/EventManagerConfig;Lme/proton/core/eventmanager/domain/entity/EventId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getAll (Lme/proton/core/domain/entity/UserId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getEvents (Lme/proton/core/domain/entity/UserId;Lme/proton/core/eventmanager/domain/entity/EventId;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getEvents (Lme/proton/core/eventmanager/domain/EventManagerConfig;Lme/proton/core/eventmanager/domain/entity/EventId;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getEvents (Lme/proton/core/eventmanager/domain/EventManagerConfig;Lme/proton/core/eventmanager/domain/entity/EventId;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getLatestEventId (Lme/proton/core/domain/entity/UserId;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun update (Lme/proton/core/eventmanager/domain/entity/EventMetadata;Lme/proton/core/eventmanager/domain/entity/EventsResponse;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;

View File

@ -45,5 +45,5 @@ interface EventMetadataRepository {
suspend fun getEvents(config: EventManagerConfig, eventId: EventId): EventsResponse?
suspend fun getLatestEventId(userId: UserId, endpoint: String): EventIdResponse
suspend fun getEvents(userId: UserId, eventId: EventId, endpoint: String): EventsResponse
suspend fun getEvents(config: EventManagerConfig, eventId: EventId, endpoint: String): EventsResponse
}