Model+Transactions.swift (8016B)
1 /* 2 * This file is part of GNU Taler, ©2022-25 Taler Systems S.A. 3 * See LICENSE.md 4 */ 5 /** 6 * @author Marc Stibane 7 */ 8 import Foundation 9 import taler_swift 10 import SymLog 11 12 // MARK: - 13 extension WalletModel { 14 static func completedTransactions(_ transactions: [TalerTransaction]) -> [TalerTransaction] { 15 transactions.filter { transaction in 16 !transaction.isPending 17 } 18 } 19 static func pendingTransactions(_ transactions: [TalerTransaction]) -> [TalerTransaction] { 20 transactions.filter { transaction in 21 transaction.isPending 22 } 23 } 24 } 25 // MARK: - 26 /// A request to get the transactions in the wallet's history. 27 fileprivate struct GetTransactions: WalletBackendFormattedRequest { 28 func operation() -> String { "getTransactions" } 29 func args() -> Args { Args(scopeInfo: scopeInfo, currency: currency, search: search, 30 sort: sort, filterByState: filterByState, includeRefreshes: includeRefreshes) } 31 var scopeInfo: ScopeInfo? 32 var currency: String? 33 var search: String? 34 var sort: String? 35 var filterByState: TransactionStateFilter? 36 var includeRefreshes: Bool? 37 struct Args: Encodable { 38 var scopeInfo: ScopeInfo? 39 var currency: String? 40 var search: String? 41 var sort: String? 42 var filterByState: TransactionStateFilter? 43 var includeRefreshes: Bool? 44 } 45 46 struct Response: Decodable { // list of transactions 47 var transactions: [TalerTransaction] 48 } 49 } 50 enum TransactionStateFilter: String, Codable { 51 case done 52 case final // done, aborted, expired, ... 53 case nonfinal // pending, dialog, suspended, aborting 54 case nonfinalApproved = "nonfinal-approved" 55 case nonfinalDialog = "nonfinal-dialog" 56 } 57 struct TransactionOffset: Codable { 58 var txID: String 59 var txTime: Timestamp 60 } 61 /// A request to get the transactions in the wallet's history. 62 fileprivate struct GetTransactionsV2: WalletBackendFormattedRequest { 63 func operation() -> String { "getTransactionsV2" } 64 func args() -> Args { Args(scopeInfo: scope, 65 filterByState: filterByState, 66 includeRefreshes: includeRefreshes, 67 limit: -limit, // descending 68 offsetTransactionId: offset?.txID, 69 offsetTimestamp: offset?.txTime) } 70 71 var scope: ScopeInfo? 72 var filterByState: TransactionStateFilter? 73 var limit: Int 74 var offset: TransactionOffset? 75 var includeRefreshes: Bool? 76 struct Args: Encodable { 77 var scopeInfo: ScopeInfo? 78 var filterByState: TransactionStateFilter? 79 var includeRefreshes: Bool? 80 var limit: Int 81 var offsetTransactionId: String? 82 var offsetTimestamp: Timestamp? 83 } 84 85 struct Response: Decodable { // list of transactions 86 var transactions: [TalerTransaction] 87 } 88 } 89 /// A request to abort a wallet transaction by ID. 90 struct AbortTransaction: WalletBackendFormattedRequest { 91 struct Response: Decodable {} // no result - getting no error back means success 92 func operation() -> String { "abortTransaction" } 93 func args() -> Args { Args(transactionId: transactionId) } 94 95 var transactionId: String 96 struct Args: Encodable { 97 var transactionId: String 98 } 99 } 100 /// A request to delete a wallet transaction by ID. 101 struct DeleteTransaction: WalletBackendFormattedRequest { 102 struct Response: Decodable {} // no result - getting no error back means success 103 func operation() -> String { "deleteTransaction" } 104 func args() -> Args { Args(transactionId: transactionId) } 105 106 var transactionId: String 107 struct Args: Encodable { 108 var transactionId: String 109 } 110 } 111 /// A request to delete a wallet transaction by ID. 112 struct FailTransaction: WalletBackendFormattedRequest { 113 struct Response: Decodable {} // no result - getting no error back means success 114 func operation() -> String { "failTransaction" } 115 func args() -> Args { Args(transactionId: transactionId) } 116 117 var transactionId: String 118 struct Args: Encodable { 119 var transactionId: String 120 } 121 } 122 /// A request to suspend a wallet transaction by ID. 123 struct SuspendTransaction: WalletBackendFormattedRequest { 124 struct Response: Decodable {} // no result - getting no error back means success 125 func operation() -> String { "suspendTransaction" } 126 func args() -> Args { Args(transactionId: transactionId) } 127 128 var transactionId: String 129 struct Args: Encodable { 130 var transactionId: String 131 } 132 } 133 /// A request to suspend a wallet transaction by ID. 134 struct ResumeTransaction: WalletBackendFormattedRequest { 135 struct Response: Decodable {} // no result - getting no error back means success 136 func operation() -> String { "resumeTransaction" } 137 func args() -> Args { Args(transactionId: transactionId) } 138 139 var transactionId: String 140 struct Args: Encodable { 141 var transactionId: String 142 } 143 } 144 // MARK: - 145 extension WalletModel { 146 /// ask wallet-core for its list of transactions filtered by state 147 nonisolated func getTransactionsV2(_ stack: CallStack, 148 scope: ScopeInfo, 149 filterByState: TransactionStateFilter, 150 limit: Int = 1_000_000, 151 offset: TransactionOffset? = nil, 152 includeRefreshes: Bool = false, 153 viewHandles: Bool = false) 154 async throws -> [TalerTransaction] { 155 let request = GetTransactionsV2(scope: scope, 156 filterByState: filterByState, 157 limit: limit, 158 offset: offset, 159 includeRefreshes: includeRefreshes) 160 let response = try await sendRequest(request, viewHandles: viewHandles) 161 return response.transactions 162 } 163 // MARK: - 164 /// abort the specified transaction from Wallet-Core. No networking involved 165 nonisolated func abortTransaction(transactionId: String, viewHandles: Bool = false) async throws { 166 let request = AbortTransaction(transactionId: transactionId) 167 logger.notice("abortTransaction: \(transactionId, privacy: .private(mask: .hash))") 168 let _ = try await sendRequest(request, viewHandles: viewHandles) 169 } 170 171 /// delete the specified transaction from Wallet-Core. No networking involved 172 nonisolated func deleteTransaction(transactionId: String, viewHandles: Bool = false) async throws { 173 let request = DeleteTransaction(transactionId: transactionId) 174 logger.notice("deleteTransaction: \(transactionId, privacy: .private(mask: .hash))") 175 let _ = try await sendRequest(request, viewHandles: viewHandles) 176 } 177 178 nonisolated func failTransaction(transactionId: String, viewHandles: Bool = false) async throws { 179 let request = FailTransaction(transactionId: transactionId) 180 logger.notice("failTransaction: \(transactionId, privacy: .private(mask: .hash))") 181 let _ = try await sendRequest(request, viewHandles: viewHandles) 182 } 183 184 nonisolated func suspendTransaction(transactionId: String, viewHandles: Bool = false) async throws { 185 let request = SuspendTransaction(transactionId: transactionId) 186 logger.notice("suspendTransaction: \(transactionId, privacy: .private(mask: .hash))") 187 let _ = try await sendRequest(request, viewHandles: viewHandles) 188 } 189 190 nonisolated func resumeTransaction(transactionId: String, viewHandles: Bool = false) async throws { 191 let request = ResumeTransaction(transactionId: transactionId) 192 logger.notice("resumeTransaction: \(transactionId, privacy: .private(mask: .hash))") 193 let _ = try await sendRequest(request, viewHandles: viewHandles) 194 } 195 }