Ensure Filter model match the spec and add Javadoc

This commit is contained in:
Benoit Marty 2020-05-18 22:38:07 +02:00
parent e793a46576
commit 6e57b06673
13 changed files with 183 additions and 88 deletions

View File

@ -35,7 +35,7 @@ internal fun FilterEntity.Companion.get(realm: Realm): FilterEntity? {
internal fun FilterEntity.Companion.getOrCreate(realm: Realm): FilterEntity {
return get(realm) ?: realm.createObject<FilterEntity>()
.apply {
filterBodyJson = FilterFactory.createDefaultFilterBody().toJSONString()
filterBodyJson = FilterFactory.createDefaultFilter().toJSONString()
roomEventFilterJson = FilterFactory.createDefaultRoomFilter().toJSONString()
filterId = ""
}

View File

@ -28,25 +28,25 @@ import javax.inject.Inject
internal class DefaultFilterRepository @Inject constructor(private val monarchy: Monarchy) : FilterRepository {
override suspend fun storeFilter(filterBody: FilterBody, roomEventFilter: RoomEventFilter): Boolean {
override suspend fun storeFilter(filter: Filter, roomEventFilter: RoomEventFilter): Boolean {
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
val filter = FilterEntity.get(realm)
val filterEntity = FilterEntity.get(realm)
// Filter has changed, or no filter Id yet
filter == null
|| filter.filterBodyJson != filterBody.toJSONString()
|| filter.filterId.isBlank()
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 filterBodyJson = filterBody.toJSONString()
val filterJson = filter.toJSONString()
val roomEventFilterJson = roomEventFilter.toJSONString()
val filterEntity = FilterEntity.getOrCreate(realm)
filterEntity.filterBodyJson = filterBodyJson
filterEntity.filterBodyJson = filterJson
filterEntity.roomEventFilterJson = roomEventFilterJson
// Reset filterId
filterEntity.filterId = ""
@ -55,14 +55,14 @@ internal class DefaultFilterRepository @Inject constructor(private val monarchy:
}
}
override suspend fun storeFilterId(filterBody: FilterBody, filterId: String) {
override suspend fun storeFilterId(filter: Filter, filterId: String) {
monarchy.awaitTransaction {
// We manage only one filter for now
val filterBodyJson = filterBody.toJSONString()
val filterJson = filter.toJSONString()
// Update the filter id, only if the filter body matches
it.where<FilterEntity>()
.equalTo(FilterEntityFields.FILTER_BODY_JSON, filterBodyJson)
.equalTo(FilterEntityFields.FILTER_BODY_JSON, filterJson)
?.findFirst()
?.filterId = filterId
}

View File

@ -43,10 +43,10 @@ internal class DefaultSaveFilterTask @Inject constructor(
override suspend fun execute(params: SaveFilterTask.Params) {
val filterBody = when (params.filterPreset) {
FilterService.FilterPreset.RiotFilter -> {
FilterFactory.createRiotFilterBody()
FilterFactory.createRiotFilter()
}
FilterService.FilterPreset.NoFilter -> {
FilterFactory.createDefaultFilterBody()
FilterFactory.createDefaultFilter()
}
}
val roomFilter = when (params.filterPreset) {

View File

@ -0,0 +1,59 @@
/*
* Copyright 2019 New Vector Ltd
*
* 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 im.vector.matrix.android.internal.session.filter
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
/**
* Represents "Filter" as mentioned in the SPEC
* https://matrix.org/docs/spec/client_server/r0.3.0.html#post-matrix-client-r0-user-userid-filter
*/
@JsonClass(generateAdapter = true)
data class EventFilter(
/**
* The maximum number of events to return.
*/
@Json(name = "limit") val limit: Int? = null,
/**
* A list of senders IDs to include. If this list is absent then all senders are included.
*/
@Json(name = "senders") val senders: List<String>? = null,
/**
* A list of sender IDs to exclude. If this list is absent then no senders are excluded.
* A matching sender will be excluded even if it is listed in the 'senders' filter.
*/
@Json(name = "not_senders") val notSenders: List<String>? = null,
/**
* A list of event types to include. If this list is absent then all event types are included.
* A '*' can be used as a wildcard to match any sequence of characters.
*/
@Json(name = "types") val types: List<String>? = null,
/**
* A list of event types to exclude. If this list is absent then no event types are excluded.
* A matching type will be excluded even if it is listed in the 'types' filter.
* A '*' can be used as a wildcard to match any sequence of characters.
*/
@Json(name = "not_types") val notTypes: List<String>? = null
) {
fun hasData(): Boolean {
return limit != null
|| senders != null
|| notSenders != null
|| types != null
|| notTypes != null
}
}

View File

@ -17,28 +17,42 @@ package im.vector.matrix.android.internal.session.filter
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.internal.di.MoshiProvider
/**
* Represents "Filter" as mentioned in the SPEC
* Class which can be parsed to a filter json string. Used for POST and GET
* Have a look here for further information:
* https://matrix.org/docs/spec/client_server/r0.3.0.html#post-matrix-client-r0-user-userid-filter
*/
@JsonClass(generateAdapter = true)
data class Filter(
@Json(name = "limit") val limit: Int? = null,
@Json(name = "senders") val senders: List<String>? = null,
@Json(name = "not_senders") val notSenders: List<String>? = null,
@Json(name = "types") val types: List<String>? = null,
@Json(name = "not_types") val notTypes: List<String>? = null,
@Json(name = "rooms") val rooms: List<String>? = null,
@Json(name = "not_rooms") val notRooms: List<String>? = null
internal data class Filter(
/**
* List of event fields to include. If this list is absent then all fields are included. The entries may
* include '.' characters to indicate sub-fields. So ['content.body'] will include the 'body' field of the
* 'content' object. A literal '.' character in a field name may be escaped using a '\'. A server may
* include more fields than were requested.
*/
@Json(name = "event_fields") val eventFields: List<String>? = null,
/**
* The format to use for events. 'client' will return the events in a format suitable for clients.
* 'federation' will return the raw event as received over federation. The default is 'client'. One of: ["client", "federation"]
*/
@Json(name = "event_format") val eventFormat: String? = null,
/**
* The presence updates to include.
*/
@Json(name = "presence") val presence: EventFilter? = null,
/**
* The user account data that isn't associated with rooms to include.
*/
@Json(name = "account_data") val accountData: EventFilter? = null,
/**
* Filters to be applied to room data.
*/
@Json(name = "room") val room: RoomFilter? = null
) {
fun hasData(): Boolean {
return (limit != null
|| senders != null
|| notSenders != null
|| types != null
|| notTypes != null
|| rooms != null
|| notRooms != null)
fun toJSONString(): String {
return MoshiProvider.providesMoshi().adapter(Filter::class.java).toJson(this)
}
}

View File

@ -32,7 +32,7 @@ internal interface FilterApi {
* @param body the Json representation of a FilterBody object
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "user/{userId}/filter")
fun uploadFilter(@Path("userId") userId: String, @Body body: FilterBody): Call<FilterResponse>
fun uploadFilter(@Path("userId") userId: String, @Body body: Filter): Call<FilterResponse>
/**
* Gets a filter with a given filterId from the homeserver
@ -42,6 +42,5 @@ internal interface FilterApi {
* @return Filter
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "user/{userId}/filter/{filterId}")
fun getFilterById(@Path("userId") userId: String, @Path("filterId")
filterId: String): Call<FilterBody>
fun getFilterById(@Path("userId") userId: String, @Path("filterId") filterId: String): Call<Filter>
}

View File

@ -1,39 +0,0 @@
/*
* Copyright 2019 New Vector Ltd
*
* 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 im.vector.matrix.android.internal.session.filter
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.internal.di.MoshiProvider
/**
* Class which can be parsed to a filter json string. Used for POST and GET
* Have a look here for further information:
* https://matrix.org/docs/spec/client_server/r0.3.0.html#post-matrix-client-r0-user-userid-filter
*/
@JsonClass(generateAdapter = true)
internal data class FilterBody(
@Json(name = "event_fields") val eventFields: List<String>? = null,
@Json(name = "event_format") val eventFormat: String? = null,
@Json(name = "presence") val presence: Filter? = null,
@Json(name = "account_data") val accountData: Filter? = null,
@Json(name = "room") val room: RoomFilter? = null
) {
fun toJSONString(): String {
return MoshiProvider.providesMoshi().adapter(FilterBody::class.java).toJson(this)
}
}

View File

@ -20,12 +20,12 @@ import im.vector.matrix.android.api.session.events.model.EventType
internal object FilterFactory {
fun createDefaultFilterBody(): FilterBody {
return FilterUtil.enableLazyLoading(FilterBody(), true)
fun createDefaultFilter(): Filter {
return FilterUtil.enableLazyLoading(Filter(), true)
}
fun createRiotFilterBody(): FilterBody {
return FilterBody(
fun createRiotFilter(): Filter {
return Filter(
room = RoomFilter(
timeline = createRiotTimelineFilter(),
state = createRiotStateFilter()

View File

@ -21,12 +21,12 @@ internal interface FilterRepository {
/**
* Return true if the filterBody has changed, or need to be sent to the server
*/
suspend fun storeFilter(filterBody: FilterBody, roomEventFilter: RoomEventFilter): Boolean
suspend fun storeFilter(filter: Filter, roomEventFilter: RoomEventFilter): Boolean
/**
* Set the filterId of this filter
*/
suspend fun storeFilterId(filterBody: FilterBody, filterId: String)
suspend fun storeFilterId(filter: Filter, filterId: String)
/**
* Return filter json or filter id

View File

@ -24,5 +24,10 @@ import com.squareup.moshi.JsonClass
*/
@JsonClass(generateAdapter = true)
data class FilterResponse(
/**
* Required. The ID of the filter that was created. Cannot start with a { as this character
* is used to determine if the filter provided is inline JSON or a previously declared
* filter by homeservers on some APIs.
*/
@Json(name = "filter_id") val filterId: String
)

View File

@ -81,30 +81,30 @@ internal object FilterUtil {
} */
/**
* Compute a new filterBody to enable or disable the lazy loading
* Compute a new filter to enable or disable the lazy loading
*
*
* If lazy loading is on, the filterBody will looks like
* If lazy loading is on, the filter will looks like
* {"room":{"state":{"lazy_load_members":true})}
*
* @param filterBody filterBody to patch
* @param filter filter to patch
* @param useLazyLoading true to enable lazy loading
*/
fun enableLazyLoading(filterBody: FilterBody, useLazyLoading: Boolean): FilterBody {
fun enableLazyLoading(filter: Filter, useLazyLoading: Boolean): Filter {
if (useLazyLoading) {
// Enable lazy loading
return filterBody.copy(
room = filterBody.room?.copy(
state = filterBody.room.state?.copy(lazyLoadMembers = true)
return filter.copy(
room = filter.room?.copy(
state = filter.room.state?.copy(lazyLoadMembers = true)
?: RoomEventFilter(lazyLoadMembers = true)
)
?: RoomFilter(state = RoomEventFilter(lazyLoadMembers = true))
)
} else {
val newRoomEventFilter = filterBody.room?.state?.copy(lazyLoadMembers = null)?.takeIf { it.hasData() }
val newRoomFilter = filterBody.room?.copy(state = newRoomEventFilter)?.takeIf { it.hasData() }
val newRoomEventFilter = filter.room?.state?.copy(lazyLoadMembers = null)?.takeIf { it.hasData() }
val newRoomFilter = filter.room?.copy(state = newRoomEventFilter)?.takeIf { it.hasData() }
return filterBody.copy(
return filter.copy(
room = newRoomFilter
)
}

View File

@ -25,14 +25,46 @@ import im.vector.matrix.android.internal.di.MoshiProvider
*/
@JsonClass(generateAdapter = true)
data class RoomEventFilter(
/**
* The maximum number of events to return.
*/
@Json(name = "limit") var limit: Int? = null,
/**
* A list of sender IDs to exclude. If this list is absent then no senders are excluded. A matching sender will
* be excluded even if it is listed in the 'senders' filter.
*/
@Json(name = "not_senders") val notSenders: List<String>? = null,
/**
* A list of event types to exclude. If this list is absent then no event types are excluded. A matching type will
* be excluded even if it is listed in the 'types' filter. A '*' can be used as a wildcard to match any sequence of characters.
*/
@Json(name = "not_types") val notTypes: List<String>? = null,
/**
* A list of senders IDs to include. If this list is absent then all senders are included.
*/
@Json(name = "senders") val senders: List<String>? = null,
/**
* A list of event types to include. If this list is absent then all event types are included. A '*' can be used as
* a wildcard to match any sequence of characters.
*/
@Json(name = "types") val types: List<String>? = null,
/**
* A list of room IDs to include. If this list is absent then all rooms are included.
*/
@Json(name = "rooms") val rooms: List<String>? = null,
/**
* A list of room IDs to exclude. If this list is absent then no rooms are excluded. A matching room will be excluded
* even if it is listed in the 'rooms' filter.
*/
@Json(name = "not_rooms") val notRooms: List<String>? = null,
/**
* If true, includes only events with a url key in their content. If false, excludes those events. If omitted, url
* key is not considered for filtering.
*/
@Json(name = "contains_url") val containsUrl: Boolean? = null,
/**
* If true, enables lazy-loading of membership events. See Lazy-loading room members for more information. Defaults to false.
*/
@Json(name = "lazy_load_members") val lazyLoadMembers: Boolean? = null
) {

View File

@ -24,12 +24,37 @@ import com.squareup.moshi.JsonClass
*/
@JsonClass(generateAdapter = true)
data class RoomFilter(
/**
* A list of room IDs to exclude. If this list is absent then no rooms are excluded.
* A matching room will be excluded even if it is listed in the 'rooms' filter.
* This filter is applied before the filters in ephemeral, state, timeline or account_data
*/
@Json(name = "not_rooms") val notRooms: List<String>? = null,
/**
* A list of room IDs to include. If this list is absent then all rooms are included.
* This filter is applied before the filters in ephemeral, state, timeline or account_data
*/
@Json(name = "rooms") val rooms: List<String>? = null,
/**
* The events that aren't recorded in the room history, e.g. typing and receipts, to include for rooms.
*/
@Json(name = "ephemeral") val ephemeral: RoomEventFilter? = null,
/**
* Include rooms that the user has left in the sync, default false
*/
@Json(name = "include_leave") val includeLeave: Boolean? = null,
/**
* The state events to include for rooms.
* Developer remark: StateFilter is exactly the same than RoomEventFilter
*/
@Json(name = "state") val state: RoomEventFilter? = null,
/**
* The message and state update events to include for rooms.
*/
@Json(name = "timeline") val timeline: RoomEventFilter? = null,
/**
* The per user account data to include for rooms.
*/
@Json(name = "account_data") val accountData: RoomEventFilter? = null
) {