New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improved /settings/notifications push toggle error handling #4062
Conversation
@@ -130,6 +130,8 @@ dependencies { | |||
|
|||
// Database | |||
implementation 'com.github.Zhuinden:realm-monarchy:0.7.1' | |||
testImplementation libs.rx.rxKotlin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in order to mock the realm instance we need to include a test dependency on rx
as realm internally uses it and monarchy
doesn't include rx
in its transitive dependencies
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add the comment in the code? Also maybe move this line next to the other testImplementation
items (I have no strong opinion on this last remark)?
@Inject lateinit var pushersAPI: PushersAPI | ||
@Inject @SessionDatabase lateinit var monarchy: Monarchy | ||
@Inject lateinit var globalErrorReceiver: GlobalErrorReceiver | ||
@Inject lateinit var addPusherTask: AddPusherTask |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The worker now delegates to the extracted AddPusherTask
Result.success() | ||
} catch (exception: Throwable) { | ||
when (exception) { | ||
is Failure.NetworkConnection -> Result.retry() | ||
else -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
due to extracting the logic to a task there's now a slight change in behaviour, the AddPusherTask
will always update any existing pusher state on failure instead of exception != NetworkConnection
as the task can be used in non worker contexts (such as the settings page)
@@ -107,13 +103,6 @@ internal class DefaultPushersService @Inject constructor( | |||
return request.id | |||
} | |||
|
|||
private fun JsonPusher.validateParameters() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as we're always validating the payload I've lifted the logic as close of possible to the fields by doing it in the given class's init
private val realm = mockk<Realm>(relaxed = true) | ||
|
||
init { | ||
mockkStatic("org.matrix.android.sdk.internal.util.MonarchyKt") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in its current form the transactions call chain isn't very test friendly so I've opted to mock the top level awaitTransaction
extension
private var error: Throwable? = null | ||
|
||
override suspend fun getPushers(): GetPushersResponse { | ||
TODO("Not yet implemented") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the current tests don't need this yet, hence the throwing TODO, ideally this would be implemented when a test around fetching pushers is created
} | ||
} | ||
|
||
internal class FakePushersAPI : PushersAPI { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this PR introduces the concept of fakes (here's a nice refresher!), the main purpose in this context is to avoid hitting the real api and database and to provide some reusable assertion/verification helpers
I've left the implementations within the test file for easier discussion/reviewing, will extract out to their own package if the team is happy with them 🤞
|
||
pushersAPI.verifySetPusher(A_JSON_PUSHER) | ||
monarchy.verifyInsertOrUpdate<PusherEntity> { | ||
withArg { actual -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unfortunately the base entity models don't include any equality implementations so we have to manually check the fields
36c42f3
to
12df746
Compare
5033ea5
to
8316728
Compare
8627a08
to
a0d322f
Compare
} | ||
} | ||
|
||
internal interface RequestExecutor { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be extracted to its own file, which package would be best?
a0d322f
to
56bbeae
Compare
d701974
to
71fea25
Compare
…task from the worker
… toggle - uses the synchronous token registering which also means we get error handling
…request (without hitting the network)
…) tranisitive depends on rx but doesn't propagate it as an API dependency - without an explicit declaration we can't mock the realm instance
- introduces the concepts of Fakes for handling the dependencies, unforuntately realm/monarchy aren't very testable in their current state so we'll need to use mocks
- also creates a dedicated RequestModule instead of providing the executor via the pushers module
71fea25
to
e82de2b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for improving the test framework.
Some minor remarks
@@ -130,6 +130,8 @@ dependencies { | |||
|
|||
// Database | |||
implementation 'com.github.Zhuinden:realm-monarchy:0.7.1' | |||
testImplementation libs.rx.rxKotlin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add the comment in the code? Also maybe move this line next to the other testImplementation
items (I have no strong opinion on this last remark)?
* this is the routing or destination address information for the notification, | ||
* for example, the APNS token for APNS or the Registration ID for GCM. If your | ||
* notification client has no such concept, use any unique identifier. Max length, 512 chars. | ||
* If the kind is "email", this is the email address to send notifications to. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think with this Service API change, the kind should not be "email", so maybe remove this line?
internal interface RequestExecutor { | ||
suspend fun <DATA> executeRequest(globalErrorReceiver: GlobalErrorReceiver?, | ||
canRetry: Boolean = false, | ||
maxDelayBeforeRetry: Long = 32_000L, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this default value 32s ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it comes from here
The request executor is an injectable wrapper of the request
extension, open to other ideas for making this testable!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
} | ||
|
||
fun registerEmailForPush(email: String) { | ||
private fun httpPusher(pushKey: String) = PushersService.HttpPusher( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename to createHttpPusher
? functions should start with a verb
it.setTransactionalSwitchChangeListener(lifecycleScope) { isChecked -> | ||
if (isChecked) { | ||
FcmHelper.getFcmToken(requireContext())?.let { | ||
pushManager.registerPusherWithFcmKey(it) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's weird that in this case, we do not call session.refreshPushers()
(I see this is not a change from this PR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we move the refresh after change to the PusherManager
? 🤔 (and add to the registerPusherWithFcmKey
)
Carrying on from #4052 Email notifications
Adds a separate synchronous (via suspend) api for registering pushers in order to provide instant feedback within the settings page when an error occurs. The background enqueuing version is still used for the FCM token regeneration.
WorkManager
based implementation of theAddPusherWorker
into aAddPusherTask
which the worker now delegates to.AddPusherTask
which in turn meant extracting our aRequestExecutor
along with other test fakesEmail notification toggles whilst offline
Session notification toggles whilst offline