import json
import logging
import requests
from parsons.etl import Table
from parsons.utilities import check_env
from parsons.utilities.api_connector import APIConnector
logger = logging.getLogger(__name__)
URI = "https://api.bloomerang.co/v2/"
URI_AUTH = "https://crm.bloomerang.co/authorize/"
[docs]
class Bloomerang(object):
"""
Instantiate Bloomerang class
`Args:`
api_key: str
The Bloomerang API key. Not required if the ``BLOOMERANG_API_KEY`` environmental
variable is set or if the OAuth2 authentication parameters ``client_id`` and
``client_secret`` are set.
client_id: str
The Bloomerang client ID for OAuth2 authentication. Not required if
the ``BLOOMERANG_CLIENT_ID`` env variable is set or if the ``api_key``
parameter is set. Note that the ``client_secret`` parameter must
also be set in order to use OAuth2 authentication.
client_secret: str
The Bloomerang client secret for OAuth2 authetication. Not required if
the ``BLOOMERANG_CLIENT_SECRET`` env variable is set or if the ``api_key``
parameter is set. Note that the ``client_id`` parameter must
also be set in order to use OAuth2 authentication.
"""
def __init__(self, api_key=None, client_id=None, client_secret=None):
self.api_key = check_env.check("BLOOMERANG_API_KEY", api_key, optional=True)
self.client_id = check_env.check("BLOOMERANG_CLIENT_ID", client_id, optional=True)
self.client_secret = check_env.check(
"BLOOMERANG_CLIENT_SECRET", client_secret, optional=True
)
self.uri = URI
self.uri_auth = URI_AUTH
self.conn = self._conn()
def _conn(self):
# Instantiate APIConnector with authentication credentials
headers = {"accept": "application/json", "Content-Type": "application/json"}
if self.api_key is not None:
logger.info("Using API key authentication.")
headers["X-API-KEY"] = f"{self.api_key}"
elif (self.client_id is not None) & (self.client_secret is not None):
logger.info("Using OAuth2 authentication.")
self._generate_authorization_code()
self._generate_access_token()
headers["Authorization"] = f"Bearer {self.access_token}"
else:
raise Exception("Missing authorization credentials.")
return APIConnector(uri=self.uri, headers=headers)
def _generate_authorization_code(self):
data = {"client_id": self.client_id, "response_type": "code"}
r = requests.post(url=self.uri_auth, json=data)
self.authorization_code = r.json().get("code", None)
def _generate_access_token(self):
data = {
"client_id": self.client_id,
"client_secret": self.client_secret,
"grant_type": "authorization_code",
"code": self.authorization_code,
}
r = requests.post(url=self.uri + "oauth/token", json=data)
self.access_token = r.json().get("access_token", None)
def _base_endpoint(self, endpoint, entity_id=None):
url = f"{self.uri}{endpoint}/"
if entity_id:
url = url + f"{entity_id}/"
return url
@staticmethod
def _base_pagination_params(page_number=1, page_size=50):
return {"skip": page_size * (page_number - 1), "take": min(page_size, 50)}
@staticmethod
def _base_ordering_params(order_by=None, order_direction=None):
params = {}
if order_by:
params["orderBy"] = order_by
if order_direction:
params["orderDirection"] = order_direction
return params
def _base_create(self, endpoint, entity_id=None, **kwargs):
return self.conn.post_request(
url=self._base_endpoint(endpoint, entity_id), json=json.dumps({**kwargs})
)
def _base_update(self, endpoint, entity_id=None, **kwargs):
return self.conn.put_request(
url=self._base_endpoint(endpoint, entity_id), json=json.dumps({**kwargs})
)
def _base_get(self, endpoint, entity_id=None, params=None):
return self.conn.get_request(url=self._base_endpoint(endpoint, entity_id), params=params)
def _base_delete(self, endpoint, entity_id=None):
return self.conn.delete_request(url=self._base_endpoint(endpoint, entity_id))
[docs]
def create_constituent(self, **kwargs):
"""
`Args:`
**kwargs:`
Fields to include, e.g., FirstName = 'Rachel'.
See the Bloomerang API docs for a full list of `fields <https://bloomerang.co/features/integrations/api/rest-api#/Constituents/post_constituent>`_.
"""
return self._base_create("constituent", **kwargs)
[docs]
def update_constituent(self, constituent_id, **kwargs):
"""
`Args:`
constituent_id: str or int
Constituent ID to update
**kwargs:`
Fields to update, e.g., FirstName = 'RJ'.
See the Bloomerang API docs for a full list of `fields <https://bloomerang.co/features/integrations/api/rest-api#/Constituents/put_constituent__id_>`_.
"""
return self._base_update("constituent", entity_id=constituent_id, **kwargs)
[docs]
def get_constituent(self, constituent_id):
"""
`Args:`
constituent_id: str or int
Constituent ID to get fields for
`Returns:`
A JSON of the entry or an error.
"""
return self._base_get("constituent", entity_id=constituent_id)
[docs]
def delete_constituent(self, constituent_id):
"""
`Args:`
constituent_id: str or int
Constituent ID to delete
"""
return self._base_delete("constituent", entity_id=constituent_id)
[docs]
def get_constituents(
self,
page_number=1,
page_size=50,
order_by=None,
order_direction=None,
last_modified=None,
):
"""
`Args:`
page_number: int
Number of the page to fetch
page_size: int
Number of records per page (maximum allowed is 50)
order_by: str
Sorts by ``Id``, ``CreatedDate``, or ``LastModifiedDate`` (default ``Id``).
order_direction: str
Sorts the order_by in ``Asc`` or ``Desc`` order.
last_modified: str
Filters to constituents last modified after the specified date (ISO-8601 format).
`Returns:`
A Table of the entries.
"""
params = self._base_pagination_params(page_number, page_size)
params.update(self._base_ordering_params(order_by, order_direction))
if last_modified:
params["lastModified"] = last_modified
response = self._base_get("constituents", params=params)
return Table(response["Results"])
[docs]
def create_transaction(self, **kwargs):
"""
`Args:`
**kwargs:`
Fields to include, e.g., CreditCardType = 'Visa'.
See the Bloomerang API docs for a full list of `fields <https://bloomerang.co/features/integrations/api/rest-api#/Transactions/post_transaction>`_.
"""
return self._base_create("transaction", **kwargs)
[docs]
def update_transaction(self, transaction_id, **kwargs):
"""
`Args:`
transaction_id: str or int
Transaction ID to update
**kwargs:`
Fields to update, e.g., CreditCardType = 'Visa'.
See the Bloomerang API docs for a full list of `fields <https://bloomerang.co/features/integrations/api/rest-api#/Transactions/put_transaction__id_>`_.
"""
return self._base_update("transaction", entity_id=transaction_id, **kwargs)
[docs]
def get_transaction(self, transaction_id):
"""
`Args:`
transaction_id: str or int
Transaction ID to get fields for
`Returns:`
A JSON of the entry or an error.
"""
return self._base_get("transaction", entity_id=transaction_id)
[docs]
def delete_transaction(self, transaction_id):
"""
`Args:`
transaction_id: str or int
Transaction ID to delete
"""
return self._base_delete("transaction", entity_id=transaction_id)
[docs]
def get_transactions(self, page_number=1, page_size=50, order_by=None, order_direction=None):
"""
`Args:`
page_number: int
Number of the page to fetch
page_size: int
Number of records per page (maximum allowed is 50)
order_by: str
Sorts by ``Date``, ``CreatedDate``, or ``LastModifiedDate`` (default ``Date``).
order_direction: str
Sorts the order_by in ``Asc`` or ``Desc`` order (default ``Desc``).
`Returns:`
A JSON of the entry or an error.
"""
params = self._base_pagination_params(page_number, page_size)
params.update(self._base_ordering_params(order_by, order_direction))
response = self._base_get("transactions", params=params)
return Table(response["Results"])
[docs]
def get_transaction_designation(self, designation_id):
"""
`Args:`
designation_id: str or int
Transaction Designation ID to get fields for
`Returns:`
A JSON of the entry or an error.
"""
return self._base_get("transaction/designation", entity_id=designation_id)
[docs]
def get_transaction_designations(
self, page_number=1, page_size=50, order_by=None, order_direction=None
):
"""
`Args:`
page_number: int
Number of the page to fetch
page_size: int
Number of records per page (maximum allowed is 50)
order_by: str
Sorts by ``Date``, ``CreatedDate``, or ``LastModifiedDate`` (default ``Date``).
order_direction: str
Sorts the order_by in ``Asc`` or ``Desc`` order (default ``Desc``).
`Returns:`
A JSON of the entry or an error.
"""
params = self._base_pagination_params(page_number, page_size)
params.update(self._base_ordering_params(order_by, order_direction))
response = self._base_get("transactions/designations", params=params)
return Table(response["Results"])
[docs]
def create_interaction(self, **kwargs):
"""
`Args:`
**kwargs:`
Fields to include, e.g., Channel = "Email".
See the Bloomerang API docs for a full list of `fields <https://bloomerang.co/features/integrations/api/rest-api#/Interactions/post_interaction>`_.
"""
return self._base_create("interaction", **kwargs)
[docs]
def update_interaction(self, interaction_id, **kwargs):
"""
`Args:`
interaction_id: str or int
Interaction ID to update
**kwargs:`
Fields to update, e.g., EmailAddress = "user@example.com".
See the Bloomerang API docs for a full list of `fields <https://bloomerang.co/features/integrations/api/rest-api#/Interactions/put_interaction__id_>`_.
"""
return self._base_update("interaction", entity_id=interaction_id, **kwargs)
[docs]
def get_interaction(self, interaction_id):
"""
`Args:`
interaction_id: str or int
Interaction ID to get fields for
`Returns:`
A JSON of the entry or an error.
"""
return self._base_get("interaction", entity_id=interaction_id)
[docs]
def delete_interaction(self, interaction_id):
"""
`Args:`
interaction_id: str or int
Interaction ID to delete
"""
return self._base_delete("interaction", entity_id=interaction_id)
[docs]
def get_interactions(self, page_number=1, page_size=50):
"""
`Args:`
page_number: int
Number of the page to fetch
page_size: int
Number of records per page (maximum allowed is 50)
`Returns:`
A JSON of the entry or an error.
"""
params = self._base_pagination_params(page_number, page_size)
response = self._base_get("interactions", params=params)
return Table(response["Results"])