extracting the add pusher logic for the worker and delegating to the task from the worker

This commit is contained in:
Adam Brown 2021-10-01 19:04:41 +01:00
parent 7088e5cf54
commit 6c9fcc0d93
7 changed files with 201 additions and 118 deletions

View file

@ -52,7 +52,25 @@ interface PushersService {
* (LiveData<WorkInfo> status = workManager.getWorkInfoByIdLiveData(<UUID>)) * (LiveData<WorkInfo> status = workManager.getWorkInfoByIdLiveData(<UUID>))
* @throws [InvalidParameterException] if a parameter is not correct * @throws [InvalidParameterException] if a parameter is not correct
*/ */
fun addHttpPusher(pushkey: String, suspend fun addHttpPusher(pushkey: String,
appId: String,
profileTag: String,
lang: String,
appDisplayName: String,
deviceDisplayName: String,
url: String,
append: Boolean,
withEventIdOnly: Boolean)
/**
* Enqueues a new HTTP pusher via the WorkManager API.
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-pushers-set
*
* @return A work request uuid. Can be used to listen to the status
* (LiveData<WorkInfo> status = workManager.getWorkInfoByIdLiveData(<UUID>))
* @throws [InvalidParameterException] if a parameter is not correct
*/
fun enqueueAddHttpPusher(pushkey: String,
appId: String, appId: String,
profileTag: String, profileTag: String,
lang: String, lang: String,
@ -75,16 +93,14 @@ interface PushersService {
* to any others with different user IDs. Otherwise, the homeserver must remove any other pushers * to any others with different user IDs. Otherwise, the homeserver must remove any other pushers
* with the same App ID and pushkey for different users. Typically We always want to append for * with the same App ID and pushkey for different users. Typically We always want to append for
* email pushers since we don't want to stop other accounts notifying to the same email address. * email pushers since we don't want to stop other accounts notifying to the same email address.
* @return A work request uuid. Can be used to listen to the status
* (LiveData<WorkInfo> status = workManager.getWorkInfoByIdLiveData(<UUID>))
* @throws [InvalidParameterException] if a parameter is not correct * @throws [InvalidParameterException] if a parameter is not correct
*/ */
fun addEmailPusher(email: String, suspend fun addEmailPusher(email: String,
lang: String, lang: String,
emailBranding: String, emailBranding: String,
appDisplayName: String, appDisplayName: String,
deviceDisplayName: String, deviceDisplayName: String,
append: Boolean = true): UUID append: Boolean = true)
/** /**
* Directly ask the push gateway to send a push to this device * Directly ask the push gateway to send a push to this device

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.session.pushers
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.session.pushers.PusherState
import org.matrix.android.sdk.internal.database.mapper.toEntity
import org.matrix.android.sdk.internal.database.model.PusherEntity
import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction
import javax.inject.Inject
internal interface AddPusherTask : Task<AddPusherTask.Params, Unit> {
data class Params(val pusher: JsonPusher)
}
internal class DefaultAddPusherTask @Inject constructor(
private val pushersAPI: PushersAPI,
@SessionDatabase private val monarchy: Monarchy,
private val globalErrorReceiver: GlobalErrorReceiver
) : AddPusherTask {
override suspend fun execute(params: AddPusherTask.Params) {
val pusher = params.pusher
try {
setPusher(pusher)
} catch (error: Throwable) {
monarchy.awaitTransaction { realm ->
PusherEntity.where(realm, pusher.pushKey).findFirst()?.let {
// update it
it.state = PusherState.FAILED_TO_REGISTER
}
}
throw error
}
}
private suspend fun setPusher(pusher: JsonPusher) {
executeRequest(globalErrorReceiver) {
pushersAPI.setPusher(pusher)
}
monarchy.awaitTransaction { realm ->
val echo = PusherEntity.where(realm, pusher.pushKey).findFirst()
if (echo != null) {
// update it
echo.appDisplayName = pusher.appDisplayName
echo.appId = pusher.appId
echo.kind = pusher.kind
echo.lang = pusher.lang
echo.profileTag = pusher.profileTag
echo.data?.format = pusher.data?.format
echo.data?.url = pusher.data?.url
echo.state = PusherState.REGISTERED
} else {
pusher.toEntity().also {
it.state = PusherState.REGISTERED
realm.insertOrUpdate(it)
}
}
}
}
}

View file

@ -18,17 +18,8 @@ package org.matrix.android.sdk.internal.session.pushers
import android.content.Context import android.content.Context
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.session.pushers.PusherState
import org.matrix.android.sdk.internal.database.mapper.toEntity
import org.matrix.android.sdk.internal.database.model.PusherEntity
import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.SessionComponent import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.util.awaitTransaction
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import javax.inject.Inject import javax.inject.Inject
@ -43,9 +34,7 @@ internal class AddPusherWorker(context: Context, params: WorkerParameters) :
override val lastFailureMessage: String? = null override val lastFailureMessage: String? = null
) : SessionWorkerParams ) : SessionWorkerParams
@Inject lateinit var pushersAPI: PushersAPI @Inject lateinit var addPusherTask: AddPusherTask
@Inject @SessionDatabase lateinit var monarchy: Monarchy
@Inject lateinit var globalErrorReceiver: GlobalErrorReceiver
override fun injectWith(injector: SessionComponent) { override fun injectWith(injector: SessionComponent) {
injector.inject(this) injector.inject(this)
@ -58,20 +47,12 @@ internal class AddPusherWorker(context: Context, params: WorkerParameters) :
return Result.failure() return Result.failure()
} }
return try { return try {
setPusher(pusher) addPusherTask.execute(AddPusherTask.Params(pusher))
Result.success() Result.success()
} catch (exception: Throwable) { } catch (exception: Throwable) {
when (exception) { when (exception) {
is Failure.NetworkConnection -> Result.retry() is Failure.NetworkConnection -> Result.retry()
else -> { else -> Result.failure()
monarchy.awaitTransaction { realm ->
PusherEntity.where(realm, pusher.pushKey).findFirst()?.let {
// update it
it.state = PusherState.FAILED_TO_REGISTER
}
}
Result.failure()
}
} }
} }
} }
@ -79,29 +60,4 @@ internal class AddPusherWorker(context: Context, params: WorkerParameters) :
override fun buildErrorParams(params: Params, message: String): Params { override fun buildErrorParams(params: Params, message: String): Params {
return params.copy(lastFailureMessage = params.lastFailureMessage ?: message) return params.copy(lastFailureMessage = params.lastFailureMessage ?: message)
} }
private suspend fun setPusher(pusher: JsonPusher) {
executeRequest(globalErrorReceiver) {
pushersAPI.setPusher(pusher)
}
monarchy.awaitTransaction { realm ->
val echo = PusherEntity.where(realm, pusher.pushKey).findFirst()
if (echo != null) {
// update it
echo.appDisplayName = pusher.appDisplayName
echo.appId = pusher.appId
echo.kind = pusher.kind
echo.lang = pusher.lang
echo.profileTag = pusher.profileTag
echo.data?.format = pusher.data?.format
echo.data?.url = pusher.data?.url
echo.state = PusherState.REGISTERED
} else {
pusher.toEntity().also {
it.state = PusherState.REGISTERED
realm.insertOrUpdate(it)
}
}
}
}
} }

View file

@ -30,7 +30,6 @@ import org.matrix.android.sdk.internal.session.pushers.gateway.PushGatewayNotify
import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.task.configureWith import org.matrix.android.sdk.internal.task.configureWith
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import java.security.InvalidParameterException
import java.util.UUID import java.util.UUID
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
@ -41,6 +40,7 @@ internal class DefaultPushersService @Inject constructor(
@SessionId private val sessionId: String, @SessionId private val sessionId: String,
private val getPusherTask: GetPushersTask, private val getPusherTask: GetPushersTask,
private val pushGatewayNotifyTask: PushGatewayNotifyTask, private val pushGatewayNotifyTask: PushGatewayNotifyTask,
private val addPusherTask: AddPusherTask,
private val removePusherTask: RemovePusherTask, private val removePusherTask: RemovePusherTask,
private val taskExecutor: TaskExecutor private val taskExecutor: TaskExecutor
) : PushersService { ) : PushersService {
@ -58,7 +58,7 @@ internal class DefaultPushersService @Inject constructor(
.executeBy(taskExecutor) .executeBy(taskExecutor)
} }
override fun addHttpPusher(pushkey: String, override fun enqueueAddHttpPusher(pushkey: String,
appId: String, appId: String,
profileTag: String, profileTag: String,
lang: String, lang: String,
@ -67,10 +67,11 @@ internal class DefaultPushersService @Inject constructor(
url: String, url: String,
append: Boolean, append: Boolean,
withEventIdOnly: Boolean withEventIdOnly: Boolean
) = addPusher( ): UUID {
return enqueueAddPusher(
JsonPusher( JsonPusher(
pushKey = pushkey, pushKey = pushkey,
kind = Pusher.KIND_HTTP, kind = "http",
appId = appId, appId = appId,
profileTag = profileTag, profileTag = profileTag,
lang = lang, lang = lang,
@ -80,15 +81,42 @@ internal class DefaultPushersService @Inject constructor(
append = append append = append
) )
) )
}
override fun addEmailPusher(email: String, override suspend fun addHttpPusher(pushkey: String,
appId: String,
profileTag: String,
lang: String,
appDisplayName: String,
deviceDisplayName: String,
url: String,
append: Boolean,
withEventIdOnly: Boolean) {
addPusherTask.execute(
AddPusherTask.Params(
JsonPusher(
pushKey = pushkey,
kind = "http",
appId = appId,
profileTag = profileTag,
lang = lang,
appDisplayName = appDisplayName,
deviceDisplayName = deviceDisplayName,
data = JsonPusherData(url, EVENT_ID_ONLY.takeIf { withEventIdOnly }),
append = append
)
)
)
}
override suspend fun addEmailPusher(email: String,
lang: String, lang: String,
emailBranding: String, emailBranding: String,
appDisplayName: String, appDisplayName: String,
deviceDisplayName: String, deviceDisplayName: String,
append: Boolean append: Boolean) {
) = addPusher( addPusherTask.execute(
JsonPusher( AddPusherTask.Params(JsonPusher(
pushKey = email, pushKey = email,
kind = Pusher.KIND_EMAIL, kind = Pusher.KIND_EMAIL,
appId = Pusher.APP_ID_EMAIL, appId = Pusher.APP_ID_EMAIL,
@ -98,11 +126,11 @@ internal class DefaultPushersService @Inject constructor(
deviceDisplayName = deviceDisplayName, deviceDisplayName = deviceDisplayName,
data = JsonPusherData(brand = emailBranding), data = JsonPusherData(brand = emailBranding),
append = append append = append
))
) )
) }
private fun addPusher(pusher: JsonPusher): UUID { private fun enqueueAddPusher(pusher: JsonPusher): UUID {
pusher.validateParameters()
val params = AddPusherWorker.Params(sessionId, pusher) val params = AddPusherWorker.Params(sessionId, pusher)
val request = workManagerProvider.matrixOneTimeWorkRequestBuilder<AddPusherWorker>() val request = workManagerProvider.matrixOneTimeWorkRequestBuilder<AddPusherWorker>()
.setConstraints(WorkManagerProvider.workConstraints) .setConstraints(WorkManagerProvider.workConstraints)
@ -113,13 +141,6 @@ internal class DefaultPushersService @Inject constructor(
return request.id return request.id
} }
private fun JsonPusher.validateParameters() {
// Do some parameter checks. It's ok to throw Exception, to inform developer of the problem
if (pushKey.length > 512) throw InvalidParameterException("pushkey should not exceed 512 chars")
if (appId.length > 64) throw InvalidParameterException("appId should not exceed 64 chars")
data?.url?.let { url -> if ("/_matrix/push/v1/notify" !in url) throw InvalidParameterException("url should contain '/_matrix/push/v1/notify'") }
}
override suspend fun removePusher(pusher: Pusher) { override suspend fun removePusher(pusher: Pusher) {
removePusher(pusher.pushKey, pusher.appId) removePusher(pusher.pushKey, pusher.appId)
} }

View file

@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.pushers
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.internal.di.SerializeNulls import org.matrix.android.sdk.internal.di.SerializeNulls
import java.security.InvalidParameterException
/** /**
* Example: * Example:
@ -112,4 +113,11 @@ internal data class JsonPusher(
*/ */
@Json(name = "append") @Json(name = "append")
val append: Boolean? = false val append: Boolean? = false
) ) {
init {
// Do some parameter checks. It's ok to throw Exception, to inform developer of the problem
if (pushKey.length > 512) throw InvalidParameterException("pushkey should not exceed 512 chars")
if (appId.length > 64) throw InvalidParameterException("appId should not exceed 64 chars")
data?.url?.let { url -> if ("/_matrix/push/v1/notify" !in url) throw InvalidParameterException("url should contain '/_matrix/push/v1/notify'") }
}
}

View file

@ -65,6 +65,9 @@ internal abstract class PushersModule {
@Binds @Binds
abstract fun bindSavePushRulesTask(task: DefaultSavePushRulesTask): SavePushRulesTask abstract fun bindSavePushRulesTask(task: DefaultSavePushRulesTask): SavePushRulesTask
@Binds
abstract fun bindAddPusherTask(task: DefaultAddPusherTask): AddPusherTask
@Binds @Binds
abstract fun bindRemovePusherTask(task: DefaultRemovePusherTask): RemovePusherTask abstract fun bindRemovePusherTask(task: DefaultRemovePusherTask): RemovePusherTask

View file

@ -48,7 +48,7 @@ class PushersManager @Inject constructor(
val currentSession = activeSessionHolder.getActiveSession() val currentSession = activeSessionHolder.getActiveSession()
val profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + abs(currentSession.myUserId.hashCode()) val profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + abs(currentSession.myUserId.hashCode())
return currentSession.addHttpPusher( return currentSession.enqueueAddHttpPusher(
pushKey, pushKey,
stringProvider.getString(R.string.pusher_app_id), stringProvider.getString(R.string.pusher_app_id),
profileTag, profileTag,
@ -61,7 +61,7 @@ class PushersManager @Inject constructor(
) )
} }
fun registerEmailForPush(email: String) { suspend fun registerEmailForPush(email: String) {
val currentSession = activeSessionHolder.getActiveSession() val currentSession = activeSessionHolder.getActiveSession()
val appName = appNameProvider.getAppName() val appName = appNameProvider.getAppName()
currentSession.addEmailPusher( currentSession.addEmailPusher(