Using HQ API
“HQ” is the business logic layer of the Q2 platform. It handles authentication, session, communication with cores, and database access. Easy access to HQ is one of the most powerful features of the Caliper SDK.
For our transaction history, we need a list of the recent transactions made against a selected account. We can easily get this using our HQ API. More specifically, we’re going to use the GetAccountHistoryById module module.
First, we’re going to import the API endpoint we wish to use at the top of the file with the other imports:
from q2_sdk.hq.hq_api.q2_api import GetAccountHistoryById
An HQ call using this endpoint resembles the following code sample (We’ll tell you where to add it below):
params_obj = GetAccountHistoryById.ParamsObj(
self.logger,
self.form_fields['account_id'],
'',
hq_credentials=self.hq_credentials
)
hq_response = await GetAccountHistoryById.execute(params_obj)
We need to construct a ParamsObj
for our HQ call. Each endpoint has its own required parameters, information about which
lives inside the code as type hints and parameter docstrings. A full reference guide to HQ API can be found here.
In our case, the ParamsObj
specific to GetAccountHistoryById
requires, in order:
Our built-in logger object
The
account_id
from the input, helpfully stored inside theself.form_fields
dictionaryA filter, which we don’t need right now, so we’ll just set it to blank
self.hq_credentials
is optional but highly recommended to allow flexible deployment options once in our datacenter
Before we update our AccountDashboard/extension.py
, let’s take a minute to setup our results display. We’ll review this in detail in the next step.
For now, create a file in AccountDashboard/templates
called transaction_history.html.jinja2
, and drop in the following:
<div>
<a href="#default">Back to Accounts</a>
</div>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th width="100">
Date
</th>
<th>
Description
</th>
<th class="text-right">
Amount
</th>
</tr>
</thead>
<tbody>
{% for transaction in transactions %}
<tr>
<td>{{transaction.display_date}}</td>
<td>{{transaction.display_description}}</td>
{% if transaction.is_credit %}
<td class="clr(const-stoplight-success)">+{{transaction.display_amount}}</td>
{% else %}
<td>-{{transaction.display_amount}}</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
Now, for submit
:
async def submit(self):
params_obj = GetAccountHistoryById.ParamsObj(
self.logger,
self.form_fields['account_id'],
'',
hq_credentials=self.hq_credentials
)
hq_response = await GetAccountHistoryById.execute(params_obj)
transaction_models = []
for transaction in hq_response.result_node.Data.AllHostTransactions.Transactions:
display_date = str(transaction.PostDate)
display_amount = str(transaction.TxnAmount)
is_credit = (transaction.SignedTxnAmount > 0)
display_description = str(transaction.Description)
transaction_models.append({
'display_date': display_date,
'display_amount': display_amount,
'is_credit': is_credit,
'display_description': display_description
})
template = self.get_template(
'transaction_history.html.jinja2', {
'header': "Transaction History",
'transactions': transaction_models,
'current_account_id': self.form_fields['account_id'],
}
)
html = self.get_tecton_form(
"Transaction History",
custom_template=template,
hide_submit_button=True
)
return html
Let’s make our actual HQ call. GetAccountHistoryById.execute
is an asynchronous function, allowing our extensions to serve many hundreds of simultaneous user interactions.
To take advantage of this, we use the await
keyword provided with Python 3.5+. This is also why we define submit
with the
async
helper. If you’ve ever dealt with asynchronous coding challenges, you’ll appreciate how easy modern Python makes this!
The data we wanted is nested pretty deeply inside our hq_response
object: this structure is an objectified representation
of the complex XML structure that HQ returns. Luckily, if you are using a good IDE like PyCharm, you should have type hinting
for the most common response types to help you find what you need.
In this case, Transactions
is the level we need. We iterate over that list, then convert and print the date and amount of each transaction made
against this account, one per line. Try it out and see for yourself.
What we have now in the most basic kind of Caliper SDK extension: it renders a form, and handles the submission of that form.
In the next section, we’ll review the use of Templates and the role they play in the SDK.