taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

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 }