Skip to content

saving sync filter changed #7627

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

Merged
merged 10 commits into from
Nov 28, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.d/7626.sdk
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Sync Filter now taking in account homeserver capabilities to not pass unsupported parameters.
Sync Filter is now configured by providing SyncFilterBuilder class instance, instead of Filter to identify Filter changes related to homeserver capabilities
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,12 @@

package org.matrix.android.sdk.api.session.sync

interface FilterService {

enum class FilterPreset {
NoFilter,
import org.matrix.android.sdk.internal.session.filter.SyncFilterBuilder

/**
* Filter for Element, will include only known event type.
*/
ElementFilter
}
interface FilterService {

/**
* Configure the filter for the sync.
*/
fun setFilter(filterPreset: FilterPreset)
fun setSyncFilter(filterBuilder: SyncFilterBuilder)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2020 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.api.session.sync.filter

internal data class SyncFilterParams(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it's internal it should be moved to the internal package.

val lazyLoadMembersForStateEvents: Boolean? = null,
val lazyLoadMembersForMessageEvents: Boolean? = null,
val useThreadNotifications: Boolean? = null,
val listOfSupportedEventTypes: List<String>? = null,
val listOfSupportedStateEventTypes: List<String>? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo039
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo040
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo041
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo042
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo043
import org.matrix.android.sdk.internal.util.Normalizer
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
import javax.inject.Inject
Expand All @@ -67,7 +68,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
private val normalizer: Normalizer
) : MatrixRealmMigration(
dbName = "Session",
schemaVersion = 42L,
schemaVersion = 43L,
) {
/**
* Forces all RealmSessionStoreMigration instances to be equal.
Expand Down Expand Up @@ -119,5 +120,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
if (oldVersion < 40) MigrateSessionTo040(realm).perform()
if (oldVersion < 41) MigrateSessionTo041(realm).perform()
if (oldVersion < 42) MigrateSessionTo042(realm).perform()
if (oldVersion < 43) MigrateSessionTo043(realm).perform()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2022 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.database.mapper

import io.realm.RealmList
import org.matrix.android.sdk.api.session.sync.filter.SyncFilterParams
import org.matrix.android.sdk.internal.database.model.SyncFilterParamsEntity
import javax.inject.Inject

internal class FilterParamsMapper @Inject constructor() {

fun map(entity: SyncFilterParamsEntity): SyncFilterParams {
val eventTypes = if (entity.listOfSupportedEventTypesHasSet) {
entity.listOfSupportedEventTypes?.toList()
} else {
null
}
val stateEventTypes = if (entity.listOfSupportedStateEventTypesHasSet) {
entity.listOfSupportedStateEventTypes?.toList()
} else {
null
}
return SyncFilterParams(
useThreadNotifications = entity.useThreadNotifications,
lazyLoadMembersForMessageEvents = entity.lazyLoadMembersForMessageEvents,
lazyLoadMembersForStateEvents = entity.lazyLoadMembersForStateEvents,
listOfSupportedEventTypes = eventTypes,
listOfSupportedStateEventTypes = stateEventTypes,
)
}

fun map(params: SyncFilterParams): SyncFilterParamsEntity {
return SyncFilterParamsEntity(
useThreadNotifications = params.useThreadNotifications,
lazyLoadMembersForMessageEvents = params.lazyLoadMembersForMessageEvents,
lazyLoadMembersForStateEvents = params.lazyLoadMembersForStateEvents,
listOfSupportedEventTypes = params.listOfSupportedEventTypes.toRealmList(),
listOfSupportedEventTypesHasSet = params.listOfSupportedEventTypes != null,
listOfSupportedStateEventTypes = params.listOfSupportedStateEventTypes.toRealmList(),
listOfSupportedStateEventTypesHasSet = params.listOfSupportedEventTypes != null,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy paste bug here:

Suggested change
listOfSupportedStateEventTypesHasSet = params.listOfSupportedEventTypes != null,
listOfSupportedStateEventTypesHasSet = params.listOfSupportedStateEventTypes != null,

)
}

private fun List<String>?.toRealmList(): RealmList<String>? {
return this?.toTypedArray()?.let { RealmList(*it) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2022 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.database.migration

import io.realm.DynamicRealm
import org.matrix.android.sdk.internal.database.model.SyncFilterParamsEntityFields
import org.matrix.android.sdk.internal.util.database.RealmMigrator

internal class MigrateSessionTo043(realm: DynamicRealm) : RealmMigrator(realm, 43) {

override fun doMigrate(realm: DynamicRealm) {
realm.schema.create("SyncFilterParamsEntity")
.addField(SyncFilterParamsEntityFields.LAZY_LOAD_MEMBERS_FOR_STATE_EVENTS, Boolean::class.java)
.addField(SyncFilterParamsEntityFields.LAZY_LOAD_MEMBERS_FOR_MESSAGE_EVENTS, Boolean::class.java)
.addField(SyncFilterParamsEntityFields.LIST_OF_SUPPORTED_EVENT_TYPES_HAS_SET, Boolean::class.java)
.addField(SyncFilterParamsEntityFields.LIST_OF_SUPPORTED_STATE_EVENT_TYPES_HAS_SET, Boolean::class.java)
.addField(SyncFilterParamsEntityFields.USE_THREAD_NOTIFICATIONS, Boolean::class.java)
.addRealmListField(SyncFilterParamsEntityFields.LIST_OF_SUPPORTED_EVENT_TYPES.`$`, String::class.java)
.addRealmListField(SyncFilterParamsEntityFields.LIST_OF_SUPPORTED_STATE_EVENT_TYPES.`$`, String::class.java)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntit
SpaceChildSummaryEntity::class,
SpaceParentSummaryEntity::class,
UserPresenceEntity::class,
ThreadSummaryEntity::class
ThreadSummaryEntity::class,
SyncFilterParamsEntity::class,
]
)
internal class SessionRealmModule
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2020 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.database.model

import io.realm.RealmList
import io.realm.RealmObject

/**
* This entity stores Sync Filter configuration data, provided by the client
*/
internal open class SyncFilterParamsEntity(
var lazyLoadMembersForStateEvents: Boolean? = null,
var lazyLoadMembersForMessageEvents: Boolean? = null,
var useThreadNotifications: Boolean? = null,
var listOfSupportedEventTypes: RealmList<String>? = null,
var listOfSupportedEventTypesHasSet: Boolean = false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe rename to

Suggested change
var listOfSupportedEventTypesHasSet: Boolean = false,
var listOfSupportedEventTypesHasBeenSet: Boolean = false,

var listOfSupportedStateEventTypes: RealmList<String>? = null,
var listOfSupportedStateEventTypesHasSet: Boolean = false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe rename to

Suggested change
var listOfSupportedStateEventTypesHasSet: Boolean = false,
var listOfSupportedStateEventTypesHasBeenSet: Boolean = false,

) : RealmObject() {

companion object
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,74 +17,71 @@
package org.matrix.android.sdk.internal.session.filter

import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import io.realm.kotlin.where
import org.matrix.android.sdk.api.session.sync.filter.SyncFilterParams
import org.matrix.android.sdk.internal.database.mapper.FilterParamsMapper
import org.matrix.android.sdk.internal.database.model.FilterEntity
import org.matrix.android.sdk.internal.database.model.FilterEntityFields
import org.matrix.android.sdk.internal.database.model.SyncFilterParamsEntity
import org.matrix.android.sdk.internal.database.query.get
import org.matrix.android.sdk.internal.database.query.getOrCreate
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.util.awaitTransaction
import javax.inject.Inject

internal class DefaultFilterRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy) : FilterRepository {
internal class DefaultFilterRepository @Inject constructor(
@SessionDatabase private val monarchy: Monarchy,
) : FilterRepository {

override suspend fun storeFilter(filter: Filter, roomEventFilter: RoomEventFilter): Boolean {
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
val filterEntity = FilterEntity.get(realm)
// Filter has changed, or no filter Id yet
filterEntity == null ||
filterEntity.filterBodyJson != filter.toJSONString() ||
filterEntity.filterId.isBlank()
}.also { hasChanged ->
if (hasChanged) {
// Filter is new or has changed, store it and reset the filter Id.
// This has to be done outside of the Realm.use(), because awaitTransaction change the current thread
monarchy.awaitTransaction { realm ->
// We manage only one filter for now
val filterJson = filter.toJSONString()
val roomEventFilterJson = roomEventFilter.toJSONString()
override suspend fun storeSyncFilter(filter: Filter, filterId: String, roomEventFilter: RoomEventFilter) {
monarchy.awaitTransaction { realm ->
// We manage only one filter for now
val filterJson = filter.toJSONString()
val roomEventFilterJson = roomEventFilter.toJSONString()

val filterEntity = FilterEntity.getOrCreate(realm)
val filterEntity = FilterEntity.getOrCreate(realm)

filterEntity.filterBodyJson = filterJson
filterEntity.roomEventFilterJson = roomEventFilterJson
// Reset filterId
filterEntity.filterId = ""
}
}
filterEntity.filterBodyJson = filterJson
filterEntity.roomEventFilterJson = roomEventFilterJson
filterEntity.filterId = filterId
}
}

override suspend fun storeFilterId(filter: Filter, filterId: String) {
monarchy.awaitTransaction {
// We manage only one filter for now
val filterJson = filter.toJSONString()

// Update the filter id, only if the filter body matches
it.where<FilterEntity>()
.equalTo(FilterEntityFields.FILTER_BODY_JSON, filterJson)
?.findFirst()
?.filterId = filterId
override suspend fun getStoredSyncFilterBody(): String {
return monarchy.awaitTransaction {
FilterEntity.getOrCreate(it).filterBodyJson
}
}

override suspend fun getFilter(): String {
override suspend fun getStoredSyncFilterId(): String? {
return monarchy.awaitTransaction {
val filter = FilterEntity.getOrCreate(it)
if (filter.filterId.isBlank()) {
// Use the Json format
filter.filterBodyJson
val id = FilterEntity.get(it)?.filterId
if (id.isNullOrBlank()) {
null
} else {
// Use FilterId
filter.filterId
id
}
}
}

override suspend fun getRoomFilter(): String {
override suspend fun getRoomFilterBody(): String {
return monarchy.awaitTransaction {
FilterEntity.getOrCreate(it).roomEventFilterJson
}
}

override suspend fun getStoredFilterParams(): SyncFilterParams? {
return monarchy.awaitTransaction { realm ->
realm.where<SyncFilterParamsEntity>().findFirst()?.let {
val params = FilterParamsMapper().map(it)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FilterParamsMapper could be injected in the constructor.

params
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe simplify those two lines to

FilterParamsMapper().map(it)

(will also remove the indentation issue)

}
}
}

override suspend fun storeFilterParams(params: SyncFilterParams) {
return monarchy.awaitTransaction { realm ->
val entity = FilterParamsMapper().map(params)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use injected FilterParamsMapper

realm.insertOrUpdate(entity)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,36 @@

package org.matrix.android.sdk.internal.session.filter

import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
import org.matrix.android.sdk.api.session.sync.FilterService
import org.matrix.android.sdk.internal.session.homeserver.HomeServerCapabilitiesDataSource
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.task.configureWith
import javax.inject.Inject

internal class DefaultFilterService @Inject constructor(
private val saveFilterTask: SaveFilterTask,
private val taskExecutor: TaskExecutor
private val taskExecutor: TaskExecutor,
private val filterRepository: FilterRepository,
private val homeServerCapabilitiesDataSource: HomeServerCapabilitiesDataSource,
) : FilterService {

// TODO Pass a list of support events instead
override fun setFilter(filterPreset: FilterService.FilterPreset) {
saveFilterTask
.configureWith(SaveFilterTask.Params(filterPreset))
.executeBy(taskExecutor)
override fun setSyncFilter(filterBuilder: SyncFilterBuilder) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe take the opportunity to make this fun suspendable?

taskExecutor.executorScope.launch {
filterRepository.storeFilterParams(filterBuilder.extractParams())
}

// don't upload/store filter until homeserver capabilities are fetched
homeServerCapabilitiesDataSource.getHomeServerCapabilities()?.let { homeServerCapabilities ->
saveFilterTask
.configureWith(
SaveFilterTask.Params(
filter = filterBuilder.build(homeServerCapabilities)
)
)
.executeBy(taskExecutor)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you are in a suspend fun, I think you do not need to use the taskExecutor (it can be removed from the constructor).
Just call

saveFilterTask.execute(
    SaveFilterTask.Params(
        filter = filterBuilder.build(homeServerCapabilities)
    )
)

Should be fine

}
}
}
Loading