TUTORIAL: Bank Tutorial 4 (Programmatic Access)

The following is a tutorial on using the Bank Client programmatically (as opposed to interactively as was covered in Part 3, see also Part 2 and Part 1).


The Short Version

The Bank Client Protocol is around the concepts of firing off a message to the server, then registering a callback to receive the result. Let’s look at a simple example: getBalance

def balanceCallback(msgObj):
  print "Got balance from server:", msgObj.Balance

def balanceErrback(e):
  print "get balance failed. Reason:", str(e)

d = bankClient.getBalance()
d.addCallback(balanceCallback)
d.addErrback(balanceErrback)

Assuming that bankClient is our protocol instance, we call getBalance to trigger a transmission to the server. But because we don’t know when the server will get back to us, we register callbacks and errback’s. When either a success or failure comes back, we’ll be notified.


The Longer Version

To use the bank programatically, first you have to import PlaygroundOnlineBankClient, which is the bank client factory. I’m also going to import some other components that give us some simplifications for later.

Then, you need to connect to the bank server using the factory and get the protocol that this produces. Let’s look at the code:

from apps.bank.OnlineBank import PlaygroundOnlineBankClient 			# this is the factory
from apps.bank.OnlineBank import BankClientProtocol, BankClientSimpleCommand 	# this is a simplification 
										#I'll show you later

# assume we have code that gives us our clientbase
bankFactory = PlaygroundOnlineBankClient(cert, userName, password)
srcPort, bankProtocol = self.__playground.connect(bankFactory,
                                                  bankAddr, 
                                                  800,
                                                  connectionType="RAW")

You can ignore srcPort. The part we care about is bankProtocol, which is a protocol that has a connection to the bank server (or will very shortly).

Now that we have the protocol, we can use its methods to connect to the bank, check our balance, and transfer bitpoints. Every operation on the protocol returns a Twisted Deferred, which is basically a callback holder. So, for each call, you need to be prepared to give it a callback for correct results and a callback for failures.

We start by logging in and switching to the right account. Here’s a sample of how we might login.

def handleSuccessfulLoginCallback(result):
  print "We logged in!"

def handleLoginFailureCallback(result):
  print "We couldn't login! Error mesage: %s" % result

d = bankProtocol.loginToServer()
d.addCallback(handleSuccessfulLoginCallback)
d.addErrback(handleLoginFailureCallback)

Callbacks for twisted deferreds always take just one parameter: “result”.  And errbacks always take just one parameter: “failure”. The “result” value can be anything, including a sequence of many values while “failure” is always an exception of some type (so, you could raise it if you wanted to).

For all the bank callbacks, the result will always be the msgObj sent back from the server (the message builder’s .data() result). So, it’s basically just a struct with fields of data. If you want to know what’s in it, check out the descriptions of each message in BankMessages.py.

Once you’ve logged in, you can switch accounts. Let’s rewrite our successful login above to do that:

def handleSuccessfulLoginCallback(result):
  print "We logged in! Switching to account myaccount1"
  # assume we have bankprotocol... could be self.protocol for example, or a global
  d = bankProtocol.switchAccount("myaccount1")
  d.addCallback(handleSuccessfulAccountSwitchCallback)
  d.addErrback(handleSwitchAccountFailure)

You may think it tedious to do all these callbacks, but they’re amazingly powerful. And, if you’re clever, you can use the same callback for multiple operations. For example, for most of these, you don’t need a dedicated errback. Just something that prints the error and switches to an error state.

Once you’ve switched accounts, you can then call the transfer command to transfer money

d = bankProtocol.transfer("someaccount2", 50, "This is an API initiated transfer"
d.addCallback... # you know what to do here I hope
d.addErrback...

So, that’s an awful lot of code to execute a single command. Especially if you only want to do something simple (like check a balance), that’s a lot of boilerplate.

So, I’ve added a helper class called BankClientSimpleCommand. It takes a single command, performs the login, account switch, and single action before logging out. It does return a deferred, but you only need a single callback for the result of the final command.

For checking a balance, here’s what you do:

def balanceResponse(msgObj):
  print "Got balance", msgObj.Balance

def errHandler(failure):
  print "operation failed", failure

bankFactory = PlaygroundOnlineBankClient(cert, userName, password)
srcPort, bankProtocol = self.__playground.connect(bankFactory,
                                                  bankAddr, 
                                                  800,
                                                  connectionType="RAW")
bankCmd = BankClientSimpleCommand()
d = bankCmd(bankProtocol, "myaccount1", BankClientProtocol.getBalance)
d.addCallback(balanceResponse)
d.addErrback(errHandler)

Note that this helper logs the bank out when you’re done, so you can’t use bankProtocol afterwards. If you need to use multiple commands, you should use something other than BankClientSimpleCommand.

BankClientSimpleCommand is a class that is “callable”. That means that once you’ve instantiated the object (bankCmd) you can call it like a function. For commands that take arguments, like transfer, you’d do this:

def transferResponse(msgObj):
  print "transfer successful"
  # you could do something with the receipt if you needed to...

def errHandler(failure):
  print "operation failed", failure

bankFactory = PlaygroundOnlineBankClient(cert, userName, password)
srcPort, bankProtocol = self.__playground.connect(bankFactory,
                                                  bankAddr, 
                                                  800,
                                                  connectionType="RAW")
bankCmd = BankClientSimpleCommand()
d = bankCmd(bankProtocol, "myaccount1", BankClientProtocol.transfer, "someaccount2", 50, "A quick transfer")
d.addCallback(balanceResponse)
d.addErrback(errHandler)

 

Note that this code is actually in use in BasicMobileCodeClient.py.

Leave a Reply

Your email address will not be published. Required fields are marked *