Handle 404s when reading call links
This commit is contained in:
parent
a45958d1a0
commit
8ebabea916
@ -57,13 +57,19 @@ actor CallLinkStateUpdater {
|
||||
rootKey: CallLinkRootKey,
|
||||
updateAndFetch: (CallLinkAuthCredential) async throws -> SignalServiceKit.CallLinkState
|
||||
) async throws -> SignalServiceKit.CallLinkState {
|
||||
return try await _updateExclusively(rootKey: rootKey, updateAndFetch: updateAndFetch)!
|
||||
return try await _updateExclusively(rootKey: rootKey, updateAndFetch: updateAndFetch)!.get()
|
||||
}
|
||||
|
||||
private enum UpdateAction {
|
||||
case update(SignalServiceKit.CallLinkState)
|
||||
case notFound
|
||||
case delete
|
||||
}
|
||||
|
||||
private func _updateExclusively(
|
||||
rootKey: CallLinkRootKey,
|
||||
updateAndFetch: (CallLinkAuthCredential) async throws -> SignalServiceKit.CallLinkState?
|
||||
) async throws -> SignalServiceKit.CallLinkState? {
|
||||
) async throws -> Result<SignalServiceKit.CallLinkState, CallLinkNotFoundError>? {
|
||||
let roomId = rootKey.deriveRoomId()
|
||||
|
||||
await withCheckedContinuation { continuation in
|
||||
@ -90,13 +96,34 @@ actor CallLinkStateUpdater {
|
||||
return try callLinkStore.fetch(roomId: roomId, tx: tx)
|
||||
}
|
||||
let authCredential = try await authCredentialManager.fetchCallLinkAuthCredential(localIdentifiers: localIdentifiers)
|
||||
let newState = try await updateAndFetch(authCredential)
|
||||
let updateResult = await Result { try await updateAndFetch(authCredential) }
|
||||
|
||||
let updateAction: UpdateAction
|
||||
let returnResult: Result<SignalServiceKit.CallLinkState, CallLinkNotFoundError>?
|
||||
|
||||
switch updateResult {
|
||||
case .success(let callLinkState?):
|
||||
updateAction = .update(callLinkState)
|
||||
returnResult = .success(callLinkState)
|
||||
case .success(nil):
|
||||
updateAction = .delete
|
||||
returnResult = nil
|
||||
case .failure(let error as CallLinkNotFoundError):
|
||||
updateAction = .notFound
|
||||
returnResult = .failure(error)
|
||||
case .failure(let error):
|
||||
throw error
|
||||
}
|
||||
|
||||
try await db.awaitableWrite { tx in
|
||||
if var newRecord = try self.callLinkStore.fetch(roomId: roomId, tx: tx) {
|
||||
if !newRecord.isDeleted {
|
||||
if let newState {
|
||||
switch updateAction {
|
||||
case .update(let newState):
|
||||
newRecord.updateState(newState)
|
||||
} else {
|
||||
case .notFound:
|
||||
break
|
||||
case .delete:
|
||||
newRecord.markDeleted(atTimestampMs: Date.ows_millisecondTimestamp())
|
||||
try self.callRecordDeleteManager.deleteCallRecords(
|
||||
self.callRecordStore.fetchExisting(conversationId: .callLink(callLinkRowId: newRecord.id), limit: nil, tx: tx),
|
||||
@ -111,13 +138,27 @@ actor CallLinkStateUpdater {
|
||||
try self.callLinkStore.update(newRecord, tx: tx)
|
||||
}
|
||||
}
|
||||
return newState
|
||||
|
||||
return returnResult
|
||||
}
|
||||
|
||||
func readCallLink(rootKey: CallLinkRootKey) async throws -> SignalServiceKit.CallLinkState {
|
||||
return try await updateExclusively(rootKey: rootKey, updateAndFetch: { authCredential in
|
||||
/// Reads a call link from the server.
|
||||
///
|
||||
/// There are two layers of errors interesting to callers: the method itself
|
||||
/// and the `Result` that's returned.
|
||||
///
|
||||
/// This is a "state updater" object, so if the "state update" operation is
|
||||
/// successful, no error is thrown. The "state update" is successful when
|
||||
/// we're able to call `clearNeedsFetch` on the underlying CallLinkRecord.
|
||||
/// (For example, no error is thrown when the call link can't be found, but
|
||||
/// an error *is* thrown when there's no network.)
|
||||
///
|
||||
/// Many callers will want access to the `CallLinkState`, and they can use
|
||||
/// `try readCallLink(...).get()` to gloss over this distinction.
|
||||
func readCallLink(rootKey: CallLinkRootKey) async throws -> Result<SignalServiceKit.CallLinkState, CallLinkNotFoundError> {
|
||||
return try await _updateExclusively(rootKey: rootKey, updateAndFetch: { authCredential in
|
||||
return try await callLinkFetcher.readCallLink(rootKey, authCredential: authCredential)
|
||||
})
|
||||
})!
|
||||
}
|
||||
|
||||
func deleteCallLink(rootKey: CallLinkRootKey, adminPasskey: Data) async throws {
|
||||
|
||||
@ -569,7 +569,7 @@ final class CallService: CallServiceStateObserver, CallServiceStateDelegate {
|
||||
case .reuse(let callLinkState):
|
||||
state = callLinkState
|
||||
case .fetch:
|
||||
state = try await callLinkStateUpdater.readCallLink(rootKey: callLink.rootKey)
|
||||
state = try await callLinkStateUpdater.readCallLink(rootKey: callLink.rootKey).get()
|
||||
}
|
||||
let localIdentifiers = DependenciesBridge.shared.tsAccountManager.localIdentifiersWithMaybeSneakyTransaction!
|
||||
let authCredential = try await authCredentialManager.fetchCallLinkAuthCredential(localIdentifiers: localIdentifiers)
|
||||
|
||||
@ -8,6 +8,8 @@ import LibSignalClient
|
||||
public import SignalRingRTC
|
||||
public import SignalServiceKit
|
||||
|
||||
public struct CallLinkNotFoundError: Error {}
|
||||
|
||||
public class CallLinkFetcherImpl {
|
||||
private let sfuClient: SFUClient
|
||||
// Even though we never use this, we need to retain it to ensure
|
||||
@ -27,20 +29,24 @@ public class CallLinkFetcherImpl {
|
||||
let sfuUrl = DebugFlags.callingUseTestSFU.get() ? TSConstants.sfuTestURL : TSConstants.sfuURL
|
||||
let secretParams = CallLinkSecretParams.deriveFromRootKey(rootKey.bytes)
|
||||
let authCredentialPresentation = authCredential.present(callLinkParams: secretParams)
|
||||
return SignalServiceKit.CallLinkState(try await self.sfuClient.readCallLink(
|
||||
sfuUrl: sfuUrl,
|
||||
authCredentialPresentation: authCredentialPresentation.serialize(),
|
||||
linkRootKey: rootKey
|
||||
).unwrap())
|
||||
do {
|
||||
return try await SignalServiceKit.CallLinkState(self.sfuClient.readCallLink(
|
||||
sfuUrl: sfuUrl,
|
||||
authCredentialPresentation: authCredentialPresentation.serialize(),
|
||||
linkRootKey: rootKey
|
||||
).unwrap())
|
||||
} catch where error.rawValue == 404 {
|
||||
throw CallLinkNotFoundError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct SFUError: Error {
|
||||
public struct SFUError: Error {
|
||||
let rawValue: UInt16
|
||||
}
|
||||
|
||||
extension SFUResult {
|
||||
public func unwrap() throws -> Value {
|
||||
public func unwrap() throws(SFUError) -> Value {
|
||||
switch self {
|
||||
case .success(let value):
|
||||
return value
|
||||
|
||||
Loading…
Reference in New Issue
Block a user