mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 02:15:35 +03:00
Merge branch 'vector-im:develop' into add-presence-indicator-online
This commit is contained in:
commit
f68d3f2b03
14 changed files with 328 additions and 97 deletions
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
|
@ -79,6 +79,6 @@ jobs:
|
|||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
matrix_access_token: ${{ secrets.ELEMENT_ANDROID_NOTIFICATION_ACCESS_TOKEN }}
|
||||
matrix_room_id: ${{ secrets.ELEMENT_ANDROID_INTERNAL_ROOM_ID }}
|
||||
text_template: "Build is broken for ${{ github.ref }}: {{#each job_statuses }}{{#with this }}{{#if completed }} {{name}} {{conclusion}} at {{completed_at}}, {{/if}}{{/with}}{{/each}}"
|
||||
html_template: "Build is broken for ${{ github.ref }}: {{#each job_statuses }}{{#with this }}{{#if completed }}<br />{{icon conclusion }}{{name}} <font color='{{color conclusion }}'>{{conclusion}} at {{completed_at}} <a href=\"{{html_url}}\">[details]</a></font>{{/if}}{{/with}}{{/each}}"
|
||||
text_template: "Build is broken for ${{ github.ref }}: {{#each job_statuses }}{{#with this }}{{#if completed }}{{name}} {{conclusion}} at {{completed_at}}, {{/if}}{{/with}}{{/each}}"
|
||||
html_template: "Build is broken for ${{ github.ref }}: {{#each job_statuses }}{{#with this }}{{#if completed }}<br />{{icon conclusion }} {{name}} <font color='{{color conclusion }}'>{{conclusion}} at {{completed_at}} <a href=\"{{html_url}}\">[details]</a></font>{{/if}}{{/with}}{{/each}}"
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<w>emoji</w>
|
||||
<w>emojis</w>
|
||||
<w>fdroid</w>
|
||||
<w>ganfra</w>
|
||||
<w>gplay</w>
|
||||
<w>hmac</w>
|
||||
<w>homeserver</w>
|
||||
|
@ -18,6 +19,7 @@
|
|||
<w>ktlint</w>
|
||||
<w>linkified</w>
|
||||
<w>linkify</w>
|
||||
<w>manu</w>
|
||||
<w>megolm</w>
|
||||
<w>msisdn</w>
|
||||
<w>msisdns</w>
|
||||
|
|
1
changelog.d/5443.misc
Normal file
1
changelog.d/5443.misc
Normal file
|
@ -0,0 +1 @@
|
|||
Adds stable room hierarchy endpoint with a fallback to the unstable one
|
|
@ -174,7 +174,7 @@ dependencies {
|
|||
// Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281
|
||||
testImplementation libs.mockk.mockk
|
||||
testImplementation libs.tests.kluent
|
||||
implementation libs.jetbrains.coroutinesAndroid
|
||||
testImplementation libs.jetbrains.coroutinesTest
|
||||
// Plant Timber tree for test
|
||||
testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
|
||||
// Transitively required for mocking realm as monarchy doesn't expose Rx
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
|||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.common.TestMatrixCallback
|
||||
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
|
||||
|
@ -99,7 +100,6 @@ class E2eeSanityTests : InstrumentedTest {
|
|||
ensureMembersHaveJoined(aliceSession, otherAccounts, e2eRoomID)
|
||||
|
||||
Log.v("#E2E TEST", "All users have joined the room")
|
||||
|
||||
Log.v("#E2E TEST", "Alice is sending the message")
|
||||
|
||||
val text = "This is my message"
|
||||
|
@ -172,7 +172,7 @@ class E2eeSanityTests : InstrumentedTest {
|
|||
}
|
||||
timelineEvent != null &&
|
||||
timelineEvent.root.getClearType() == EventType.MESSAGE &&
|
||||
secondMessage.equals(timelineEvent.root.getClearContent().toModel<MessageContent>()?.body)
|
||||
secondMessage == timelineEvent.root.getClearContent().toModel<MessageContent>()?.body
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -186,11 +186,11 @@ class E2eeSanityTests : InstrumentedTest {
|
|||
}
|
||||
|
||||
/**
|
||||
* Quick test for basic keybackup
|
||||
* Quick test for basic key backup
|
||||
* 1. Create e2e between Alice and Bob
|
||||
* 2. Alice sends 3 messages, using 3 different sessions
|
||||
* 3. Ensure bob can decrypt
|
||||
* 4. Create backup for bob and uplaod keys
|
||||
* 4. Create backup for bob and upload keys
|
||||
*
|
||||
* 5. Sign out alice and bob to ensure no gossiping will happen
|
||||
*
|
||||
|
@ -206,14 +206,14 @@ class E2eeSanityTests : InstrumentedTest {
|
|||
val bobSession = cryptoTestData.secondSession!!
|
||||
val e2eRoomID = cryptoTestData.roomId
|
||||
|
||||
Log.v("#E2E TEST", "Create and start keybackup for bob ...")
|
||||
val keysBackupService = bobSession.cryptoService().keysBackupService()
|
||||
Log.v("#E2E TEST", "Create and start key backup for bob ...")
|
||||
val bobKeysBackupService = bobSession.cryptoService().keysBackupService()
|
||||
val keyBackupPassword = "FooBarBaz"
|
||||
val megolmBackupCreationInfo = testHelper.doSync<MegolmBackupCreationInfo> {
|
||||
keysBackupService.prepareKeysBackupVersion(keyBackupPassword, null, it)
|
||||
bobKeysBackupService.prepareKeysBackupVersion(keyBackupPassword, null, it)
|
||||
}
|
||||
val version = testHelper.doSync<KeysVersion> {
|
||||
keysBackupService.createKeysBackupVersion(megolmBackupCreationInfo, it)
|
||||
bobKeysBackupService.createKeysBackupVersion(megolmBackupCreationInfo, it)
|
||||
}
|
||||
Log.v("#E2E TEST", "... Key backup started and enabled for bob")
|
||||
// Bob session should now have
|
||||
|
@ -249,12 +249,12 @@ class E2eeSanityTests : InstrumentedTest {
|
|||
|
||||
Log.v("#E2E TEST", "Force key backup for Bob...")
|
||||
testHelper.waitWithLatch { latch ->
|
||||
keysBackupService.backupAllGroupSessions(
|
||||
bobKeysBackupService.backupAllGroupSessions(
|
||||
null,
|
||||
TestMatrixCallback(latch, true)
|
||||
)
|
||||
}
|
||||
Log.v("#E2E TEST", "... Keybackup done for Bob")
|
||||
Log.v("#E2E TEST", "... Key backup done for Bob")
|
||||
|
||||
// Now lets logout both alice and bob to ensure that we won't have any gossiping
|
||||
|
||||
|
@ -397,7 +397,7 @@ class E2eeSanityTests : InstrumentedTest {
|
|||
}
|
||||
|
||||
/**
|
||||
* Test that if a better key is forwared (lower index, it is then used)
|
||||
* Test that if a better key is forwarded (lower index, it is then used)
|
||||
*/
|
||||
@Test
|
||||
fun testForwardBetterKey() {
|
||||
|
@ -525,15 +525,16 @@ class E2eeSanityTests : InstrumentedTest {
|
|||
private fun sendMessageInRoom(aliceRoomPOV: Room, text: String): String? {
|
||||
aliceRoomPOV.sendTextMessage(text)
|
||||
var sentEventId: String? = null
|
||||
testHelper.waitWithLatch(4 * 60_000) {
|
||||
testHelper.waitWithLatch(4 * TestConstants.timeOutMillis) { latch ->
|
||||
val timeline = aliceRoomPOV.createTimeline(null, TimelineSettings(60))
|
||||
timeline.start()
|
||||
|
||||
testHelper.retryPeriodicallyWithLatch(it) {
|
||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||
val decryptedMsg = timeline.getSnapshot()
|
||||
.filter { it.root.getClearType() == EventType.MESSAGE }
|
||||
.also {
|
||||
Log.v("#E2E TEST", "Timeline snapshot is ${it.map { "${it.root.type}|${it.root.sendState}" }.joinToString(",", "[", "]")}")
|
||||
.also { list ->
|
||||
val message = list.joinToString(",", "[", "]") { "${it.root.type}|${it.root.sendState}" }
|
||||
Log.v("#E2E TEST", "Timeline snapshot is $message")
|
||||
}
|
||||
.filter { it.root.sendState == SendState.SYNCED }
|
||||
.firstOrNull { it.root.getClearContent().toModel<MessageContent>()?.body?.startsWith(text) == true }
|
||||
|
@ -547,8 +548,8 @@ class E2eeSanityTests : InstrumentedTest {
|
|||
}
|
||||
|
||||
private fun ensureMembersHaveJoined(aliceSession: Session, otherAccounts: List<Session>, e2eRoomID: String) {
|
||||
testHelper.waitWithLatch {
|
||||
testHelper.retryPeriodicallyWithLatch(it) {
|
||||
testHelper.waitWithLatch { latch ->
|
||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||
otherAccounts.map {
|
||||
aliceSession.getRoomMember(it.myUserId, e2eRoomID)?.membership
|
||||
}.all {
|
||||
|
@ -559,8 +560,8 @@ class E2eeSanityTests : InstrumentedTest {
|
|||
}
|
||||
|
||||
private fun waitForAndAcceptInviteInRoom(otherSession: Session, e2eRoomID: String) {
|
||||
testHelper.waitWithLatch {
|
||||
testHelper.retryPeriodicallyWithLatch(it) {
|
||||
testHelper.waitWithLatch { latch ->
|
||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||
val roomSummary = otherSession.getRoomSummary(e2eRoomID)
|
||||
(roomSummary != null && roomSummary.membership == Membership.INVITE).also {
|
||||
if (it) {
|
||||
|
|
|
@ -21,6 +21,7 @@ internal object NetworkConstants {
|
|||
private const val URI_API_PREFIX_PATH = "_matrix/client"
|
||||
const val URI_API_PREFIX_PATH_ = "$URI_API_PREFIX_PATH/"
|
||||
const val URI_API_PREFIX_PATH_R0 = "$URI_API_PREFIX_PATH/r0/"
|
||||
const val URI_API_PREFIX_PATH_V1 = "$URI_API_PREFIX_PATH/v1/"
|
||||
const val URI_API_PREFIX_PATH_UNSTABLE = "$URI_API_PREFIX_PATH/unstable/"
|
||||
|
||||
// Media
|
||||
|
|
|
@ -113,71 +113,108 @@ internal class DefaultSpaceService @Inject constructor(
|
|||
return peekSpaceTask.execute(PeekSpaceTask.Params(spaceId))
|
||||
}
|
||||
|
||||
override suspend fun querySpaceChildren(spaceId: String,
|
||||
suggestedOnly: Boolean?,
|
||||
limit: Int?,
|
||||
from: String?,
|
||||
knownStateList: List<Event>?): SpaceHierarchyData {
|
||||
return resolveSpaceInfoTask.execute(
|
||||
ResolveSpaceInfoTask.Params(
|
||||
spaceId = spaceId, limit = limit, maxDepth = 1, from = from, suggestedOnly = suggestedOnly
|
||||
)
|
||||
).let { response ->
|
||||
val spaceDesc = response.rooms?.firstOrNull { it.roomId == spaceId }
|
||||
val root = RoomSummary(
|
||||
roomId = spaceDesc?.roomId ?: spaceId,
|
||||
roomType = spaceDesc?.roomType,
|
||||
name = spaceDesc?.name ?: "",
|
||||
displayName = spaceDesc?.name ?: "",
|
||||
topic = spaceDesc?.topic ?: "",
|
||||
joinedMembersCount = spaceDesc?.numJoinedMembers,
|
||||
avatarUrl = spaceDesc?.avatarUrl ?: "",
|
||||
encryptionEventTs = null,
|
||||
typingUsers = emptyList(),
|
||||
isEncrypted = false,
|
||||
flattenParentIds = emptyList(),
|
||||
canonicalAlias = spaceDesc?.canonicalAlias,
|
||||
joinRules = RoomJoinRules.PUBLIC.takeIf { spaceDesc?.worldReadable == true }
|
||||
)
|
||||
val children = response.rooms
|
||||
?.filter { it.roomId != spaceId }
|
||||
?.flatMap { childSummary ->
|
||||
(spaceDesc?.childrenState ?: knownStateList)
|
||||
?.filter { it.stateKey == childSummary.roomId && it.type == EventType.STATE_SPACE_CHILD }
|
||||
?.mapNotNull { childStateEv ->
|
||||
// create a child entry for everytime this room is the child of a space
|
||||
// beware that a room could appear then twice in this list
|
||||
childStateEv.content.toModel<SpaceChildContent>()?.let { childStateEvContent ->
|
||||
SpaceChildInfo(
|
||||
childRoomId = childSummary.roomId,
|
||||
isKnown = true,
|
||||
roomType = childSummary.roomType,
|
||||
name = childSummary.name,
|
||||
topic = childSummary.topic,
|
||||
avatarUrl = childSummary.avatarUrl,
|
||||
order = childStateEvContent.order,
|
||||
// autoJoin = childStateEvContent.autoJoin ?: false,
|
||||
viaServers = childStateEvContent.via.orEmpty(),
|
||||
activeMemberCount = childSummary.numJoinedMembers,
|
||||
parentRoomId = childStateEv.roomId,
|
||||
suggested = childStateEvContent.suggested,
|
||||
canonicalAlias = childSummary.canonicalAlias,
|
||||
aliases = childSummary.aliases,
|
||||
worldReadable = childSummary.worldReadable
|
||||
)
|
||||
}
|
||||
}.orEmpty()
|
||||
}
|
||||
.orEmpty()
|
||||
SpaceHierarchyData(
|
||||
rootSummary = root,
|
||||
children = children,
|
||||
childrenState = spaceDesc?.childrenState.orEmpty(),
|
||||
nextToken = response.nextBatch
|
||||
)
|
||||
}
|
||||
override suspend fun querySpaceChildren(
|
||||
spaceId: String,
|
||||
suggestedOnly: Boolean?,
|
||||
limit: Int?,
|
||||
from: String?,
|
||||
knownStateList: List<Event>?
|
||||
): SpaceHierarchyData {
|
||||
val spacesResponse = getSpacesResponse(spaceId, suggestedOnly, limit, from)
|
||||
val spaceRootResponse = spacesResponse.getRoot(spaceId)
|
||||
val spaceRoot = spaceRootResponse?.toRoomSummary() ?: createBlankRoomSummary(spaceId)
|
||||
val spaceChildren = spacesResponse.rooms.mapSpaceChildren(spaceId, spaceRootResponse, knownStateList)
|
||||
|
||||
return SpaceHierarchyData(
|
||||
rootSummary = spaceRoot,
|
||||
children = spaceChildren,
|
||||
childrenState = spaceRootResponse?.childrenState.orEmpty(),
|
||||
nextToken = spacesResponse.nextBatch
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun getSpacesResponse(spaceId: String, suggestedOnly: Boolean?, limit: Int?, from: String?) =
|
||||
resolveSpaceInfoTask.execute(
|
||||
ResolveSpaceInfoTask.Params(spaceId = spaceId, limit = limit, maxDepth = 1, from = from, suggestedOnly = suggestedOnly)
|
||||
)
|
||||
|
||||
private fun SpacesResponse.getRoot(spaceId: String) = rooms?.firstOrNull { it.roomId == spaceId }
|
||||
|
||||
private fun SpaceChildSummaryResponse.toRoomSummary() = RoomSummary(
|
||||
roomId = roomId,
|
||||
roomType = roomType,
|
||||
name = name ?: "",
|
||||
displayName = name ?: "",
|
||||
topic = topic ?: "",
|
||||
joinedMembersCount = numJoinedMembers,
|
||||
avatarUrl = avatarUrl ?: "",
|
||||
encryptionEventTs = null,
|
||||
typingUsers = emptyList(),
|
||||
isEncrypted = false,
|
||||
flattenParentIds = emptyList(),
|
||||
canonicalAlias = canonicalAlias,
|
||||
joinRules = RoomJoinRules.PUBLIC.takeIf { isWorldReadable }
|
||||
)
|
||||
|
||||
private fun createBlankRoomSummary(spaceId: String) = RoomSummary(
|
||||
roomId = spaceId,
|
||||
joinedMembersCount = null,
|
||||
encryptionEventTs = null,
|
||||
typingUsers = emptyList(),
|
||||
isEncrypted = false,
|
||||
flattenParentIds = emptyList(),
|
||||
canonicalAlias = null,
|
||||
joinRules = null
|
||||
)
|
||||
|
||||
private fun List<SpaceChildSummaryResponse>?.mapSpaceChildren(
|
||||
spaceId: String,
|
||||
spaceRootResponse: SpaceChildSummaryResponse?,
|
||||
knownStateList: List<Event>?,
|
||||
) = this?.filterIdIsNot(spaceId)
|
||||
?.toSpaceChildInfoList(spaceId, spaceRootResponse, knownStateList)
|
||||
.orEmpty()
|
||||
|
||||
private fun List<SpaceChildSummaryResponse>.filterIdIsNot(spaceId: String) = filter { it.roomId != spaceId }
|
||||
|
||||
private fun List<SpaceChildSummaryResponse>.toSpaceChildInfoList(
|
||||
spaceId: String,
|
||||
rootRoomResponse: SpaceChildSummaryResponse?,
|
||||
knownStateList: List<Event>?,
|
||||
) = flatMap { spaceChildSummary ->
|
||||
(rootRoomResponse?.childrenState ?: knownStateList)
|
||||
?.filter { it.isChildOf(spaceChildSummary) }
|
||||
?.mapNotNull { childStateEvent -> childStateEvent.toSpaceChildInfo(spaceId, spaceChildSummary) }
|
||||
.orEmpty()
|
||||
}
|
||||
|
||||
private fun Event.isChildOf(space: SpaceChildSummaryResponse) = stateKey == space.roomId && type == EventType.STATE_SPACE_CHILD
|
||||
|
||||
private fun Event.toSpaceChildInfo(spaceId: String, summary: SpaceChildSummaryResponse) = content.toModel<SpaceChildContent>()?.let { content ->
|
||||
createSpaceChildInfo(spaceId, summary, content)
|
||||
}
|
||||
|
||||
private fun createSpaceChildInfo(
|
||||
spaceId: String,
|
||||
summary: SpaceChildSummaryResponse,
|
||||
content: SpaceChildContent
|
||||
) = SpaceChildInfo(
|
||||
childRoomId = summary.roomId,
|
||||
isKnown = true,
|
||||
roomType = summary.roomType,
|
||||
name = summary.name,
|
||||
topic = summary.topic,
|
||||
avatarUrl = summary.avatarUrl,
|
||||
order = content.order,
|
||||
viaServers = content.via.orEmpty(),
|
||||
activeMemberCount = summary.numJoinedMembers,
|
||||
parentRoomId = spaceId,
|
||||
suggested = content.suggested,
|
||||
canonicalAlias = summary.canonicalAlias,
|
||||
aliases = summary.aliases,
|
||||
worldReadable = summary.isWorldReadable
|
||||
)
|
||||
|
||||
override suspend fun joinSpace(spaceIdOrAlias: String,
|
||||
reason: String?,
|
||||
viaServers: List<String>): JoinSpaceResult {
|
||||
|
@ -192,10 +229,6 @@ internal class DefaultSpaceService @Inject constructor(
|
|||
leaveRoomTask.execute(LeaveRoomTask.Params(spaceId, reason))
|
||||
}
|
||||
|
||||
// override fun getSpaceParentsOfRoom(roomId: String): List<SpaceSummary> {
|
||||
// return spaceSummaryDataSource.getParentsOfRoom(roomId)
|
||||
// }
|
||||
|
||||
override suspend fun setSpaceParent(childRoomId: String, parentSpaceId: String, canonical: Boolean, viaServers: List<String>) {
|
||||
// Should we perform some validation here?,
|
||||
// and if client want to bypass, it could use sendStateEvent directly?
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.space
|
|||
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 retrofit2.HttpException
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface ResolveSpaceInfoTask : Task<ResolveSpaceInfoTask.Params, SpacesResponse> {
|
||||
|
@ -28,7 +29,6 @@ internal interface ResolveSpaceInfoTask : Task<ResolveSpaceInfoTask.Params, Spac
|
|||
val maxDepth: Int?,
|
||||
val from: String?,
|
||||
val suggestedOnly: Boolean?
|
||||
// val autoJoinOnly: Boolean?
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -36,14 +36,30 @@ internal class DefaultResolveSpaceInfoTask @Inject constructor(
|
|||
private val spaceApi: SpaceApi,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver
|
||||
) : ResolveSpaceInfoTask {
|
||||
override suspend fun execute(params: ResolveSpaceInfoTask.Params): SpacesResponse {
|
||||
return executeRequest(globalErrorReceiver) {
|
||||
|
||||
override suspend fun execute(params: ResolveSpaceInfoTask.Params) = executeRequest(globalErrorReceiver) {
|
||||
try {
|
||||
getSpaceHierarchy(params)
|
||||
} catch (e: HttpException) {
|
||||
getUnstableSpaceHierarchy(params)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getSpaceHierarchy(params: ResolveSpaceInfoTask.Params) =
|
||||
spaceApi.getSpaceHierarchy(
|
||||
spaceId = params.spaceId,
|
||||
suggestedOnly = params.suggestedOnly,
|
||||
limit = params.limit,
|
||||
maxDepth = params.maxDepth,
|
||||
from = params.from)
|
||||
}
|
||||
}
|
||||
from = params.from,
|
||||
)
|
||||
|
||||
private suspend fun getUnstableSpaceHierarchy(params: ResolveSpaceInfoTask.Params) =
|
||||
spaceApi.getSpaceHierarchyUnstable(
|
||||
spaceId = params.spaceId,
|
||||
suggestedOnly = params.suggestedOnly,
|
||||
limit = params.limit,
|
||||
maxDepth = params.maxDepth,
|
||||
from = params.from,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -31,11 +31,22 @@ internal interface SpaceApi {
|
|||
* @param from: Optional. Pagination token given to retrieve the next set of rooms.
|
||||
* Note that if a pagination token is provided, then the parameters given for suggested_only and max_depth must be the same.
|
||||
*/
|
||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "org.matrix.msc2946/rooms/{roomId}/hierarchy")
|
||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_V1 + "rooms/{roomId}/hierarchy")
|
||||
suspend fun getSpaceHierarchy(
|
||||
@Path("roomId") spaceId: String,
|
||||
@Query("suggested_only") suggestedOnly: Boolean?,
|
||||
@Query("limit") limit: Int?,
|
||||
@Query("max_depth") maxDepth: Int?,
|
||||
@Query("from") from: String?): SpacesResponse
|
||||
|
||||
/**
|
||||
* Unstable version of [getSpaceHierarchy]
|
||||
*/
|
||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "org.matrix.msc2946/rooms/{roomId}/hierarchy")
|
||||
suspend fun getSpaceHierarchyUnstable(
|
||||
@Path("roomId") spaceId: String,
|
||||
@Query("suggested_only") suggestedOnly: Boolean?,
|
||||
@Query("limit") limit: Int?,
|
||||
@Query("max_depth") maxDepth: Int?,
|
||||
@Query("from") from: String?): SpacesResponse
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ internal data class SpaceChildSummaryResponse(
|
|||
* Required. Whether the room may be viewed by guest users without joining.
|
||||
*/
|
||||
@Json(name = "world_readable")
|
||||
val worldReadable: Boolean = false,
|
||||
val isWorldReadable: Boolean = false,
|
||||
|
||||
/**
|
||||
* Required. Whether guest users may join the room and participate in it. If they can,
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright 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.space
|
||||
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||
import org.amshove.kluent.shouldBeEqualTo
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.test.fakes.FakeGlobalErrorReceiver
|
||||
import org.matrix.android.sdk.test.fakes.FakeSpaceApi
|
||||
import org.matrix.android.sdk.test.fixtures.SpacesResponseFixture.aSpacesResponse
|
||||
import retrofit2.HttpException
|
||||
import retrofit2.Response
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
internal class DefaultResolveSpaceInfoTaskTest {
|
||||
|
||||
private val spaceApi = FakeSpaceApi()
|
||||
private val globalErrorReceiver = FakeGlobalErrorReceiver()
|
||||
private val resolveSpaceInfoTask = DefaultResolveSpaceInfoTask(spaceApi.instance, globalErrorReceiver)
|
||||
|
||||
@Test
|
||||
fun `given stable endpoint works, when execute, then return stable api data`() = runBlockingTest {
|
||||
spaceApi.givenStableEndpointReturns(response)
|
||||
|
||||
val result = resolveSpaceInfoTask.execute(spaceApi.params)
|
||||
|
||||
result shouldBeEqualTo response
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given stable endpoint fails, when execute, then fallback to unstable endpoint`() = runBlockingTest {
|
||||
spaceApi.givenStableEndpointThrows(httpException)
|
||||
spaceApi.givenUnstableEndpointReturns(response)
|
||||
|
||||
val result = resolveSpaceInfoTask.execute(spaceApi.params)
|
||||
|
||||
result shouldBeEqualTo response
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val response = aSpacesResponse()
|
||||
private val httpException = HttpException(Response.error<SpacesResponse>(500, "".toResponseBody()))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 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.test.fakes
|
||||
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.mockk
|
||||
import org.matrix.android.sdk.internal.session.space.SpaceApi
|
||||
import org.matrix.android.sdk.internal.session.space.SpacesResponse
|
||||
import org.matrix.android.sdk.test.fixtures.ResolveSpaceInfoTaskParamsFixture
|
||||
|
||||
internal class FakeSpaceApi {
|
||||
|
||||
val instance: SpaceApi = mockk()
|
||||
val params = ResolveSpaceInfoTaskParamsFixture.aResolveSpaceInfoTaskParams()
|
||||
|
||||
fun givenStableEndpointReturns(response: SpacesResponse) {
|
||||
coEvery { instance.getSpaceHierarchy(params.spaceId, params.suggestedOnly, params.limit, params.maxDepth, params.from) } returns response
|
||||
}
|
||||
|
||||
fun givenStableEndpointThrows(throwable: Throwable) {
|
||||
coEvery { instance.getSpaceHierarchy(params.spaceId, params.suggestedOnly, params.limit, params.maxDepth, params.from) } throws throwable
|
||||
}
|
||||
|
||||
fun givenUnstableEndpointReturns(response: SpacesResponse) {
|
||||
coEvery { instance.getSpaceHierarchyUnstable(params.spaceId, params.suggestedOnly, params.limit, params.maxDepth, params.from) } returns response
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 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.test.fixtures
|
||||
|
||||
import org.matrix.android.sdk.internal.session.space.ResolveSpaceInfoTask
|
||||
|
||||
internal object ResolveSpaceInfoTaskParamsFixture {
|
||||
fun aResolveSpaceInfoTaskParams(
|
||||
spaceId: String = "",
|
||||
limit: Int? = null,
|
||||
maxDepth: Int? = null,
|
||||
from: String? = null,
|
||||
suggestedOnly: Boolean? = null,
|
||||
) = ResolveSpaceInfoTask.Params(
|
||||
spaceId,
|
||||
limit,
|
||||
maxDepth,
|
||||
from,
|
||||
suggestedOnly,
|
||||
)
|
||||
}
|
30
matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SpacesResponseFixture.kt
vendored
Normal file
30
matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SpacesResponseFixture.kt
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 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.test.fixtures
|
||||
|
||||
import org.matrix.android.sdk.internal.session.space.SpaceChildSummaryResponse
|
||||
import org.matrix.android.sdk.internal.session.space.SpacesResponse
|
||||
|
||||
internal object SpacesResponseFixture {
|
||||
fun aSpacesResponse(
|
||||
nextBatch: String? = null,
|
||||
rooms: List<SpaceChildSummaryResponse>? = null,
|
||||
) = SpacesResponse(
|
||||
nextBatch,
|
||||
rooms,
|
||||
)
|
||||
}
|
Loading…
Reference in a new issue