Use device specific API for linked devices updating capabilities.

This commit is contained in:
Cody Henthorne
2026-07-01 15:10:24 -04:00
committed by Michelle Tang
parent ee11d1fd3b
commit c4faafa9ae
6 changed files with 61 additions and 5 deletions
@@ -2,11 +2,13 @@
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
@file:JvmName("RequestResultUtil")
package org.whispersystems.signalservice.api
import org.signal.core.util.concurrent.safeBlockingGet
import org.signal.libsignal.net.RequestResult
import org.signal.network.exceptions.NonSuccessfulResponseCodeException
import org.signal.network.exceptions.PushNetworkException
import org.signal.network.rest.RestStatusCodeError
import org.signal.network.util.JsonUtil
@@ -49,6 +51,25 @@ fun <T : Any> SignalWebSocket.fromWebSocketRequest(
}
}
/**
* Unwraps a [RequestResult] to its success value, or throws an [IOException]. Useful for callers bridging the
* libsignal [RequestResult] surface back into legacy, exception-based code.
*
* All non-2xx responses become a [NonSuccessfulResponseCodeException].
*/
@Throws(IOException::class)
fun <T : Any> RequestResult<T, RestStatusCodeError>.successOrThrow(): T {
return when (this) {
is RequestResult.Success -> result
is RequestResult.RetryableNetworkError -> throw networkError
is RequestResult.NonSuccess -> throw NonSuccessfulResponseCodeException(error.statusCode, "StatusCode: ${error.statusCode}", error.body, error.headers)
is RequestResult.ApplicationError -> throw when (val error = cause) {
is IOException, is RuntimeException -> error
else -> RuntimeException(error)
}
}
}
private fun <T : Any> WebsocketResponse.toRequestResult(clazz: KClass<T>): RequestResult<T, RestStatusCodeError> {
return if (status < 200 || status > 299) {
RequestResult.NonSuccess(RestStatusCodeError(status, headers, body?.toByteArray()))
@@ -7,9 +7,11 @@ package org.whispersystems.signalservice.api.account
import org.signal.core.util.Base64
import org.signal.core.util.Base64.encodeUrlSafeWithoutPadding
import org.signal.libsignal.net.RequestResult
import org.signal.libsignal.usernames.BaseUsernameException
import org.signal.libsignal.usernames.Username
import org.signal.network.NetworkResult
import org.signal.network.rest.RestStatusCodeError
import org.signal.network.websocket.WebSocketRequestMessage
import org.signal.network.websocket.delete
import org.signal.network.websocket.get
@@ -77,6 +79,17 @@ class AccountApi(private val authWebSocket: SignalWebSocket.AuthenticatedWebSock
return NetworkResult.fromWebSocketRequest(authWebSocket, request)
}
/**
* Update the capabilities of the calling device.
*
* PUT /v1/devices/capabilities
* - 200: Success
*/
fun setCapabilities(capabilities: AccountAttributes.Capabilities): RequestResult<Unit, RestStatusCodeError> {
val request = WebSocketRequestMessage.put("/v1/devices/capabilities", capabilities)
return authWebSocket.fromWebSocketRequest(request, Unit::class)
}
/**
* PUT /v1/accounts/registration_lock
* - 204: Success