-
Notifications
You must be signed in to change notification settings - Fork 0
/
RogersUser.swift
137 lines (121 loc) · 5.19 KB
/
RogersUser.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
/// An online user account
public protocol User {
/// User name used to login
var userName: String { get }
/// Credit Card Accounts the user has access to
var accounts: [Account] { get }
/// Authenticated
var authenticated: Bool { get }
/// Login and load a user account
/// - Parameters:
/// - username: user name
/// - password: password
/// - deviceId: device id to skip 2FA
/// - deviceInfo: device info to skip 2FA
/// - completion: completion handler, returns the User or a Download Error
static func load(username: String, password: String, deviceId: String, deviceInfo: String, completion: @escaping (Result<User, DownloadError>) -> Void)
}
public struct RogersUser: User, Codable {
enum CodingKeys: String, CodingKey {
case userName
case authenticated
case rogersAccounts = "accounts"
}
private static var startURL = URL(string: "https://rbaccess.rogersbank.com/?product=ROGERSBRAND")!
private static var authenticationURL = URL(string: "https://rbaccess.rogersbank.com/issuing/digital/authenticate/user")!
private static let dateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
return dateFormatter
}()
private static let authenticationRequest: URLRequest = {
var request = URLRequest(url: authenticationURL)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("ROGERSBRAND", forHTTPHeaderField: "Brand_id")
request.setValue("json", forHTTPHeaderField: "Datatype")
request.setValue("web", forHTTPHeaderField: "Sourcetype")
return request
}()
public let userName: String
public let authenticated: Bool
private let rogersAccounts: [RogersAccount]
public var accounts: [Account] {
rogersAccounts
}
public static func load(username: String, password: String, deviceId: String, deviceInfo: String, completion: @escaping (Result<User, DownloadError>) -> Void) {
sendStartRequest {
if let error = $0 {
completion(.failure(error))
return
} else {
let parameters = [
"username": username,
"password": password,
"deviceId": deviceId,
"deviceInfo": deviceInfo
]
sendAuthenticationRequest(parameters: parameters) {
completion($0)
}
}
}
}
private static func sendStartRequest(completion: @escaping (DownloadError?) -> Void) {
var request = URLRequest(url: startURL)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard data != nil else {
if let error {
completion(DownloadError.httpError(error: error.localizedDescription))
} else {
completion(DownloadError.noDataReceived)
}
return
}
guard let httpResponse = response as? HTTPURLResponse else {
completion(DownloadError.httpError(error: "No HTTPURLResponse"))
return
}
guard httpResponse.statusCode == 200 else {
completion(DownloadError.httpError(error: "Status code \(httpResponse.statusCode)"))
return
}
completion(nil)
}
task.resume()
}
private static func sendAuthenticationRequest(parameters: [String: String], completion: @escaping (Result<User, DownloadError>) -> Void) {
guard let jsonData = try? JSONSerialization.data(withJSONObject: parameters, options: []) else {
completion(.failure(DownloadError.invalidParameters(parameters: parameters)))
return
}
let task = URLSession.shared.uploadTask(with: authenticationRequest, from: jsonData) { data, response, error in
let processedResponse = URLTaskHelper.processResponse(data: data, response: response, error: error)
guard case let .success((data, httpResponse)) = processedResponse else {
if case let .failure(error) = processedResponse {
completion(.failure(error))
}
return
}
guard httpResponse.statusCode == 200 else {
completion(.failure(DownloadError.httpError(error: "Status code \(httpResponse.statusCode)")))
return
}
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let user = try decoder.decode(Self.self, from: data)
completion(.success(user))
} catch {
completion(.failure(DownloadError.invalidJson(error: String(describing: error))))
}
}
task.resume()
}
}