Skip to content

Commit

Permalink
Updated docs with new design
Browse files Browse the repository at this point in the history
  • Loading branch information
JakeUrban committed Dec 5, 2019
1 parent b327087 commit 92278bc
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 95 deletions.
2 changes: 1 addition & 1 deletion docs/contents.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ django-polaris documentation contents
==========================================

.. toctree::
:hidden:

index

Expand All @@ -12,6 +11,7 @@ django-polaris documentation contents

integrations/index
models/index
forms/index


Indices and tables
Expand Down
5 changes: 5 additions & 0 deletions docs/forms/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
=============
Forms
=============

.. autoclass:: polaris.integrations.TransactionForm
123 changes: 39 additions & 84 deletions docs/integrations/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,112 +6,60 @@ Integrations
.. _Django Commands: https://docs.djangoproject.com/en/2.2/howto/custom-management-commands
.. _stellar-anchor-server: https://github.com/stellar/stellar-anchor-server

Polaris does most of the work implementing SEP-24_. However, Polaris simply
doesn't have the information it needs to interface with an anchor's partner
financial entities. This is where :class:`.DepositIntegration` and
:class:`.WithdrawalIntegration` come in.
Polaris does most of the work implementing SEP-24_. However, some pieces of
SEP-24 can only be implemented by the anchor. Specifically, anchors need to
implement their own banking rails and KYC requirements. This is where
:class:`DepositIntegration` and :class:`WithdrawalIntegration` come in.

Integration Base Classes
------------------------
These classes should be subclassed and its methods overridden by Polaris
developers to fill in the gaps in Polaris's functionality.

Polaris expects developers to override these base class methods and register
them using :func:`.register_integrations`.
Banking Rails
-------------
Polaris simply doesn't have the information it needs to interface with an
anchor's partner financial entities. That is why Polaris provides a set of
integration functions for anchors to override.

.. automodule:: polaris.integrations
:members: DepositIntegration, WithdrawalIntegration
:exclude-members: RegisteredDepositIntegration, RegisteredWithdrawalIntegration
.. autofunction:: polaris.integrations.DepositIntegration.poll_pending_deposits

Registering Integrations
------------------------
.. autofunction:: polaris.integrations.DepositIntegration.after_deposit

.. autofunction:: polaris.integrations.register_integrations
.. autofunction:: polaris.integrations.WithdrawalIntegration.process_withdrawal

Form Integrations
-----------------

.. _Bulma: https://bulma.io/documentation
.. _Django Forms: https://docs.djangoproject.com/en/2.2/topics/forms/#forms-in-django

Polaris uses `Django Forms`_ for collecting users' information, like their
Polaris uses `Django Forms`_ for collecting users' information, such as their
email or how much of their asset they want to deposit. Polaris comes out of
the box with forms for deposit and withdrawal flows.

However, the data collected may not be sufficient for your needs, or maybe you
need to do some validation or processing with the data that Polaris doesn't
already do. That is why Polaris provides the
:class:`polaris.integrations.TransactionForm` for you to subclass and extend.
However, the data collected may not be sufficient for your needs. Maybe you
need to do collect additional fields and some validation or processing with
the data that Polaris doesn't already do. Maybe you need to serve more forms
than just the one to collect transaction information.

.. autoclass:: polaris.integrations.TransactionForm

Lets look at an example to see how you could use this functionality.
You should be familiar with `Django Forms`_ and how they validate their inputs.

::
That is why Polaris provides a set of integration functions that allow you
to collect, validate, and process the information you need with as many forms
as you want. The set of functions documented below outline how Polaris
supports a customizable interactive flow.

from django import forms
from polaris.models import Transaction
from polaris.integrations import TransactionForm, DepositIntegration
from myapp.models import FormSubmissions

class MyDepositForm(TransactionForm):
"""This form accepts the amount to deposit from the user."""
first_name = forms.CharField(
widget=forms.widgets.TextInput(attrs={"class": "input"})
)
last_name = forms.CharField(
widget=forms.widgets.TextInput(attrs={"class": "input"})
)

def clean(self):
data = self.cleaned_data
if not (data["first_name"] and data["last_name"]):
raise ValidationError("Please enter your name.")
return data


class MyDepositIntegration(DepositIntegration):
def after_form_validation(self, form: forms.Form, transaction: Transaction):
"""Saves the data collected as a FormSubmission database object"""
data = form.cleaned_data
FormSubmission.objects.create(
name=" ".join(data["first_name"], data["last_name"])
amount=data["amount"]
asset=data["asset"],
transaction=transaction
)

