Skip to content

Commit cdc66db

Browse files
committed
Add MatrixInteractiveAuth to error struct
1 parent 050aa48 commit cdc66db

File tree

9 files changed

+188
-81
lines changed

9 files changed

+188
-81
lines changed

Sources/MatrixClient/API/Auth/Interactive.swift

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,12 @@ public struct MatrixInteractiveAuth: MatrixResponse {
1313
flows: [MatrixInteractiveAuth.Flow],
1414
params: [String: AnyCodable],
1515
session: String? = nil,
16-
completed: [MatrixLoginFlow]? = nil,
17-
error: String? = nil,
18-
errcode: MatrixErrorCode? = nil
16+
completed: [MatrixLoginFlow]? = nil
1917
) {
2018
self.flows = flows
2119
self.params = params
2220
self.session = session
2321
self.completed = completed
24-
self.error = error
25-
self.errcode = errcode
2622
}
2723

2824
public var flows: [Flow]
@@ -38,9 +34,6 @@ public struct MatrixInteractiveAuth: MatrixResponse {
3834

3935
public var completed: [MatrixLoginFlow]?
4036

41-
public var error: String?
42-
public var errcode: MatrixErrorCode?
43-
4437
// MARK: Dynamic vars
4538

4639
public var notCompletedStages: [Flow] {
@@ -100,8 +93,6 @@ public struct MatrixInteractiveAuth: MatrixResponse {
10093
case flows
10194
case params
10295
case completed
103-
case error
104-
case errcode
10596
}
10697

10798
public struct LoginFlowWithParams {

Sources/MatrixClient/API/Auth/Register.swift

Lines changed: 1 addition & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -64,22 +64,10 @@ extension MatrixRegisterRequest: MatrixRequest {
6464
false
6565
}
6666

67-
public typealias Response = MatrixRegisterContainer
67+
public typealias Response = MatrixRegister
6868

6969
/// The kind of account to register. Defaults to user.
7070
public typealias URLParameters = MatrixRegisterRequest.RegisterKind
71-
72-
public func parse(data: Data, response: HTTPURLResponse) throws -> Response {
73-
guard response.statusCode != 401 else {
74-
return try MatrixRegisterContainer.interactive(.init(fromMatrixRequestData: data))
75-
}
76-
77-
guard response.statusCode == 200 else {
78-
throw try MatrixServerError(json: data, code: response.statusCode)
79-
}
80-
81-
return try MatrixRegisterContainer.success(.init(fromMatrixRequestData: data))
82-
}
8371
}
8472

8573
public struct MatrixRegister: MatrixResponse {
@@ -121,39 +109,6 @@ public struct MatrixRegister: MatrixResponse {
121109
}
122110
}
123111

124-
/// Container to either hold a successfully register answer, or an answer to do it interactivly.
125-
public enum MatrixRegisterContainer: MatrixResponse {
126-
case success(MatrixRegister)
127-
case interactive(MatrixInteractiveAuth)
128-
129-
public var isSuccess: Bool {
130-
switch self {
131-
case .success:
132-
return true
133-
case .interactive:
134-
return false
135-
}
136-
}
137-
138-
public var successData: MatrixRegister? {
139-
switch self {
140-
case let .success(register):
141-
return register
142-
case .interactive:
143-
return nil
144-
}
145-
}
146-
147-
public var interactiveData: MatrixInteractiveAuth? {
148-
switch self {
149-
case .success:
150-
return nil
151-
case let .interactive(interactive):
152-
return interactive
153-
}
154-
}
155-
}
156-
157112
public struct MatrixRegisterRequestEmailTokenRequest: MatrixRequest {
158113
public init(clientSecret: String, email: String, sendAttempt: Int = 0) {
159114
self.clientSecret = clientSecret

Sources/MatrixClient/API/WhoAmI.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
//
22
// WhoAmI.swift
3-
//
3+
//
44
//
55
// Created by Finn Behrens on 23.04.22.
66
//
77

88
import Foundation
99

1010
public struct MatrixWhoAmIRequest: MatrixRequest {
11-
public func components(for homeserver: MatrixHomeserver, with parameters: ()) throws -> URLComponents {
11+
public func components(for homeserver: MatrixHomeserver, with _: ()) throws -> URLComponents {
1212
homeserver.path("/_matrix/client/v3/account/whoami")
1313
}
1414

Sources/MatrixClient/Error.swift

Lines changed: 115 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Created by Finn Behrens on 07.08.21.
66
//
77

8+
import AnyCodable
89
import Foundation
910

1011
public enum MatrixCommonErrorCode: String, Error, Codable {
@@ -250,6 +251,13 @@ public extension MatrixErrorCode {
250251
}
251252

252253
public struct MatrixServerError: Error, Codable {
254+
public init(errcode: MatrixErrorCode, error: String, code: Int? = nil, extraInfo: [String: AnyCodable] = [:]) {
255+
self.errcode = errcode
256+
self.error = error
257+
self.code = code
258+
self.extraInfo = extraInfo
259+
}
260+
253261
/// Error code
254262
public var errcode: MatrixErrorCode
255263

@@ -259,30 +267,128 @@ public struct MatrixServerError: Error, Codable {
259267
/// HTTP status code
260268
public var code: Int?
261269

270+
public var interactiveAuth: MatrixInteractiveAuth?
271+
272+
public var extraInfo: [String: AnyCodable]
273+
262274
// TODO: extra data
263275

264276
public init(json: Data, code: Int? = nil) throws {
265277
let decoder = JSONDecoder()
278+
decoder.userInfo[.matrixErrorHttpCode] = code
266279

267280
do {
268281
self = try decoder.decode(Self.self, from: json)
269282
} catch {
270-
throw MatrixInvalidError(data: json, code: code)
283+
throw MatrixServerError(
284+
errcode: .Unknown,
285+
error: error.localizedDescription,
286+
code: code,
287+
extraInfo: ["json": .init(json)]
288+
)
271289
}
272290
self.code = code
273291
}
274292
}
275293

276-
public struct MatrixInvalidError: Error, LocalizedError {
277-
public var data: Data
278-
public var code: Int?
294+
public extension MatrixServerError {
295+
internal enum KnownCodingKeys: String, CodingKey, CaseIterable {
296+
case errcode
297+
case error
279298

280-
public init(data: Data, code: Int? = nil) {
281-
self.data = data
282-
self.code = code
299+
static let extraIgnoreValues = [
300+
"session",
301+
"flows",
302+
"params",
303+
"completed",
304+
]
305+
306+
static func doesNotContain(_ key: DynamicCodingKeys) -> Bool {
307+
!Self.allCases.map(\.stringValue).contains(key.stringValue) && !Self.extraIgnoreValues
308+
.contains(key.stringValue)
309+
}
310+
}
311+
312+
internal struct DynamicCodingKeys: CodingKey {
313+
var stringValue: String
314+
init?(stringValue: String) {
315+
self.stringValue = stringValue
316+
}
317+
318+
// not used here, but a protocol requirement
319+
var intValue: Int?
320+
init?(intValue _: Int) {
321+
nil
322+
}
283323
}
284324

285-
var localisedDescription: String {
286-
NSLocalizedString("Failed to parse error result for code \(code ?? -1)", comment: "MatrixInvalidError")
325+
init(from decoder: Decoder) throws {
326+
let container = try decoder.container(keyedBy: KnownCodingKeys.self)
327+
errcode = try container.decodeIfPresent(MatrixErrorCode.self, forKey: .errcode) ?? .Unknown
328+
error = try container.decodeIfPresent(String.self, forKey: .error) ?? ""
329+
330+
extraInfo = [:]
331+
let extraContainer = try decoder.container(keyedBy: DynamicCodingKeys.self)
332+
333+
for key in extraContainer.allKeys where KnownCodingKeys.doesNotContain(key) {
334+
let decoded = try extraContainer.decode(
335+
AnyCodable.self,
336+
forKey: DynamicCodingKeys(stringValue: key.stringValue)!
337+
)
338+
self.extraInfo[key.stringValue] = decoded
339+
}
340+
341+
guard let code = decoder.userInfo[.matrixErrorHttpCode] as? Int,
342+
code == 401
343+
else {
344+
return
345+
}
346+
347+
do {
348+
interactiveAuth = try MatrixInteractiveAuth(from: decoder)
349+
} catch {
350+
// don't care if it fails
351+
}
352+
}
353+
354+
func encode(to encoder: Encoder) throws {
355+
var container = encoder.container(keyedBy: KnownCodingKeys.self)
356+
try container.encode(errcode, forKey: .errcode)
357+
try container.encode(error, forKey: .error)
358+
359+
var extraContainer = encoder.container(keyedBy: DynamicCodingKeys.self)
360+
for (name, value) in extraInfo {
361+
try extraContainer.encode(value, forKey: .init(stringValue: name)!)
362+
}
363+
}
364+
}
365+
366+
public extension MatrixServerError {
367+
var is401: Bool {
368+
errcode == .Unauthorized && code == 401
369+
}
370+
371+
var is404: Bool {
372+
errcode == .NotFound && code == 404
373+
}
374+
375+
var isTokenError: Bool {
376+
errcode == .UnknownToken || errcode == .MissingToken
377+
}
378+
379+
var isLimitexceededError: Bool {
380+
code == 429 && errcode == .LimitExceeded
381+
}
382+
383+
var shouldbeRetried: Bool {
384+
// Investigate network error codes
385+
isLimitexceededError
386+
}
387+
}
388+
389+
extension CodingUserInfoKey {
390+
/// The key used to determine the types of `MatrixEvent` that can be decoded.
391+
static var matrixErrorHttpCode: CodingUserInfoKey {
392+
CodingUserInfoKey(rawValue: "MatrixClient.ErrorHttpCode")!
287393
}
288394
}

Sources/MatrixClient/MatrixClient.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,10 @@ public struct MatrixClient {
145145
.response(on: homeserver, withToken: accessToken, with: (), withUrlSession: urlSession, callback: callback)
146146
}
147147

148-
149148
@available(swift, introduced: 5.5)
150149
@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
151150
public func whoami() async throws -> MatrixWhoAmI {
152-
return try await MatrixWhoAmIRequest()
151+
try await MatrixWhoAmIRequest()
153152
.response(on: homeserver, withToken: accessToken, with: (), withUrlSession: urlSession)
154153
}
155154

Sources/MatrixClient/MatrixClient/MatrixClient+Auth.swift

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,16 @@ public extension MatrixClient {
3636

3737
@available(swift, introduced: 5.5)
3838
@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
39-
func getRegisterFlows(kind: MatrixRegisterRequest.RegisterKind = .user) async throws -> MatrixInteractiveAuth {
40-
let resp = try await MatrixRegisterRequest(password: "")
41-
.response(on: homeserver, with: kind, withUrlSession: urlSession)
42-
43-
switch resp {
44-
case let .interactive(flows):
45-
return flows
46-
default:
47-
throw MatrixErrorCode.NotFound
39+
func getRegisterFlows(kind: MatrixRegisterRequest.RegisterKind = .user) async throws -> MatrixInteractiveAuth? {
40+
do {
41+
_ = try await MatrixRegisterRequest(password: "")
42+
.response(on: homeserver, with: kind, withUrlSession: urlSession)
43+
return nil
44+
} catch let error as MatrixServerError {
45+
guard let interactive = error.interactiveAuth else {
46+
throw error
47+
}
48+
return interactive
4849
}
4950
}
5051

@@ -72,7 +73,7 @@ public extension MatrixClient {
7273
auth: MatrixInteractiveAuthResponse? = nil,
7374
bind_email: Bool? = nil,
7475
kind: MatrixRegisterRequest.RegisterKind = .user
75-
) async throws -> MatrixRegisterContainer {
76+
) async throws -> MatrixRegister {
7677
try await MatrixRegisterRequest(
7778
username: username,
7879
bindEmail: bind_email,

Tests/MatrixClientTests/ApiAuthInteractiveTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ final class ApiAuthInteractiveTests: XCTestCase {
1515
MatrixInteractiveAuth
1616
.Flow(stages: [MatrixLoginFlow.recaptcha, MatrixLoginFlow.token, MatrixLoginFlow.oauth2,
1717
MatrixLoginFlow.email]),
18-
], params: [:], session: nil, completed: [MatrixLoginFlow.email, MatrixLoginFlow.terms], error: nil, errcode: nil)
18+
], params: [:], session: nil, completed: [MatrixLoginFlow.email, MatrixLoginFlow.terms])
1919

2020
func testIsOptional() throws {
2121
XCTAssertTrue(flow.isOptional(.recaptcha))

Tests/MatrixClientTests/ApiCapabilitiesTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ final class ApiCapabilityTests: XCTestCase {
5252
.value as? [String: Any],
5353
let ratelimit = ratelimitContainer["max_requests_per_hour"] as? Int
5454
else {
55-
throw MatrixError.BadJSON
55+
throw MatrixErrorCode.BadJSON
5656
}
5757

5858
XCTAssertEqual(ratelimit, 600)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//
2+
// ErrorTests.swift
3+
//
4+
//
5+
// Created by Finn Behrens on 24.04.22.
6+
//
7+
8+
@testable import MatrixClient
9+
import XCTest
10+
11+
class ErrorTests: XCTestCase {
12+
func testDoesNotContain() throws {
13+
XCTAssertFalse(MatrixServerError.KnownCodingKeys.doesNotContain(.init(stringValue: "error")!))
14+
XCTAssertFalse(MatrixServerError.KnownCodingKeys.doesNotContain(.init(stringValue: "flows")!))
15+
}
16+
17+
func testExample() throws {
18+
let data = Data("""
19+
{
20+
"session": "session_id",
21+
"flows": [
22+
{
23+
"stages": [
24+
"m.login.recaptcha",
25+
"m.login.terms",
26+
"m.login.email.identity"
27+
]
28+
}
29+
],
30+
"params": {
31+
"m.login.recaptcha": {
32+
"public_key": "recaptha_public_key"
33+
},
34+
"m.login.terms": {
35+
"policies": {
36+
"privacy_policy": {
37+
"version": "1.0",
38+
"en": {
39+
"name": "Terms and Conditions",
40+
"url": "https://example.com/_matrix/consent?v=1.0"
41+
}
42+
}
43+
}
44+
}
45+
}
46+
}
47+
""".utf8)
48+
49+
let error = try MatrixServerError(json: data, code: 401)
50+
51+
XCTAssertNotNil(error.interactiveAuth)
52+
XCTAssertEqual(error.interactiveAuth?.session, "session_id")
53+
XCTAssertEqual(error.interactiveAuth?.flows.count, 1)
54+
}
55+
}

0 commit comments

Comments
 (0)