The ``TransactionForm`` superclass collects the deposit amount and asset type
and validates that the amount is within the asset's accepted deposit range.
In this example, we've also added contact information fields to the form
and validate that they're not empty after submission.

Processing Form Submissions
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Once the form is validated, Polaris will call :func:`after_form_validation` on
the integration subclass, which in this case saves the form data collected to
the database.

Specifically, Polaris will facilitate that functionality like so:
::
See the :doc:`../forms/index` documentation for the `TransactionForm` definition.

from polaris.integrations import registered_deposit_integration as rdi
.. autofunction:: polaris.integrations.DepositIntegration.form_for_transaction

form = rdi.form(request.POST)
if form.is_valid():
afv = getattr(rdi, "after_form_validation", None)
if callable(afv):
afv(form, transaction)
.. autofunction:: polaris.integrations.DepositIntegration.after_form_validation

If form is not valid, Polaris will return a rendered form with the errors
raised during validation:
::
.. autofunction:: polaris.integrations.WithdrawalIntegration.form_for_transaction

else:
return Response({"form": form}, template_name="deposit/form.html")
.. autofunction:: polaris.integrations.WithdrawalIntegration.after_form_validation

Polaris does not yet allow you customize the template used to render the form,
although that functionality is on the road map. For now, you can be assured
that your ``ValidationError`` will be displayed.
Implementation Example
^^^^^^^^^^^^^^^^^^^^^^
The stellar-anchor-server_ implements many of these functions outlined above. For
reference examples, see the github repository.

Form CSS
^^^^^^^^
Expand All @@ -127,3 +75,10 @@ The `attrs` parameter adds a HTML attribute to the `<input>` tag that Bulma
uses to add better styling. You may also add more Bulma-supported attributes
to Polaris forms.

Registering Integrations
------------------------
In order for Polaris to use the integration classes you've defined, you
must register them.

.. autofunction:: polaris.integrations.register_integrations

9 changes: 3 additions & 6 deletions polaris/polaris/integrations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ def register_integrations(deposit: DepositIntegration = None,
"""
Registers instances of user-defined subclasses of
:class:`.WithdrawalIntegration` and
:class:`.DepositIntegration` with Polaris. Each subclasses' `.form`
attribute must be a subclass of
:class:`polaris.integration.forms.TransactionForm`.
:class:`.DepositIntegration` with Polaris.
Call this function in the relevant Django AppConfig.ready() function:
::
Expand All @@ -26,13 +24,12 @@ class PolarisIntegrationApp(AppConfig):
def ready(self):
from polaris.integrations import register_integrations
from myapp.integrations.forms import MyDepositForm
from myapp.integrations import (MyDepositIntegration,
MyWithdrawalIntegration)
register_integrations(
deposit=MyDepositIntegration(MyDepositForm),
withdrawal=MyWithdrawalIntegration(MyWithdrawalForm)
deposit=MyDepositIntegration(),
withdrawal=MyWithdrawalIntegration()
)
These integration classes provide a structured interface for implementing
Expand Down
10 changes: 6 additions & 4 deletions polaris/polaris/integrations/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ def poll_pending_deposits(cls, pending_deposits: QuerySet
**OVERRIDE REQUIRED**
This function should poll the financial entity for the state of all
`pending_deposits` transactions and return the ones ready to be
executed on the Stellar network.
`pending_deposits` and return the ones ready to be executed on the
Stellar network.
For every transaction that is returned, Polaris will submit it to the
Stellar network. If a transaction was completed on the network, the
Expand All @@ -53,7 +53,7 @@ def poll_pending_deposits(cls, pending_deposits: QuerySet
memory usage.
:param pending_deposits: a django Queryset for pending Transactions
:return a list of Transaction database objects to execute
:return: a list of Transaction database objects to execute
"""
raise NotImplementedError(
"`poll_transactions` must be implemented to process deposits"
Expand Down Expand Up @@ -107,6 +107,8 @@ def form_for_transaction(cls, transaction: Transaction) -> Optional[Type[Transac
:param transaction: the :class:`Transaction` database object
:return: an uninitialized :class:`forms.Form` subclass. For transaction
information, return a :class:`polaris.integrations.TransactionForm`
subclass.
"""
if transaction.amount_in:
# we've collected transaction info
Expand All @@ -128,7 +130,7 @@ def after_form_validation(cls, form: forms.Form, transaction: Transaction):
There is no need to implement that yourself.
DO NOT update `transaction.status` here or in any other function for
that matter. This column is managed by Polaris and is expected to have
that matter. This column is managed by Polaris and is expected to have
particular values at different points in the flow.
If you need to store some data to determine which form to return next when
Expand Down

0 comments on commit 92278bc

Please sign in to comment.