From 90d01e80cd89b01345ecfcfe7514cc8757daec95 Mon Sep 17 00:00:00 2001 From: Joe Sheldon Date: Tue, 11 Oct 2022 11:35:02 -0700 Subject: [PATCH 01/17] ran black, pre-commit to requirements.txt --- .pre-commit-config.yaml | 6 + requirements.txt | 1 + src/blueink/__init__.py | 3 +- src/blueink/bundle_helper.py | 159 +++++++++++++++--------- src/blueink/client.py | 13 +- src/blueink/model/bundles.py | 67 ++++------ src/blueink/model/persons.py | 6 +- src/blueink/paginator.py | 9 +- src/blueink/request_helper.py | 15 ++- src/blueink/subclients/bundle.py | 75 ++++++----- src/blueink/subclients/person.py | 34 ++--- src/blueink/subclients/subclient.py | 8 +- src/blueink/subclients/template.py | 23 ++-- src/blueink/subclients/webhook.py | 11 +- src/blueink/test.py | 22 ++-- src/blueink/tests/test_bundle_helper.py | 5 +- src/blueink/tests/test_client.py | 110 ++++++++-------- src/blueink/tests/test_person_helper.py | 16 +-- src/blueink/utils/testcase.py | 1 - 19 files changed, 335 insertions(+), 249 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..5ed79b8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,6 @@ +repos: + - repo: https://github.com/psf/black + rev: 22.10.0 + hooks: + - id: black + language_version: python3.9 diff --git a/requirements.txt b/requirements.txt index 59833df..eff09b8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ munch~=2.5 requests~=2.27 pydantic~=1.9 email-validator~=1.2 +pre-commit~=2.20 diff --git a/src/blueink/__init__.py b/src/blueink/__init__.py index 22446fe..bffa4c0 100644 --- a/src/blueink/__init__.py +++ b/src/blueink/__init__.py @@ -4,4 +4,5 @@ from .bundle_helper import BundleHelper from .person_helper import PersonHelper from . import constants -__all__ = ['Client', 'BundleHelper', 'PersonHelper', 'exceptions', 'constants'] + +__all__ = ["Client", "BundleHelper", "PersonHelper", "exceptions", "constants"] diff --git a/src/blueink/bundle_helper.py b/src/blueink/bundle_helper.py index 5124602..0327d95 100644 --- a/src/blueink/bundle_helper.py +++ b/src/blueink/bundle_helper.py @@ -72,7 +72,7 @@ def add_document_by_url(self, url: str, **additional_data) -> str: def add_document_by_path(self, file_path: str, **additional_data) -> str: filename = basename(file_path) - with open(file_path, 'rb') as file: + with open(file_path, "rb") as file: b64str = b64encode(file.read()).decode("utf-8") return self.add_document_by_b64(filename, b64str, **additional_data) @@ -80,9 +80,9 @@ def add_document_by_path(self, file_path: str, **additional_data) -> str: def add_document_by_b64(self, filename: str, b64str: str, **additional_data): file_index = len(self.files) - document = Document.create(filename=filename, - file_b64=b64str, - **additional_data) + document = Document.create( + filename=filename, file_b64=b64str, **additional_data + ) print(f"doc -- {document.key}") self._documents[document.key] = document return document.key @@ -102,10 +102,13 @@ def add_document_by_b64(self, filename: str, b64str: str, **additional_data): # file = io.BufferedReader(bytes, len(byte_array)) # return self.add_document_by_file(file, file_name, **additional_data) - def add_document_template(self, template_id: str, - assignments: dict, - initial_field_values: dict, - **additional_data) -> str: + def add_document_template( + self, + template_id: str, + assignments: dict, + initial_field_values: dict, + **additional_data, + ) -> str: """Create and add a template reference Args: @@ -119,7 +122,8 @@ def add_document_template(self, template_id: str, if template_id in self._documents.keys(): raise RuntimeError( - f'Document/Template with id {template_id} already added.') + f"Document/Template with id {template_id} already added." + ) assigns = [] for role, signer in assignments.items(): @@ -128,23 +132,38 @@ def add_document_template(self, template_id: str, vals = [] for field_key, init_val in initial_field_values.items(): - fieldval = TemplateRefFieldValue.create(key=field_key, - initial_value=init_val) + fieldval = TemplateRefFieldValue.create( + key=field_key, initial_value=init_val + ) vals.append(fieldval) - template = TemplateRef.create(template_id=template_id, - assignments=assigns, - field_values=vals, - **additional_data) + template = TemplateRef.create( + template_id=template_id, + assignments=assigns, + field_values=vals, + **additional_data, + ) self._documents[template.key] = template return template.key - def add_field(self, document_key: str, x: int, y: int, w: int, h: int, p: int, - kind: str, - editors: List[str] = None, label: str = None, v_pattern: str = None, - v_min: int = None, - v_max: int = None, key=None, **additional_data): + def add_field( + self, + document_key: str, + x: int, + y: int, + w: int, + h: int, + p: int, + kind: str, + editors: List[str] = None, + label: str = None, + v_pattern: str = None, + v_min: int = None, + v_max: int = None, + key=None, + **additional_data, + ): """Create and add a field to a particular document. Args @@ -170,9 +189,18 @@ def add_field(self, document_key: str, x: int, y: int, w: int, h: int, p: int, raise RuntimeError(f"No document found with key {document_key}!") field = Field.create( - x, y, w, h, p, kind, - label=label, v_pattern=v_pattern, v_min=v_min, v_max=v_max, key=key, - **additional_data + x, + y, + w, + h, + p, + kind, + label=label, + v_pattern=v_pattern, + v_min=v_min, + v_max=v_max, + key=key, + **additional_data, ) for packet_key in editors: field.add_editor(packet_key) @@ -180,11 +208,20 @@ def add_field(self, document_key: str, x: int, y: int, w: int, h: int, p: int, self._documents[document_key].add_field(field) return field.key - def add_signer(self, name: str, email: str = None, phone: str = None, - deliver_via: str = None, - person_id=None, auth_sms: bool = False, auth_selfie: bool = False, - auth_id: bool = False, - order: int = None, key=None, **additional_data): + def add_signer( + self, + name: str, + email: str = None, + phone: str = None, + deliver_via: str = None, + person_id=None, + auth_sms: bool = False, + auth_selfie: bool = False, + auth_id: bool = False, + order: int = None, + key=None, + **additional_data, + ): """Create and add a signer. With at least an email xor phone number. Args: @@ -204,24 +241,27 @@ def add_signer(self, name: str, email: str = None, phone: str = None, Packet key """ if phone is None and email is None: - raise ValidationError('Packet must have either an email or phone number') - - packet = Packet.create(name=name, - person_id=person_id, - email=email, - phone=phone, - auth_sms=auth_sms, - auth_selfie=auth_selfie, - auth_id=auth_id, - deliver_via=deliver_via, - order=order, - key=key, - **additional_data) + raise ValidationError("Packet must have either an email or phone number") + + packet = Packet.create( + name=name, + person_id=person_id, + email=email, + phone=phone, + auth_sms=auth_sms, + auth_selfie=auth_selfie, + auth_id=auth_id, + deliver_via=deliver_via, + order=order, + key=key, + **additional_data, + ) self._packets[packet.key] = packet return packet.key - def assign_role(self, document_key: str, signer_key: str, role: str, - **additional_data): + def assign_role( + self, document_key: str, signer_key: str, role: str, **additional_data + ): """Assign a signer to a particular role in a template Args: @@ -234,10 +274,12 @@ def assign_role(self, document_key: str, signer_key: str, role: str, raise RuntimeError(f"No document found with key {document_key}!") if type(self._documents[document_key]) is not TemplateRef: raise RuntimeError( - f"Document found with key {document_key} is not a Template!") + f"Document found with key {document_key} is not a Template!" + ) if signer_key not in self._packets: raise RuntimeError( - f"Signer {signer_key} does not have a corresponding packet") + f"Signer {signer_key} does not have a corresponding packet" + ) assignment = TemplateRefAssignment.create(role, signer_key, **additional_data) self._documents[document_key].add_assignment(assignment) @@ -255,7 +297,8 @@ def set_value(self, document_key: str, key: str, value: str, **additional_data): raise RuntimeError(f"No document found with key {document_key}!") if type(self._documents[document_key]) is not TemplateRef: raise RuntimeError( - f"Document found with key {document_key} is not a Template!") + f"Document found with key {document_key} is not a Template!" + ) field_val = TemplateRefFieldValue.create(key, value, **additional_data) self._documents[document_key].field_values.append(field_val) @@ -270,17 +313,19 @@ def _compile_bundle(self, **additional_data) -> Bundle: """ packets = list(self._packets.values()) documents = list(self._documents.values()) - bundle_out = Bundle.create(packets, - documents, - label=self._label, - in_order=self._in_order, - email_subject=self._email_subj, - email_message=self._email_msg, - is_test=self._is_test, - cc_emails=self._cc_emails, - custom_key=self._custom_key, - team=self._team, - **additional_data) + bundle_out = Bundle.create( + packets, + documents, + label=self._label, + in_order=self._in_order, + email_subject=self._email_subj, + email_message=self._email_msg, + is_test=self._is_test, + cc_emails=self._cc_emails, + custom_key=self._custom_key, + team=self._team, + **additional_data, + ) return bundle_out def as_data(self, **additional_data): diff --git a/src/blueink/client.py b/src/blueink/client.py index 88dbea0..528b55f 100644 --- a/src/blueink/client.py +++ b/src/blueink/client.py @@ -3,19 +3,24 @@ from src.blueink.constants import ( DEFAULT_BASE_URL, ENV_BLUEINK_API_URL, - ENV_BLUEINK_PRIVATE_API_KEY + ENV_BLUEINK_PRIVATE_API_KEY, ) from src.blueink.request_helper import RequestHelper from src.blueink.subclients.bundle import BundleSubClient from src.blueink.subclients.packet import PacketSubClient from src.blueink.subclients.person import PersonSubClient from src.blueink.subclients.template import TemplateSubClient + # from src.blueink.subclients.webhook import WebhookSubClient class Client: - def __init__(self, private_api_key: str = None, base_url: str = None, - raise_exceptions: bool = True): + def __init__( + self, + private_api_key: str = None, + base_url: str = None, + raise_exceptions: bool = True, + ): """Initialize a Client instance to access the Blueink eSignature API Args: @@ -58,5 +63,3 @@ def __init__(self, private_api_key: str = None, base_url: str = None, self.packets = PacketSubClient(base_url, self._request_helper) self.templates = TemplateSubClient(base_url, self._request_helper) # self.webhooks = WebhookSubClient(base_url, self._request_helper) - - diff --git a/src/blueink/model/bundles.py b/src/blueink/model/bundles.py index e4c75bd..029cfb3 100644 --- a/src/blueink/model/bundles.py +++ b/src/blueink/model/bundles.py @@ -12,8 +12,8 @@ def __init__(self, error_text: str): def generate_key(type, length=5): - slug = ''.join(random.choice(string.ascii_letters) for i in range(length)) - return f'{type}_{slug}' + slug = "".join(random.choice(string.ascii_letters) for i in range(length)) + return f"{type}_{slug}" class Field(BaseModel): @@ -31,25 +31,20 @@ class Field(BaseModel): editors: Optional[List[str]] class Config: - extra = 'allow' + extra = "allow" @classmethod def create(cls, x, y, w, h, page, kind, key=None, **kwargs): if not key: - key = generate_key('field', 5) - obj = Field(key=key, - x=x, - y=y, - w=w, - h=h, - page=page, - kind=kind, - **kwargs) + key = generate_key("field", 5) + obj = Field(key=key, x=x, y=y, w=w, h=h, page=page, kind=kind, **kwargs) return obj - @validator('kind') + @validator("kind") def kind_is_allowed(cls, v): - assert v in FIELD_KIND.values(), f'Field Kind \'{v}\' not allowed. Must be one of {FIELD_KIND.values()}' + assert ( + v in FIELD_KIND.values() + ), f"Field Kind '{v}' not allowed. Must be one of {FIELD_KIND.values()}" return v def add_editor(self, editor: str): @@ -71,22 +66,22 @@ class Packet(BaseModel): order: Optional[str] class Config: - extra = 'allow' + extra = "allow" - @validator('deliver_via') + @validator("deliver_via") def deliver_via_is_allowed(cls, v): if v is not None: - assert v in DELIVER_VIA.values(), f'deliver_via \'{v}\' not allowed. Must be None' \ - f' or one of {DELIVER_VIA.values()}' + assert v in DELIVER_VIA.values(), ( + f"deliver_via '{v}' not allowed. Must be None" + f" or one of {DELIVER_VIA.values()}" + ) return v @classmethod def create(cls, name, key=None, **kwargs): if not key: - key = generate_key('packet', 5) - obj = Packet(key=key, - name=name, - **kwargs) + key = generate_key("packet", 5) + obj = Packet(key=key, name=name, **kwargs) return obj @@ -95,13 +90,11 @@ class TemplateRefAssignment(BaseModel): signer: str = ... class Config: - extra = 'allow' + extra = "allow" @classmethod def create(cls, role, signer, **kwargs): - obj = TemplateRefAssignment(role=role, - signer=signer, - **kwargs) + obj = TemplateRefAssignment(role=role, signer=signer, **kwargs) return obj @@ -110,13 +103,11 @@ class TemplateRefFieldValue(BaseModel): initial_value: str = ... class Config: - extra = 'allow' + extra = "allow" @classmethod def create(cls, key, initial_value, **kwargs): - obj = TemplateRefFieldValue(key=key, - initial_value=initial_value, - **kwargs) + obj = TemplateRefFieldValue(key=key, initial_value=initial_value, **kwargs) return obj @@ -126,12 +117,12 @@ class TemplateRef(BaseModel): field_values: Optional[List[TemplateRefFieldValue]] class Config: - extra = 'allow' + extra = "allow" @classmethod def create(cls, key=None, **kwargs): if not key: - key = generate_key('tmpl', 5) + key = generate_key("tmpl", 5) obj = TemplateRef(key=key, **kwargs) return obj @@ -157,12 +148,12 @@ class Document(BaseModel): fields: Optional[List[Field]] class Config: - extra = 'allow' + extra = "allow" @classmethod def create(cls, key=None, **kwargs): if not key: - key = generate_key('doc', 5) + key = generate_key("doc", 5) obj = Document(key=key, **kwargs) return obj @@ -190,13 +181,11 @@ class Bundle(BaseModel): team: Optional[str] class Config: - extra = 'allow' + extra = "allow" @classmethod def create(cls, packets: List[Packet], documents: List[Document], **kwargs): - obj = Bundle(packets=packets, - documents=documents, - **kwargs) + obj = Bundle(packets=packets, documents=documents, **kwargs) return obj def add_packet(self, packet: Packet): @@ -208,5 +197,3 @@ def add_document(self, document: Document): if self.documents is None: self.documents = [] self.documents.append(document) - - diff --git a/src/blueink/model/persons.py b/src/blueink/model/persons.py index 7b88a2a..bf4cb96 100644 --- a/src/blueink/model/persons.py +++ b/src/blueink/model/persons.py @@ -8,7 +8,7 @@ class ContactChannelSchema(BaseModel): kind: Optional[str] class Config: - extra = 'allow' + extra = "allow" class PersonSchema(BaseModel): @@ -17,6 +17,4 @@ class PersonSchema(BaseModel): channels: Optional[List[ContactChannelSchema]] class Config: - extra = 'allow' - - + extra = "allow" diff --git a/src/blueink/paginator.py b/src/blueink/paginator.py index 3e69ece..18dad2b 100644 --- a/src/blueink/paginator.py +++ b/src/blueink/paginator.py @@ -35,9 +35,11 @@ def __next__(self): raise StopIteration try: - api_response: NormalizedResponse = self._paged_func(page=self.next_page, - per_page=self._items_per_page, - **self._paged_func_args) + api_response: NormalizedResponse = self._paged_func( + page=self.next_page, + per_page=self._items_per_page, + **self._paged_func_args + ) except HTTPError: raise StopIteration @@ -49,4 +51,3 @@ def __next__(self): self.next_page = self.next_page + 1 return api_response - diff --git a/src/blueink/request_helper.py b/src/blueink/request_helper.py index a0866ce..6b54667 100644 --- a/src/blueink/request_helper.py +++ b/src/blueink/request_helper.py @@ -97,17 +97,26 @@ def _build_headers(self, content_type=None, more_headers=None): return hdrs def _make_request( - self, method, url, data=None, json=None, files=None, params=None, headers=None, content_type=None + self, + method, + url, + data=None, + json=None, + files=None, + params=None, + headers=None, + content_type=None, ): - response = requests.request( method, url, params=params, data=data, json=json, - headers=self._build_headers(content_type=content_type, more_headers=headers), + headers=self._build_headers( + content_type=content_type, more_headers=headers + ), files=files, ) diff --git a/src/blueink/subclients/bundle.py b/src/blueink/subclients/bundle.py index 03c9580..2b17dba 100644 --- a/src/blueink/subclients/bundle.py +++ b/src/blueink/subclients/bundle.py @@ -29,14 +29,22 @@ def _prepare_files(self, file_list: [io.BufferedReader]): f" (e.g. an open file handle)" ) field_name = f"files[{idx}]" - files_data.append((field_name, (file_dict.get("filename"), - fh, - file_dict.get("content_type")))) + files_data.append( + ( + field_name, + ( + file_dict.get("filename"), + fh, + file_dict.get("content_type"), + ), + ) + ) return files_data - def create(self, data: dict, - files: List[io.BufferedReader] = []) -> NormalizedResponse: - """ Post a Bundle to the BlueInk application. + def create( + self, data: dict, files: List[io.BufferedReader] = [] + ) -> NormalizedResponse: + """Post a Bundle to the BlueInk application. Args: data: raw data for Bundle, expressed as a python dict @@ -59,15 +67,14 @@ def create(self, data: dict, bundle_request_data = {"bundle_request": json.dumps(data)} - response = self._requests.post(url, - data=bundle_request_data, - files=files_data) + response = self._requests.post( + url, data=bundle_request_data, files=files_data + ) return response - def create_from_bundle_helper(self, - bdl_helper: BundleHelper) -> NormalizedResponse: - """ Post a Bundle to the BlueInk application. + def create_from_bundle_helper(self, bdl_helper: BundleHelper) -> NormalizedResponse: + """Post a Bundle to the BlueInk application. Provided as a convenience to simplify posting of a Bundle. This is the recommended way to create a Bundle. @@ -83,8 +90,13 @@ def create_from_bundle_helper(self, files = bdl_helper.files return self.create(data=data, files=files) - def paged_list(self, page: int = 1, per_page: int = 50, - related_data: bool = False, **query_params) -> PaginatedIterator: + def paged_list( + self, + page: int = 1, + per_page: int = 50, + related_data: bool = False, + **query_params, + ) -> PaginatedIterator: """Returns an iterable object such that you may lazily fetch a number of Bundles @@ -101,16 +113,23 @@ def paged_list(self, page: int = 1, per_page: int = 50, Returns: PaginatedIterator object, compliant with python iterables """ - iterator = PaginatedIterator(paged_api_function=self.list, - page=page, - per_page=per_page, - related_data=related_data, - **query_params) + iterator = PaginatedIterator( + paged_api_function=self.list, + page=page, + per_page=per_page, + related_data=related_data, + **query_params, + ) return iterator - def list(self, page: int = None, per_page: int = None, - related_data: bool = False, **query_params) -> NormalizedResponse: - """ Returns a list of bundles + def list( + self, + page: int = None, + per_page: int = None, + related_data: bool = False, + **query_params, + ) -> NormalizedResponse: + """Returns a list of bundles Args: page: which page to fetch @@ -122,10 +141,9 @@ def list(self, page: int = None, per_page: int = None, NormalizedResponse object """ url = self.build_url(endpoints.BUNDLES.LIST) - response = self._requests.get(url, - params=self.build_params(page, - per_page, - **query_params)) + response = self._requests.get( + url, params=self.build_params(page, per_page, **query_params) + ) if related_data: for bundle in response.data: @@ -148,8 +166,9 @@ def _attach_additional_data(self, bundle): data_response = self.list_data(bundle_id) bundle.data = data_response.data - def retrieve(self, bundle_id: str, - related_data: bool = False) -> NormalizedResponse: + def retrieve( + self, bundle_id: str, related_data: bool = False + ) -> NormalizedResponse: """Request a single bundle Args: diff --git a/src/blueink/subclients/person.py b/src/blueink/subclients/person.py index cd99477..2904235 100644 --- a/src/blueink/subclients/person.py +++ b/src/blueink/subclients/person.py @@ -24,8 +24,9 @@ def create(self, data: dict, **kwargs) -> NormalizedResponse: url = self.build_url(endpoints.PERSONS.CREATE) return self._requests.post(url, json=data) - def create_from_person_helper(self, person_helper: PersonHelper, - **kwargs) -> NormalizedResponse: + def create_from_person_helper( + self, person_helper: PersonHelper, **kwargs + ) -> NormalizedResponse: """Create a person using PersonHelper convenience object Args: @@ -36,8 +37,9 @@ def create_from_person_helper(self, person_helper: PersonHelper, """ return self.create(person_helper.as_dict(**kwargs)) - def paged_list(self, page: int = 1, per_page: int = 50, - **query_params) -> PaginatedIterator: + def paged_list( + self, page: int = 1, per_page: int = 50, **query_params + ) -> PaginatedIterator: """Return an iterable object such that you may lazily fetch a number of Persons (signers) @@ -54,14 +56,14 @@ def paged_list(self, page: int = 1, per_page: int = 50, PaginatedIterator object """ - iterator = PaginatedIterator(paged_api_function=self.list, - page=page, - per_page=per_page, - **query_params) + iterator = PaginatedIterator( + paged_api_function=self.list, page=page, per_page=per_page, **query_params + ) return iterator - def list(self, page: int = None, per_page: int = None, - **query_params) -> NormalizedResponse: + def list( + self, page: int = None, per_page: int = None, **query_params + ) -> NormalizedResponse: """Return a list of persons (signers). Args: @@ -73,10 +75,9 @@ def list(self, page: int = None, per_page: int = None, NormalizedResponse object """ url = self.build_url(endpoints.PERSONS.LIST) - return self._requests.get(url, - params=self.build_params(page, - per_page, - **query_params)) + return self._requests.get( + url, params=self.build_params(page, per_page, **query_params) + ) def retrieve(self, person_id: str) -> NormalizedResponse: """Retrieve details on a singular person @@ -90,8 +91,9 @@ def retrieve(self, person_id: str) -> NormalizedResponse: url = self.build_url(endpoints.PERSONS.RETRIEVE, person_id=person_id) return self._requests.get(url) - def update(self, person_id: str, data: dict, - partial: bool = False) -> NormalizedResponse: + def update( + self, person_id: str, data: dict, partial: bool = False + ) -> NormalizedResponse: """Update a Person (signer)'s record Args: diff --git a/src/blueink/subclients/subclient.py b/src/blueink/subclients/subclient.py index 8a5f090..ed67650 100644 --- a/src/blueink/subclients/subclient.py +++ b/src/blueink/subclients/subclient.py @@ -25,14 +25,16 @@ def build_url(self, endpoint: str, **kwargs): """ # All of our current endpoints take 1 parameter, max if len(kwargs) > 1: - raise ValueError('Only one interpolation parameter is allowed') + raise ValueError("Only one interpolation parameter is allowed") try: url = endpoints.URLBuilder(self._base_url, endpoint).build(**kwargs) except KeyError: arg_name = list(kwargs.keys())[0] - raise ValueError(f'Invalid substitution argument "{arg_name}"' - f' provided for endpoint "{endpoint}"') + raise ValueError( + f'Invalid substitution argument "{arg_name}"' + f' provided for endpoint "{endpoint}"' + ) return url diff --git a/src/blueink/subclients/template.py b/src/blueink/subclients/template.py index aca37b5..961d943 100644 --- a/src/blueink/subclients/template.py +++ b/src/blueink/subclients/template.py @@ -8,8 +8,9 @@ class TemplateSubClient(SubClient): def __init__(self, base_url, private_api_key): super().__init__(base_url, private_api_key) - def paged_list(self, page: int = 1, per_page: int = 50, - **query_params) -> PaginatedIterator: + def paged_list( + self, page: int = 1, per_page: int = 50, **query_params + ) -> PaginatedIterator: """return an iterable object containing a list of templates Typical Usage: @@ -24,14 +25,14 @@ def paged_list(self, page: int = 1, per_page: int = 50, Returns: PaginatedIterator object """ - iterator = PaginatedIterator(paged_api_function=self.list, - page=page, - per_page=per_page, - **query_params) + iterator = PaginatedIterator( + paged_api_function=self.list, page=page, per_page=per_page, **query_params + ) return iterator - def list(self, page: int = None, per_page: int = None, - **query_params) -> NormalizedResponse: + def list( + self, page: int = None, per_page: int = None, **query_params + ) -> NormalizedResponse: """Return a list of Templates. Args: @@ -43,9 +44,9 @@ def list(self, page: int = None, per_page: int = None, NormalizedResponse object """ url = self.build_url(endpoints.TEMPLATES.LIST) - return self._requests.get(url, params=self.build_params(page, - per_page, - **query_params)) + return self._requests.get( + url, params=self.build_params(page, per_page, **query_params) + ) def retrieve(self, template_id: str) -> NormalizedResponse: """Return a singular Template by id. diff --git a/src/blueink/subclients/webhook.py b/src/blueink/subclients/webhook.py index e1a9c59..12d6acd 100644 --- a/src/blueink/subclients/webhook.py +++ b/src/blueink/subclients/webhook.py @@ -11,9 +11,14 @@ def __init__(self, base_url, private_api_key): # ---------- # Webhooks # ---------- - def create_webhook(self, url: str, event_types:List[str], - extra_headers: List[WebhookExtraHeader], enabled: bool = True, - json: bool = True): + def create_webhook( + self, + url: str, + event_types: List[str], + extra_headers: List[WebhookExtraHeader], + enabled: bool = True, + json: bool = True, + ): raise RuntimeError("Not Implemented") def list_webhooks(self): diff --git a/src/blueink/test.py b/src/blueink/test.py index fe794fd..1b3cc07 100644 --- a/src/blueink/test.py +++ b/src/blueink/test.py @@ -1,15 +1,15 @@ - - def lilboi(a=1, b=1, **kwargs): - print('lb.a', a) - print('lb.b', b) - print('lb.kwargs', kwargs) + print("lb.a", a) + print("lb.b", b) + print("lb.kwargs", kwargs) + def bigboi(a=1, b=1, c=1, **kwargs): - print('bb.a', a) - print('bb.b', b) - print('bb.c', c) - print('bb.kwargs', kwargs) - lilboi(a,b,c=c,**kwargs) + print("bb.a", a) + print("bb.b", b) + print("bb.c", c) + print("bb.kwargs", kwargs) + lilboi(a, b, c=c, **kwargs) + -bigboi(2, 2, c=2) \ No newline at end of file +bigboi(2, 2, c=2) diff --git a/src/blueink/tests/test_bundle_helper.py b/src/blueink/tests/test_bundle_helper.py index 16ab3da..9c2dc35 100644 --- a/src/blueink/tests/test_bundle_helper.py +++ b/src/blueink/tests/test_bundle_helper.py @@ -56,7 +56,7 @@ class TestBundleHelper(TestCase): "p": 1, "kind": "inp", "editors": [], - "label": "MY_INPUT_01" + "label": "MY_INPUT_01", } FIELD_02_DATA = { @@ -67,7 +67,7 @@ class TestBundleHelper(TestCase): "p": 1, "kind": "inp", "editors": [], - "label": "MY_INPUT_02" + "label": "MY_INPUT_02", } DOCUMENT_01_URL = "https://www.example.com/example1.pdf" @@ -133,7 +133,6 @@ def test_adding_fields(self): field01_data = copy.deepcopy(self.FIELD_01_DATA) field02_data = copy.deepcopy(self.FIELD_02_DATA) - bh = BundleHelper(**input_data) doc01_key = bh.add_document_by_url(url01) signer01_key = bh.add_signer(**signer01_data) diff --git a/src/blueink/tests/test_client.py b/src/blueink/tests/test_client.py index 536fae7..0bada4d 100644 --- a/src/blueink/tests/test_client.py +++ b/src/blueink/tests/test_client.py @@ -68,10 +68,12 @@ def _create_test_bundle_helper(self, method: str) -> (BundleHelper, str, str): (BundleHelper, signerkey, fieldkey) """ - bh = BundleHelper(self._bundle_label(method), - self.EMAIL_SUBJECT, - self.EMAIL_MESSAGE, - is_test=True) + bh = BundleHelper( + self._bundle_label(method), + self.EMAIL_SUBJECT, + self.EMAIL_MESSAGE, + is_test=True, + ) # Add Document doc01_key = None @@ -80,34 +82,37 @@ def _create_test_bundle_helper(self, method: str) -> (BundleHelper, str, str): elif method == self.DOC_METHODS.URL: doc01_key = bh.add_document_by_url(self.REAL_DOCUMENT_URL) elif method == self.DOC_METHODS.B64: - file = open(self.REAL_DOCUMENT_PATH, 'rb') + file = open(self.REAL_DOCUMENT_PATH, "rb") filename = basename(self.REAL_DOCUMENT_PATH) b64str = b64encode(file.read()).decode("utf-8") file.close() doc01_key = bh.add_document_by_b64(filename, b64str) # Add Signer 1 - signer01_key = bh.add_signer(key=self.SIGNER01_KEY, - name=self.SIGNER01_NAME, - email=self.SIGNER01_EMAIL, - phone=self.SIGNER01_PHONE, - deliver_via=self.SIGNER01_DELIVERY) + signer01_key = bh.add_signer( + key=self.SIGNER01_KEY, + name=self.SIGNER01_NAME, + email=self.SIGNER01_EMAIL, + phone=self.SIGNER01_PHONE, + deliver_via=self.SIGNER01_DELIVERY, + ) # Add Field - field01_key = bh.add_field(document_key=doc01_key, - x=self.FIELD01_X, - y=self.FIELD01_Y, - w=self.FIELD01_W, - h=self.FIELD01_H, - p=self.FIELD01_P, - kind=self.FIELD01_KIND, - editors=self.FIELD01_EDITORS, - label=self.FIELD01_LABEL - ) - - self._check_bundle_data(bh.as_data(), - signerkey=signer01_key, - fieldkey=field01_key) + field01_key = bh.add_field( + document_key=doc01_key, + x=self.FIELD01_X, + y=self.FIELD01_Y, + w=self.FIELD01_W, + h=self.FIELD01_H, + p=self.FIELD01_P, + kind=self.FIELD01_KIND, + editors=self.FIELD01_EDITORS, + label=self.FIELD01_LABEL, + ) + + self._check_bundle_data( + bh.as_data(), signerkey=signer01_key, fieldkey=field01_key + ) return bh, signer01_key, field01_key @@ -135,9 +140,14 @@ def _check_bundle_data(self, compiled_bundle: dict, signerkey, fieldkey): self.assert_equal(compiled_bundle["packets"][0]["key"], signerkey) - def _poll_for_successful_file_processing(self, client: Client, bundle_id: str, - expected_document_count: int, - max_attempts=5, delay_between_seconds=5): + def _poll_for_successful_file_processing( + self, + client: Client, + bundle_id: str, + expected_document_count: int, + max_attempts=5, + delay_between_seconds=5, + ): attempt = 0 while attempt < max_attempts: attempt = attempt + 1 @@ -145,8 +155,10 @@ def _poll_for_successful_file_processing(self, client: Client, bundle_id: str, resp = client.bundles.retrieve(bundle_id) if resp.status != 200: - print(f"Failed to get bundle {bundle_id} on attempt {attempt}" - f" of {max_attempts}") + print( + f"Failed to get bundle {bundle_id} on attempt {attempt}" + f" of {max_attempts}" + ) continue ndocs = len(resp.data["documents"]) @@ -156,45 +168,39 @@ def _poll_for_successful_file_processing(self, client: Client, bundle_id: str, return False def test_roundtrip_url(self): - bh, sk, fk = self._create_test_bundle_helper( - method=self.DOC_METHODS.URL - ) + bh, sk, fk = self._create_test_bundle_helper(method=self.DOC_METHODS.URL) client = Client(raise_exceptions=False) resp = client.bundles.create_from_bundle_helper(bh) self.assert_equal(resp.status, 201) - has_all_docs = self._poll_for_successful_file_processing(client, - resp.data.id, - 1) + has_all_docs = self._poll_for_successful_file_processing( + client, resp.data.id, 1 + ) self.assert_true(has_all_docs) def test_roundtrip_b64(self): - bh, sk, fk = self._create_test_bundle_helper( - method=self.DOC_METHODS.B64 - ) + bh, sk, fk = self._create_test_bundle_helper(method=self.DOC_METHODS.B64) client = Client(raise_exceptions=False) resp = client.bundles.create_from_bundle_helper(bh) self.assert_equal(resp.status, 201) - has_all_docs = self._poll_for_successful_file_processing(client, - resp.data.id, - 1) + has_all_docs = self._poll_for_successful_file_processing( + client, resp.data.id, 1 + ) self.assert_true(has_all_docs) def test_roundtrip_path(self): - bh, sk, fk = self._create_test_bundle_helper( - method=self.DOC_METHODS.PATH - ) + bh, sk, fk = self._create_test_bundle_helper(method=self.DOC_METHODS.PATH) client = Client(raise_exceptions=False) resp = client.bundles.create_from_bundle_helper(bh) self.assert_equal(resp.status, 201) - has_all_docs = self._poll_for_successful_file_processing(client, - resp.data.id, - 1) + has_all_docs = self._poll_for_successful_file_processing( + client, resp.data.id, 1 + ) self.assert_true(has_all_docs) # def test_roundtrip_template(self): @@ -231,10 +237,12 @@ def test_roundtrip_path(self): # self.assert_true(has_all_docs) def test_person_create(self): - ph = PersonHelper(name=self.PERSON_NAME, - metadata=self.PERSON_METADATA, - phones=self.PERSON_PHONES, - emails=self.PERSON_EMAILS) + ph = PersonHelper( + name=self.PERSON_NAME, + metadata=self.PERSON_METADATA, + phones=self.PERSON_PHONES, + emails=self.PERSON_EMAILS, + ) client = Client(raise_exceptions=False) resp = client.persons.create_from_person_helper(ph) diff --git a/src/blueink/tests/test_person_helper.py b/src/blueink/tests/test_person_helper.py index 5cf0309..1ac1329 100644 --- a/src/blueink/tests/test_person_helper.py +++ b/src/blueink/tests/test_person_helper.py @@ -3,7 +3,7 @@ class TestPersonHelper(TestCase): - NAME="JOHN DOE" + NAME = "JOHN DOE" MD_KEY01 = "KEY01" MD_KEY02 = "KEY02" @@ -17,7 +17,7 @@ class TestPersonHelper(TestCase): PHONES = ["505 555 5555"] EMAILS = ["johndoe@example.com"] - def _check_values(self, data:dict): + def _check_values(self, data: dict): self.assert_equal(data["name"], self.NAME) self.assert_in("channels", data) @@ -38,7 +38,6 @@ def _check_values(self, data:dict): self.assert_true(has_email) self.assert_true(has_phone) - self.assert_in("metadata", data) self.assert_len(data["metadata"], 2) self.assert_in(self.MD_KEY01, data["metadata"]) @@ -56,10 +55,11 @@ def test_atomic(self): self._check_values(ph.as_dict()) def test_all_in_one(self): - ph = PersonHelper(name=self.NAME, - metadata=self.METADATA, - phones=self.PHONES, - emails=self.EMAILS) + ph = PersonHelper( + name=self.NAME, + metadata=self.METADATA, + phones=self.PHONES, + emails=self.EMAILS, + ) self._check_values(ph.as_dict()) - diff --git a/src/blueink/utils/testcase.py b/src/blueink/utils/testcase.py index 93d01a9..a8cd04b 100644 --- a/src/blueink/utils/testcase.py +++ b/src/blueink/utils/testcase.py @@ -1,5 +1,4 @@ class TestCase: - def assert_true(self, val): if val is None: assert False, f"{val} is None, not True" From a6a77d5cecd17b8fa20d2ad4e699583788424263 Mon Sep 17 00:00:00 2001 From: Joe Sheldon Date: Tue, 11 Oct 2022 11:37:01 -0700 Subject: [PATCH 02/17] ran black, pre-commit to requirements.txt --- .pre-commit-config.yaml | 6 + requirements.txt | 1 + src/blueink/__init__.py | 3 +- src/blueink/bundle_helper.py | 156 ++++++++++++++++-------- src/blueink/client.py | 12 +- src/blueink/model/bundles.py | 67 ++++------ src/blueink/model/persons.py | 6 +- src/blueink/model/webhook.py | 10 +- src/blueink/paginator.py | 9 +- src/blueink/request_helper.py | 15 ++- src/blueink/subclients/bundle.py | 88 ++++++++----- src/blueink/subclients/person.py | 34 +++--- src/blueink/subclients/subclient.py | 8 +- src/blueink/subclients/template.py | 23 ++-- src/blueink/subclients/webhook.py | 55 +++++---- src/blueink/tests/test_bundle_helper.py | 5 +- src/blueink/tests/test_client.py | 89 +++++++------- src/blueink/tests/test_person_helper.py | 16 +-- src/blueink/utils/testcase.py | 1 - 19 files changed, 343 insertions(+), 261 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..5ed79b8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,6 @@ +repos: + - repo: https://github.com/psf/black + rev: 22.10.0 + hooks: + - id: black + language_version: python3.9 diff --git a/requirements.txt b/requirements.txt index 59833df..eff09b8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ munch~=2.5 requests~=2.27 pydantic~=1.9 email-validator~=1.2 +pre-commit~=2.20 diff --git a/src/blueink/__init__.py b/src/blueink/__init__.py index 22446fe..bffa4c0 100644 --- a/src/blueink/__init__.py +++ b/src/blueink/__init__.py @@ -4,4 +4,5 @@ from .bundle_helper import BundleHelper from .person_helper import PersonHelper from . import constants -__all__ = ['Client', 'BundleHelper', 'PersonHelper', 'exceptions', 'constants'] + +__all__ = ["Client", "BundleHelper", "PersonHelper", "exceptions", "constants"] diff --git a/src/blueink/bundle_helper.py b/src/blueink/bundle_helper.py index 5194cf9..962de7c 100644 --- a/src/blueink/bundle_helper.py +++ b/src/blueink/bundle_helper.py @@ -17,14 +17,14 @@ class BundleHelper: def __init__( - self, - label: str = None, - email_subject: str = None, - email_message: str = None, - in_order: bool = False, - is_test: bool = False, - custom_key: str = None, - team: str = None, + self, + label: str = None, + email_subject: str = None, + email_message: str = None, + in_order: bool = False, + is_test: bool = False, + custom_key: str = None, + team: str = None, ): """Helper class to aid building a Bundle. @@ -72,32 +72,32 @@ def add_document_by_url(self, url: str, **additional_data) -> str: def add_document_by_path(self, file_path: str, **additional_data) -> str: filename = basename(file_path) - file = open(file_path, 'rb') + file = open(file_path, "rb") b64str = b64encode(file.read()) file.close() return self.add_document_by_b64(filename, b64str, **additional_data) - def add_document_by_b64(self, filename:str, b64str:str, **additional_data): + def add_document_by_b64(self, filename: str, b64str: str, **additional_data): file_index = len(self.files) - - self.files.append({'file_b64': b64str, "filename": filename}) + self.files.append({"file_b64": b64str, "filename": filename}) document = Document.create(file_index=file_index, **additional_data) print(f"doc -- {document.key}") self._documents[document.key] = document return document.key - def add_document_by_bytearray(self, byte_array: bytearray, file_name: str, - **additional_data) -> str: - ''' + def add_document_by_bytearray( + self, byte_array: bytearray, file_name: str, **additional_data + ) -> str: + """ Add a document via url, with unique key. :param byte_array: :param file_name: :param mime_type: :param additional_data: Optional and will append any additional kwargs to the json of the document :return: - ''' + """ bytes = io.BytesIO(byte_array) file = io.BufferedReader(bytes, len(byte_array)) @@ -111,15 +111,31 @@ def add_document_template(self, template_id: str, **additional_data) -> str: :return:Template object """ if template_id in self._documents.keys(): - raise RuntimeError(f'Document/Template with id {template_id} already added.') + raise RuntimeError( + f"Document/Template with id {template_id} already added." + ) template = TemplateRef.create(template_id=template_id, **additional_data) self._documents[template.key] = template return template.key - def add_field(self, document_key: str, x: int, y: int, w: int, h: int, p: int, kind: str, - editors: List[str] = None, label: str = None, v_pattern: str = None, v_min: int = None, - v_max: int = None, key=None, **additional_data): + def add_field( + self, + document_key: str, + x: int, + y: int, + w: int, + h: int, + p: int, + kind: str, + editors: List[str] = None, + label: str = None, + v_pattern: str = None, + v_min: int = None, + v_max: int = None, + key=None, + **additional_data, + ): """Create and add a field to a particular document. Args @@ -145,9 +161,18 @@ def add_field(self, document_key: str, x: int, y: int, w: int, h: int, p: int, k raise RuntimeError(f"No document found with key {document_key}!") field = Field.create( - x, y, w, h, p, kind, - label=label, v_pattern=v_pattern, v_min=v_min, v_max=v_max, key=key, - **additional_data + x, + y, + w, + h, + p, + kind, + label=label, + v_pattern=v_pattern, + v_min=v_min, + v_max=v_max, + key=key, + **additional_data, ) for packet_key in editors: field.add_editor(packet_key) @@ -155,9 +180,20 @@ def add_field(self, document_key: str, x: int, y: int, w: int, h: int, p: int, k self._documents[document_key].add_field(field) return field.key - def add_signer(self, name: str, email: str = None, phone: str = None, deliver_via: str = None, - person_id=None, auth_sms: bool = False, auth_selfie: bool = False, auth_id: bool = False, - order: int = None, key=None, **additional_data): + def add_signer( + self, + name: str, + email: str = None, + phone: str = None, + deliver_via: str = None, + person_id=None, + auth_sms: bool = False, + auth_selfie: bool = False, + auth_id: bool = False, + order: int = None, + key=None, + **additional_data, + ): """Create and add a signer. With at least an email xor phone number. Args: @@ -177,23 +213,27 @@ def add_signer(self, name: str, email: str = None, phone: str = None, deliver_vi Packet key """ if phone is None and email is None: - raise ValidationError('Packet must have either an email or phone number') - - packet = Packet.create(name=name, - person_id=person_id, - email=email, - phone=phone, - auth_sms=auth_sms, - auth_selfie=auth_selfie, - auth_id=auth_id, - deliver_via=deliver_via, - order=order, - key=key, - **additional_data) + raise ValidationError("Packet must have either an email or phone number") + + packet = Packet.create( + name=name, + person_id=person_id, + email=email, + phone=phone, + auth_sms=auth_sms, + auth_selfie=auth_selfie, + auth_id=auth_id, + deliver_via=deliver_via, + order=order, + key=key, + **additional_data, + ) self._packets[packet.key] = packet return packet.key - def assign_role(self, document_key: str, signer_key: str, role: str, **additional_data): + def assign_role( + self, document_key: str, signer_key: str, role: str, **additional_data + ): """Assign a signer to a particular role in a template Args: @@ -205,9 +245,13 @@ def assign_role(self, document_key: str, signer_key: str, role: str, **additiona if document_key not in self._documents: raise RuntimeError(f"No document found with key {document_key}!") if type(self._documents[document_key]) is not TemplateRef: - raise RuntimeError(f"Document found with key {document_key} is not a Template!") + raise RuntimeError( + f"Document found with key {document_key} is not a Template!" + ) if signer_key not in self._packets: - raise RuntimeError(f"Signer {signer_key} does not have a corresponding packet") + raise RuntimeError( + f"Signer {signer_key} does not have a corresponding packet" + ) assignment = TemplateRefAssignment.create(role, signer_key, **additional_data) self._documents[document_key].add_assignment(assignment) @@ -224,7 +268,9 @@ def set_value(self, document_key: str, key: str, value: str, **additional_data): if document_key not in self._documents: raise RuntimeError(f"No document found with key {document_key}!") if type(self._documents[document_key]) is not TemplateRef: - raise RuntimeError(f"Document found with key {document_key} is not a Template!") + raise RuntimeError( + f"Document found with key {document_key} is not a Template!" + ) field_val = TemplateRefFieldValue.create(key, value, **additional_data) self._documents[document_key].field_values.append(field_val) @@ -239,17 +285,19 @@ def _compile_bundle(self, **additional_data) -> Bundle: """ packets = list(self._packets.values()) documents = list(self._documents.values()) - bundle_out = Bundle.create(packets, - documents, - label=self._label, - in_order=self._in_order, - email_subject=self._email_subj, - email_message=self._email_msg, - is_test=self._is_test, - cc_emails=self._cc_emails, - custom_key=self._custom_key, - team=self._team, - **additional_data) + bundle_out = Bundle.create( + packets, + documents, + label=self._label, + in_order=self._in_order, + email_subject=self._email_subj, + email_message=self._email_msg, + is_test=self._is_test, + cc_emails=self._cc_emails, + custom_key=self._custom_key, + team=self._team, + **additional_data, + ) return bundle_out def as_data(self, **additional_data): diff --git a/src/blueink/client.py b/src/blueink/client.py index a8fb279..74ee156 100644 --- a/src/blueink/client.py +++ b/src/blueink/client.py @@ -3,7 +3,7 @@ from src.blueink.constants import ( DEFAULT_BASE_URL, ENV_BLUEINK_API_URL, - ENV_BLUEINK_PRIVATE_API_KEY + ENV_BLUEINK_PRIVATE_API_KEY, ) from src.blueink.request_helper import RequestHelper from src.blueink.subclients.bundle import BundleSubClient @@ -14,8 +14,12 @@ class Client: - def __init__(self, private_api_key: str = None, base_url: str = None, - raise_exceptions: bool = True): + def __init__( + self, + private_api_key: str = None, + base_url: str = None, + raise_exceptions: bool = True, + ): """Initialize a Client instance to access the Blueink eSignature API Args: @@ -58,5 +62,3 @@ def __init__(self, private_api_key: str = None, base_url: str = None, self.packets = PacketSubClient(base_url, self._request_helper) self.templates = TemplateSubClient(base_url, self._request_helper) self.webhooks = WebhookSubClient(base_url, self._request_helper) - - diff --git a/src/blueink/model/bundles.py b/src/blueink/model/bundles.py index e90ca86..c1f1b8d 100644 --- a/src/blueink/model/bundles.py +++ b/src/blueink/model/bundles.py @@ -12,8 +12,8 @@ def __init__(self, error_text: str): def generate_key(type, length=5): - slug = ''.join(random.choice(string.ascii_letters) for i in range(length)) - return f'{type}_{slug}' + slug = "".join(random.choice(string.ascii_letters) for i in range(length)) + return f"{type}_{slug}" class Field(BaseModel): @@ -31,25 +31,20 @@ class Field(BaseModel): editors: Optional[List[str]] class Config: - extra = 'allow' + extra = "allow" @classmethod def create(cls, x, y, w, h, page, kind, key=None, **kwargs): if not key: - key = generate_key('field', 5) - obj = Field(key=key, - x=x, - y=y, - w=w, - h=h, - page=page, - kind=kind, - **kwargs) + key = generate_key("field", 5) + obj = Field(key=key, x=x, y=y, w=w, h=h, page=page, kind=kind, **kwargs) return obj - @validator('kind') + @validator("kind") def kind_is_allowed(cls, v): - assert v in FIELD_KIND.values(), f'Field Kind \'{v}\' not allowed. Must be one of {FIELD_KIND.values()}' + assert ( + v in FIELD_KIND.values() + ), f"Field Kind '{v}' not allowed. Must be one of {FIELD_KIND.values()}" return v def add_editor(self, editor: str): @@ -71,22 +66,22 @@ class Packet(BaseModel): order: Optional[str] class Config: - extra = 'allow' + extra = "allow" - @validator('deliver_via') + @validator("deliver_via") def deliver_via_is_allowed(cls, v): if v is not None: - assert v in DELIVER_VIA.values(), f'deliver_via \'{v}\' not allowed. Must be None' \ - f' or one of {DELIVER_VIA.values()}' + assert v in DELIVER_VIA.values(), ( + f"deliver_via '{v}' not allowed. Must be None" + f" or one of {DELIVER_VIA.values()}" + ) return v @classmethod def create(cls, name, key=None, **kwargs): if not key: - key = generate_key('packet', 5) - obj = Packet(key=key, - name=name, - **kwargs) + key = generate_key("packet", 5) + obj = Packet(key=key, name=name, **kwargs) return obj @@ -95,13 +90,11 @@ class TemplateRefAssignment(BaseModel): signer: str = ... class Config: - extra = 'allow' + extra = "allow" @classmethod def create(cls, role, signer, **kwargs): - obj = TemplateRefAssignment(role=role, - signer=signer, - **kwargs) + obj = TemplateRefAssignment(role=role, signer=signer, **kwargs) return obj @@ -110,13 +103,11 @@ class TemplateRefFieldValue(BaseModel): initial_value: str = ... class Config: - extra = 'allow' + extra = "allow" @classmethod def create(cls, key, initial_value, **kwargs): - obj = TemplateRefFieldValue(key=key, - initial_value=initial_value, - **kwargs) + obj = TemplateRefFieldValue(key=key, initial_value=initial_value, **kwargs) return obj @@ -126,12 +117,12 @@ class TemplateRef(BaseModel): field_values: Optional[List[TemplateRefFieldValue]] class Config: - extra = 'allow' + extra = "allow" @classmethod def create(cls, key=None, **kwargs): if not key: - key = generate_key('tmpl', 5) + key = generate_key("tmpl", 5) obj = TemplateRef(key=key, **kwargs) return obj @@ -155,12 +146,12 @@ class Document(BaseModel): fields: Optional[List[Field]] class Config: - extra = 'allow' + extra = "allow" @classmethod def create(cls, key=None, **kwargs): if not key: - key = generate_key('doc', 5) + key = generate_key("doc", 5) obj = Document(key=key, **kwargs) return obj @@ -188,13 +179,11 @@ class Bundle(BaseModel): team: Optional[str] class Config: - extra = 'allow' + extra = "allow" @classmethod def create(cls, packets: List[Packet], documents: List[Document], **kwargs): - obj = Bundle(packets=packets, - documents=documents, - **kwargs) + obj = Bundle(packets=packets, documents=documents, **kwargs) return obj def add_packet(self, packet: Packet): @@ -206,5 +195,3 @@ def add_document(self, document: Document): if self.documents is None: self.documents = [] self.documents.append(document) - - diff --git a/src/blueink/model/persons.py b/src/blueink/model/persons.py index 7b88a2a..bf4cb96 100644 --- a/src/blueink/model/persons.py +++ b/src/blueink/model/persons.py @@ -8,7 +8,7 @@ class ContactChannelSchema(BaseModel): kind: Optional[str] class Config: - extra = 'allow' + extra = "allow" class PersonSchema(BaseModel): @@ -17,6 +17,4 @@ class PersonSchema(BaseModel): channels: Optional[List[ContactChannelSchema]] class Config: - extra = 'allow' - - + extra = "allow" diff --git a/src/blueink/model/webhook.py b/src/blueink/model/webhook.py index aa6109a..630c95b 100644 --- a/src/blueink/model/webhook.py +++ b/src/blueink/model/webhook.py @@ -20,10 +20,10 @@ class WebhookSchema(BaseModel): event_types: List[str] extra_headers: Optional[List[WebhookExtraHeaderSchema]] - @validator('event_types') + @validator("event_types") def validate_event_types(cls, event_types): for event_type in event_types: - assert event_type in EVENT_TYPE.values(),\ - f'subscription event_type \'{event_type}\' not allowed. Must be one of ' \ - f'{EVENT_TYPE.values()}' - + assert event_type in EVENT_TYPE.values(), ( + f"subscription event_type '{event_type}' not allowed. Must be one of " + f"{EVENT_TYPE.values()}" + ) diff --git a/src/blueink/paginator.py b/src/blueink/paginator.py index 3e69ece..18dad2b 100644 --- a/src/blueink/paginator.py +++ b/src/blueink/paginator.py @@ -35,9 +35,11 @@ def __next__(self): raise StopIteration try: - api_response: NormalizedResponse = self._paged_func(page=self.next_page, - per_page=self._items_per_page, - **self._paged_func_args) + api_response: NormalizedResponse = self._paged_func( + page=self.next_page, + per_page=self._items_per_page, + **self._paged_func_args + ) except HTTPError: raise StopIteration @@ -49,4 +51,3 @@ def __next__(self): self.next_page = self.next_page + 1 return api_response - diff --git a/src/blueink/request_helper.py b/src/blueink/request_helper.py index dba7bf0..6b54667 100644 --- a/src/blueink/request_helper.py +++ b/src/blueink/request_helper.py @@ -97,7 +97,15 @@ def _build_headers(self, content_type=None, more_headers=None): return hdrs def _make_request( - self, method, url, data=None, json=None, files=None, params=None, headers=None, content_type=None + self, + method, + url, + data=None, + json=None, + files=None, + params=None, + headers=None, + content_type=None, ): response = requests.request( @@ -106,8 +114,9 @@ def _make_request( params=params, data=data, json=json, - headers=self._build_headers(content_type=content_type, - more_headers=headers), + headers=self._build_headers( + content_type=content_type, more_headers=headers + ), files=files, ) diff --git a/src/blueink/subclients/bundle.py b/src/blueink/subclients/bundle.py index fa48818..392ab09 100644 --- a/src/blueink/subclients/bundle.py +++ b/src/blueink/subclients/bundle.py @@ -29,23 +29,38 @@ def _prepare_files(self, file_list: [io.BufferedReader]): f" (e.g. an open file handle)" ) field_name = f"files[{idx}]" - files_data.append((field_name, (file_dict.get("filename"), - fh, - file_dict.get("content_type")))) + files_data.append( + ( + field_name, + ( + file_dict.get("filename"), + fh, + file_dict.get("content_type"), + ), + ) + ) elif "file_b64" in file_dict: print("B64 represented File") b64 = file_dict["file_b64"] field_name = f"files[{idx}]" - files_data.append((field_name, (file_dict.get("filename"), - b64, - file_dict.get("content_type")))) + files_data.append( + ( + field_name, + ( + file_dict.get("filename"), + b64, + file_dict.get("content_type"), + ), + ) + ) return files_data - def create(self, data: dict, - files: List[io.BufferedReader] = []) -> NormalizedResponse: - """ Post a Bundle to the BlueInk application. + def create( + self, data: dict, files: List[io.BufferedReader] = [] + ) -> NormalizedResponse: + """Post a Bundle to the BlueInk application. Args: data: raw data for Bundle, expressed as a python dict @@ -68,15 +83,14 @@ def create(self, data: dict, bundle_request_data = {"bundle_request": json.dumps(data)} - response = self._requests.post(url, - data=bundle_request_data, - files=files_data) + response = self._requests.post( + url, data=bundle_request_data, files=files_data + ) return response - def create_from_bundle_helper(self, - bdl_helper: BundleHelper) -> NormalizedResponse: - """ Post a Bundle to the BlueInk application. + def create_from_bundle_helper(self, bdl_helper: BundleHelper) -> NormalizedResponse: + """Post a Bundle to the BlueInk application. Provided as a convenience to simplify posting of a Bundle. This is the recommended way to create a Bundle. @@ -92,8 +106,13 @@ def create_from_bundle_helper(self, files = bdl_helper.files return self.create(data=data, files=files) - def paged_list(self, page: int = 1, per_page: int = 50, - related_data: bool = False, **query_params) -> PaginatedIterator: + def paged_list( + self, + page: int = 1, + per_page: int = 50, + related_data: bool = False, + **query_params, + ) -> PaginatedIterator: """Returns an iterable object such that you may lazily fetch a number of Bundles @@ -110,16 +129,23 @@ def paged_list(self, page: int = 1, per_page: int = 50, Returns: PaginatedIterator object, compliant with python iterables """ - iterator = PaginatedIterator(paged_api_function=self.list, - page=page, - per_page=per_page, - related_data=related_data, - **query_params) + iterator = PaginatedIterator( + paged_api_function=self.list, + page=page, + per_page=per_page, + related_data=related_data, + **query_params, + ) return iterator - def list(self, page: int = None, per_page: int = None, - related_data: bool = False, **query_params) -> NormalizedResponse: - """ Returns a list of bundles + def list( + self, + page: int = None, + per_page: int = None, + related_data: bool = False, + **query_params, + ) -> NormalizedResponse: + """Returns a list of bundles Args: page: which page to fetch @@ -131,10 +157,9 @@ def list(self, page: int = None, per_page: int = None, NormalizedResponse object """ url = self.build_url(endpoints.BUNDLES.LIST) - response = self._requests.get(url, - params=self.build_params(page, - per_page, - **query_params)) + response = self._requests.get( + url, params=self.build_params(page, per_page, **query_params) + ) if related_data: for bundle in response.data: @@ -157,8 +182,9 @@ def _attach_additional_data(self, bundle): data_response = self.list_data(bundle_id) bundle.data = data_response.data - def retrieve(self, bundle_id: str, - related_data: bool = False) -> NormalizedResponse: + def retrieve( + self, bundle_id: str, related_data: bool = False + ) -> NormalizedResponse: """Request a single bundle Args: diff --git a/src/blueink/subclients/person.py b/src/blueink/subclients/person.py index cd99477..2904235 100644 --- a/src/blueink/subclients/person.py +++ b/src/blueink/subclients/person.py @@ -24,8 +24,9 @@ def create(self, data: dict, **kwargs) -> NormalizedResponse: url = self.build_url(endpoints.PERSONS.CREATE) return self._requests.post(url, json=data) - def create_from_person_helper(self, person_helper: PersonHelper, - **kwargs) -> NormalizedResponse: + def create_from_person_helper( + self, person_helper: PersonHelper, **kwargs + ) -> NormalizedResponse: """Create a person using PersonHelper convenience object Args: @@ -36,8 +37,9 @@ def create_from_person_helper(self, person_helper: PersonHelper, """ return self.create(person_helper.as_dict(**kwargs)) - def paged_list(self, page: int = 1, per_page: int = 50, - **query_params) -> PaginatedIterator: + def paged_list( + self, page: int = 1, per_page: int = 50, **query_params + ) -> PaginatedIterator: """Return an iterable object such that you may lazily fetch a number of Persons (signers) @@ -54,14 +56,14 @@ def paged_list(self, page: int = 1, per_page: int = 50, PaginatedIterator object """ - iterator = PaginatedIterator(paged_api_function=self.list, - page=page, - per_page=per_page, - **query_params) + iterator = PaginatedIterator( + paged_api_function=self.list, page=page, per_page=per_page, **query_params + ) return iterator - def list(self, page: int = None, per_page: int = None, - **query_params) -> NormalizedResponse: + def list( + self, page: int = None, per_page: int = None, **query_params + ) -> NormalizedResponse: """Return a list of persons (signers). Args: @@ -73,10 +75,9 @@ def list(self, page: int = None, per_page: int = None, NormalizedResponse object """ url = self.build_url(endpoints.PERSONS.LIST) - return self._requests.get(url, - params=self.build_params(page, - per_page, - **query_params)) + return self._requests.get( + url, params=self.build_params(page, per_page, **query_params) + ) def retrieve(self, person_id: str) -> NormalizedResponse: """Retrieve details on a singular person @@ -90,8 +91,9 @@ def retrieve(self, person_id: str) -> NormalizedResponse: url = self.build_url(endpoints.PERSONS.RETRIEVE, person_id=person_id) return self._requests.get(url) - def update(self, person_id: str, data: dict, - partial: bool = False) -> NormalizedResponse: + def update( + self, person_id: str, data: dict, partial: bool = False + ) -> NormalizedResponse: """Update a Person (signer)'s record Args: diff --git a/src/blueink/subclients/subclient.py b/src/blueink/subclients/subclient.py index 8a5f090..ed67650 100644 --- a/src/blueink/subclients/subclient.py +++ b/src/blueink/subclients/subclient.py @@ -25,14 +25,16 @@ def build_url(self, endpoint: str, **kwargs): """ # All of our current endpoints take 1 parameter, max if len(kwargs) > 1: - raise ValueError('Only one interpolation parameter is allowed') + raise ValueError("Only one interpolation parameter is allowed") try: url = endpoints.URLBuilder(self._base_url, endpoint).build(**kwargs) except KeyError: arg_name = list(kwargs.keys())[0] - raise ValueError(f'Invalid substitution argument "{arg_name}"' - f' provided for endpoint "{endpoint}"') + raise ValueError( + f'Invalid substitution argument "{arg_name}"' + f' provided for endpoint "{endpoint}"' + ) return url diff --git a/src/blueink/subclients/template.py b/src/blueink/subclients/template.py index aca37b5..961d943 100644 --- a/src/blueink/subclients/template.py +++ b/src/blueink/subclients/template.py @@ -8,8 +8,9 @@ class TemplateSubClient(SubClient): def __init__(self, base_url, private_api_key): super().__init__(base_url, private_api_key) - def paged_list(self, page: int = 1, per_page: int = 50, - **query_params) -> PaginatedIterator: + def paged_list( + self, page: int = 1, per_page: int = 50, **query_params + ) -> PaginatedIterator: """return an iterable object containing a list of templates Typical Usage: @@ -24,14 +25,14 @@ def paged_list(self, page: int = 1, per_page: int = 50, Returns: PaginatedIterator object """ - iterator = PaginatedIterator(paged_api_function=self.list, - page=page, - per_page=per_page, - **query_params) + iterator = PaginatedIterator( + paged_api_function=self.list, page=page, per_page=per_page, **query_params + ) return iterator - def list(self, page: int = None, per_page: int = None, - **query_params) -> NormalizedResponse: + def list( + self, page: int = None, per_page: int = None, **query_params + ) -> NormalizedResponse: """Return a list of Templates. Args: @@ -43,9 +44,9 @@ def list(self, page: int = None, per_page: int = None, NormalizedResponse object """ url = self.build_url(endpoints.TEMPLATES.LIST) - return self._requests.get(url, params=self.build_params(page, - per_page, - **query_params)) + return self._requests.get( + url, params=self.build_params(page, per_page, **query_params) + ) def retrieve(self, template_id: str) -> NormalizedResponse: """Return a singular Template by id. diff --git a/src/blueink/subclients/webhook.py b/src/blueink/subclients/webhook.py index 380e7a5..7a01439 100644 --- a/src/blueink/subclients/webhook.py +++ b/src/blueink/subclients/webhook.py @@ -18,24 +18,24 @@ def create_webhook(self, data: dict): def list_webhooks(self, **query_params) -> NormalizedResponse: url = self.build_url(endpoint=endpoints.WEBHOOKS.LIST) - return self._requests.get(url, - params=self.build_params(query_params=query_params)) + return self._requests.get( + url, params=self.build_params(query_params=query_params) + ) def retrieve_webhook(self, webhook_id: str) -> NormalizedResponse: - url = self.build_url(endpoint=endpoints.WEBHOOKS.RETRIEVE, - webhook_id=webhook_id) + url = self.build_url( + endpoint=endpoints.WEBHOOKS.RETRIEVE, webhook_id=webhook_id + ) return self._requests.get(url) def delete_webhook(self, webhook_id: str) -> NormalizedResponse: - url = self.build_url(endpoint=endpoints.WEBHOOKS.DELETE, - webhook_id=webhook_id) + url = self.build_url(endpoint=endpoints.WEBHOOKS.DELETE, webhook_id=webhook_id) return self._requests.delete(url) def update_webhook(self, webhook_id: str, data: dict) -> NormalizedResponse: - url = self.build_url(endpoint=endpoints.WEBHOOKS.UPDATE, - webhook_id=webhook_id) + url = self.build_url(endpoint=endpoints.WEBHOOKS.UPDATE, webhook_id=webhook_id) return self._requests.patch(url, data=data) @@ -50,24 +50,26 @@ def create_header(self, data: dict) -> NormalizedResponse: def list_headers(self, **query_params) -> NormalizedResponse: url = self.build_url(endpoint=endpoints.WEBHOOKS.LIST_HEADERS) - return self._requests.get(url, - params=self.build_params(**query_params)) + return self._requests.get(url, params=self.build_params(**query_params)) def retrieve_header(self, header_id: str) -> NormalizedResponse: - url = self.build_url(endpoint=endpoints.WEBHOOKS.RETRIEVE_HEADER, - webhook_header_id=header_id) + url = self.build_url( + endpoint=endpoints.WEBHOOKS.RETRIEVE_HEADER, webhook_header_id=header_id + ) return self._requests.get(url) def delete_header(self, header_id: str) -> NormalizedResponse: - url = self.build_url(endpoint=endpoints.WEBHOOKS.DELETE_HEADERE, - webhook_header_id=header_id) + url = self.build_url( + endpoint=endpoints.WEBHOOKS.DELETE_HEADERE, webhook_header_id=header_id + ) return self._requests.delete(url) def update_header(self, header_id: str, **data) -> NormalizedResponse: - url = self.build_url(endpoint=endpoints.WEBHOOKS.UPDATE_HEADER, - webhook_header_id=header_id) + url = self.build_url( + endpoint=endpoints.WEBHOOKS.UPDATE_HEADER, webhook_header_id=header_id + ) return self._requests.patch(url, data=data) @@ -77,12 +79,14 @@ def update_header(self, header_id: str, **data) -> NormalizedResponse: def list_events(self, **query_params) -> NormalizedResponse: url = self.build_url(endpoint=endpoints.WEBHOOKS.LIST_EVENTS) - return self._requests.get(url, - params=self.build_params(query_params=query_params)) + return self._requests.get( + url, params=self.build_params(query_params=query_params) + ) def retrieve_event(self, event_id: str) -> NormalizedResponse: - url = self.build_url(endpoint=endpoints.WEBHOOKS.RETRIEVE_EVENT, - wehook_event_id=event_id) + url = self.build_url( + endpoint=endpoints.WEBHOOKS.RETRIEVE_EVENT, wehook_event_id=event_id + ) return self._requests.get(url) @@ -92,12 +96,15 @@ def retrieve_event(self, event_id: str) -> NormalizedResponse: def list_deliveries(self, **query_params) -> NormalizedResponse: url = self.build_url(endpoint=endpoints.WEBHOOKS.LIST_DELIVERIES) - return self._requests.get(url, - params=self.build_params(query_params=query_params)) + return self._requests.get( + url, params=self.build_params(query_params=query_params) + ) def retrieve_delivery(self, delivery_id: str) -> NormalizedResponse: - url = self.build_url(endpoint=endpoints.WEBHOOKS.RETRIEVE_DELIVERY, - webhook_delivery_id=delivery_id) + url = self.build_url( + endpoint=endpoints.WEBHOOKS.RETRIEVE_DELIVERY, + webhook_delivery_id=delivery_id, + ) return self._requests.get(url) diff --git a/src/blueink/tests/test_bundle_helper.py b/src/blueink/tests/test_bundle_helper.py index 16ab3da..9c2dc35 100644 --- a/src/blueink/tests/test_bundle_helper.py +++ b/src/blueink/tests/test_bundle_helper.py @@ -56,7 +56,7 @@ class TestBundleHelper(TestCase): "p": 1, "kind": "inp", "editors": [], - "label": "MY_INPUT_01" + "label": "MY_INPUT_01", } FIELD_02_DATA = { @@ -67,7 +67,7 @@ class TestBundleHelper(TestCase): "p": 1, "kind": "inp", "editors": [], - "label": "MY_INPUT_02" + "label": "MY_INPUT_02", } DOCUMENT_01_URL = "https://www.example.com/example1.pdf" @@ -133,7 +133,6 @@ def test_adding_fields(self): field01_data = copy.deepcopy(self.FIELD_01_DATA) field02_data = copy.deepcopy(self.FIELD_02_DATA) - bh = BundleHelper(**input_data) doc01_key = bh.add_document_by_url(url01) signer01_key = bh.add_signer(**signer01_data) diff --git a/src/blueink/tests/test_client.py b/src/blueink/tests/test_client.py index 7e885dc..fce2f21 100644 --- a/src/blueink/tests/test_client.py +++ b/src/blueink/tests/test_client.py @@ -11,12 +11,7 @@ # Bundle Subclient Tests # ----------------- class TestClientBundle(TestCase): - DOC_METHODS = Munch( - PATH="PATH", - URL="URL", - B64="BASE64" - ) - + DOC_METHODS = Munch(PATH="PATH", URL="URL", B64="BASE64") BUNDLE_LABEL_URL = "A URL Bundle Label!" BUNDLE_LABEL_PATH = "A PATH Bundle Label!" @@ -56,10 +51,12 @@ def _create_test_bundle_helper(self, method: str) -> (BundleHelper, str, str): (BundleHelper, signerkey, fieldkey) """ - bh = BundleHelper(self._bundle_label(method), - self.EMAIL_SUBJECT, - self.EMAIL_MESSAGE, - is_test=True) + bh = BundleHelper( + self._bundle_label(method), + self.EMAIL_SUBJECT, + self.EMAIL_MESSAGE, + is_test=True, + ) # Add Document doc01_key = None @@ -68,34 +65,37 @@ def _create_test_bundle_helper(self, method: str) -> (BundleHelper, str, str): elif method == self.DOC_METHODS.URL: doc01_key = bh.add_document_by_url(self.REAL_DOCUMENT_URL) elif method == self.DOC_METHODS.B64: - file = open(self.REAL_DOCUMENT_PATH, 'rb') + file = open(self.REAL_DOCUMENT_PATH, "rb") filename = basename(self.REAL_DOCUMENT_PATH) b64str = b64encode(file.read()) file.close() doc01_key = bh.add_document_by_b64(filename, b64str) # Add Signer 1 - signer01_key = bh.add_signer(key=self.SIGNER01_KEY, - name=self.SIGNER01_NAME, - email=self.SIGNER01_EMAIL, - phone=self.SIGNER01_PHONE, - deliver_via=self.SIGNER01_DELIVERY) + signer01_key = bh.add_signer( + key=self.SIGNER01_KEY, + name=self.SIGNER01_NAME, + email=self.SIGNER01_EMAIL, + phone=self.SIGNER01_PHONE, + deliver_via=self.SIGNER01_DELIVERY, + ) # Add Field - field01_key = bh.add_field(document_key=doc01_key, - x=self.FIELD01_X, - y=self.FIELD01_Y, - w=self.FIELD01_W, - h=self.FIELD01_H, - p=self.FIELD01_P, - kind=self.FIELD01_KIND, - editors=self.FIELD01_EDITORS, - label=self.FIELD01_LABEL - ) - - self._check_bundle_data(bh.as_data(), - signerkey=signer01_key, - fieldkey=field01_key) + field01_key = bh.add_field( + document_key=doc01_key, + x=self.FIELD01_X, + y=self.FIELD01_Y, + w=self.FIELD01_W, + h=self.FIELD01_H, + p=self.FIELD01_P, + kind=self.FIELD01_KIND, + editors=self.FIELD01_EDITORS, + label=self.FIELD01_LABEL, + ) + + self._check_bundle_data( + bh.as_data(), signerkey=signer01_key, fieldkey=field01_key + ) return bh, signer01_key, field01_key @@ -124,27 +124,21 @@ def _check_bundle_data(self, compiled_bundle: dict, signerkey, fieldkey): self.assert_equal(compiled_bundle["packets"][0]["key"], signerkey) def test_roundtrip_url(self): - bh, sk, fk = self._create_test_bundle_helper( - method=self.DOC_METHODS.URL - ) + bh, sk, fk = self._create_test_bundle_helper(method=self.DOC_METHODS.URL) client = Client(raise_exceptions=False) resp = client.bundles.create_from_bundle_helper(bh) self.assert_equal(resp.status, 201) def test_roundtrip_b64(self): - bh, sk, fk = self._create_test_bundle_helper( - method=self.DOC_METHODS.B64 - ) + bh, sk, fk = self._create_test_bundle_helper(method=self.DOC_METHODS.B64) client = Client(raise_exceptions=False) resp = client.bundles.create_from_bundle_helper(bh) self.assert_equal(resp.status, 201) def test_roundtrip_path(self): - bh, sk, fk = self._create_test_bundle_helper( - method=self.DOC_METHODS.PATH - ) + bh, sk, fk = self._create_test_bundle_helper(method=self.DOC_METHODS.PATH) client = Client(raise_exceptions=False) resp = client.bundles.create_from_bundle_helper(bh) @@ -170,10 +164,12 @@ class TestClientPerson(TestCase): PERSON_EMAILS = ["johndoe@example.com"] def test_person_create(self): - ph = PersonHelper(name=self.PERSON_NAME, - metadata=self.PERSON_METADATA, - phones=self.PERSON_PHONES, - emails=self.PERSON_EMAILS) + ph = PersonHelper( + name=self.PERSON_NAME, + metadata=self.PERSON_METADATA, + phones=self.PERSON_PHONES, + emails=self.PERSON_EMAILS, + ) client = Client(raise_exceptions=False) resp = client.persons.create_from_person_helper(ph) @@ -317,9 +313,7 @@ def test_webhook_update(self): update_data = { "enabled": False, - "event_types": [ - EVENT_TYPE.EVENT_PACKET_VIEWED - ] + "event_types": [EVENT_TYPE.EVENT_PACKET_VIEWED], } resp3 = client.webhooks.update_webhook(resp1.data.id, update_data) self.assert_equal(resp3.status, 200, resp3.data) @@ -329,6 +323,7 @@ def test_webhook_update(self): # Cleanup resp_clean1 = client.webhooks.delete_webhook(resp1.data.id) self.assert_equal(resp_clean1.status, 204, resp_clean1.data) + # ----------------- # Extraheader CRUD / Listing # ----------------- @@ -394,5 +389,3 @@ def test_extraheader_listing(self): # ----------------- # Secret testing not implemented, will tamper with whoever runs this test suite # ----------------- - - diff --git a/src/blueink/tests/test_person_helper.py b/src/blueink/tests/test_person_helper.py index 5cf0309..1ac1329 100644 --- a/src/blueink/tests/test_person_helper.py +++ b/src/blueink/tests/test_person_helper.py @@ -3,7 +3,7 @@ class TestPersonHelper(TestCase): - NAME="JOHN DOE" + NAME = "JOHN DOE" MD_KEY01 = "KEY01" MD_KEY02 = "KEY02" @@ -17,7 +17,7 @@ class TestPersonHelper(TestCase): PHONES = ["505 555 5555"] EMAILS = ["johndoe@example.com"] - def _check_values(self, data:dict): + def _check_values(self, data: dict): self.assert_equal(data["name"], self.NAME) self.assert_in("channels", data) @@ -38,7 +38,6 @@ def _check_values(self, data:dict): self.assert_true(has_email) self.assert_true(has_phone) - self.assert_in("metadata", data) self.assert_len(data["metadata"], 2) self.assert_in(self.MD_KEY01, data["metadata"]) @@ -56,10 +55,11 @@ def test_atomic(self): self._check_values(ph.as_dict()) def test_all_in_one(self): - ph = PersonHelper(name=self.NAME, - metadata=self.METADATA, - phones=self.PHONES, - emails=self.EMAILS) + ph = PersonHelper( + name=self.NAME, + metadata=self.METADATA, + phones=self.PHONES, + emails=self.EMAILS, + ) self._check_values(ph.as_dict()) - diff --git a/src/blueink/utils/testcase.py b/src/blueink/utils/testcase.py index 93d01a9..a8cd04b 100644 --- a/src/blueink/utils/testcase.py +++ b/src/blueink/utils/testcase.py @@ -1,5 +1,4 @@ class TestCase: - def assert_true(self, val): if val is None: assert False, f"{val} is None, not True" From 5125a947377bdf282a404cdf0f26beaa4d65174a Mon Sep 17 00:00:00 2001 From: Zach Lovelady <108858+zlove@users.noreply.github.com> Date: Thu, 17 Nov 2022 15:56:40 -0700 Subject: [PATCH 03/17] Adding isort, black and other pre-commit checks --- .pre-commit-config.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5ed79b8..f96c032 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,19 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files - repo: https://github.com/psf/black rev: 22.10.0 hooks: - id: black language_version: python3.9 + - repo: https://github.com/PyCQA/isort + rev: 5.10.1 + hooks: + - id: isort From fb23ec8784b3df79ec3124e6149c62362160b2de Mon Sep 17 00:00:00 2001 From: Zach Lovelady <108858+zlove@users.noreply.github.com> Date: Thu, 17 Nov 2022 16:05:43 -0700 Subject: [PATCH 04/17] Adding pre-commit black language version. Fixing yaml. --- .pre-commit-config.yaml | 31 +++++++++++++++---------------- pyproject.toml | 6 ++++++ requirements/dev.txt | 1 + 3 files changed, 22 insertions(+), 16 deletions(-) create mode 100644 requirements/dev.txt diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f96c032..65f3e28 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,19 +1,18 @@ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-yaml - - id: check-added-large-files - - repo: https://github.com/psf/black - rev: 22.10.0 - hooks: - - id: black - language_version: python3.9 - - repo: https://github.com/PyCQA/isort - rev: 5.10.1 - hooks: - - id: isort + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-added-large-files + - repo: https://github.com/psf/black + rev: 22.10.0 + hooks: + - id: black + language_version: python3.9 + - repo: https://github.com/PyCQA/isort + rev: 5.10.1 + hooks: + - id: isort diff --git a/pyproject.toml b/pyproject.toml index b0f0765..6f5aca5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,9 @@ [build-system] requires = ["setuptools>=42"] build-backend = "setuptools.build_meta" + +[tool.black] +line-length = 88 + +[tool.isort] +profile = "black" diff --git a/requirements/dev.txt b/requirements/dev.txt new file mode 100644 index 0000000..561cab7 --- /dev/null +++ b/requirements/dev.txt @@ -0,0 +1 @@ +pre-commit==2.20.0 From 484040b5db3f459f31ad1cb694583f6411aac243 Mon Sep 17 00:00:00 2001 From: Joe Sheldon Date: Thu, 17 Nov 2022 16:10:21 -0700 Subject: [PATCH 05/17] readme and webhook method names --- README.md | 192 ++++++++++++++++++++++++++++-- src/blueink/subclients/webhook.py | 10 +- src/blueink/tests/test_client.py | 52 ++++---- 3 files changed, 213 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index da74c51..bc40a4b 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,14 @@ HTTP5xx errors). These come from the `requests` module. If within your applicati you already handle exceptions coming out of `requests` then you should be good. If you set `raise_exceptions = False` then these will be returned as `NormalizedResponse` objects which are also used for successful communications. See -below for information about these objects. +below for information about these objects. + +```python +# In your python code, create an instance of the blueink Client +from blueink import Client + +client = Client(raise_exceptions=False) +``` ### Making API Calls @@ -218,6 +225,62 @@ for paged_response in iterator: print(bundle.id) ``` +## Client Method Index +Parameters can be found using autocomplete within your IDE. Creates/Updates take a +Python dictionary as the data field, unless special named methods like +```create_from_bundle_helper``` indicate otherwise. List methods can take query params +as kwargs. + +### Bundle Related +* Create via ```client.bundles.create(...)``` or ```client.bundles.create_from_bundle_helper(...)``` +* List via ```client.bundles.list(...)``` or ```client.bundles.paged_list(...)``` +* Retrieve via ```client.bundles.retrieve(...)``` +* Cancel via ```client.bundles.cancel(...)``` +* List Events via ```client.bundles.list_events(...)``` +* List Files via ```client.bundles.list_files(...)``` +* List Data via ```client.bundles.list_data(...)``` + +### Person Related +* Create via ```client.persons.create(...)``` or ```client.persons.create_from_person_helper(...)``` +* List via ```client.persons.list(...)``` or ```client.persons.paged_list(...)``` +* Retrieve via ```client.persons.retrieve(...)``` +* Delete via ```client.persons.delete(...)``` +* Update via ```client.persons.update(...)``` + +### Packet Related +* Update via ```client.packets.update(...)``` +* Create Embedded Signing URL via ```client.packets.embed_url(...)``` +* Retrieve COE via ```client.packets.retrieve_coe(...)``` +* Remind via ```client.packets.remind(...)``` + +### Template Related +* List via ```client.templates.list(...)``` or ```client.templates.paged_list(...)``` +* Retrieve via ```client.templates.retrieve(...)``` + +### Webhook Related + +#### Webhook Client Methods +* Create via ```client.webhooks.create(...)``` +* List via ```client.webhooks.list(...)``` +* Retrieve via ```client.webhooks.retrieve(...)``` +* Delete via ```client.webhooks.delete(...)``` +* Update via ```client.webhooks.update(...)``` + +#### WebhookExtraHeader Client Methods +* Create via ```client.webhooks.create_header(...)``` +* List via ```client.webhooks.list_headers(...)``` +* Retrieve via ```client.webhooks.retrieve_header(...)``` +* Delete via ```client.webhooks.delete_header(...)``` +* Update via ```client.webhooks.update_header(...)``` + +#### WebhookEvent Client Methods +* List via ```client.webhooks.list_events(...)``` +* Retrieve via ```client.webhooks.retrieve_event(...)``` + +#### WebhookDelivery Client Methods +* List via ```client.webhooks.list_deliveries(...)``` +* Retrieve via ```client.webhooks.retrieve_delivery(...)``` + ## Detailed Guide and Examples ### Bundles @@ -285,12 +348,24 @@ Using the `BundleHelper`, you can add files to a Bundle in multiple ways: ```python bh = BundleHelper(...) -# Add a document using a path to the file in the local filesystem -doc1_key = bh.add_document_by_path("/path/to/file/fw4.pdf", "application/pdf") +# 0) Add a document using a URL to a web resource: +doc0_key = bh.add_document_by_url("https://www.example.com/example.pdf") + +# 1) Add a document using a path to the file in the local filesystem +doc1_key = bh.add_document_by_path("/path/to/file/example.pdf") + +# 2) Add a document using a UTF-8 encoded Base64 string: +filename, pdf_b64 = read_a_file_into_b64_string() +doc02_key = bh.add_document_by_b64(filename, pdf_b64) -# Add a document that you have already read into a Python bytearray object -pdf_bytearray = read_a_file_into_a_bytearray() -doc2_key = bh.add_document_by_bytearray(pdf_bytearray, "fw4.pdf", "application/pdf") +# 3) Add a document that you have already read into a Python bytearray object +filename, pdf_bytearray = read_a_file_into_bytearray() +doc03_key = bh.add_document_by_bytearray(pdf_bytearray, filename) + +# 4) Add a document as a File object. Make sure to use 'with' or suitably close the file +# after creating the document. +with open("/path/to/file/example.pdf", 'rb') as file: + doc04_key = bh.add_document_by_file(file) ``` @@ -335,7 +410,6 @@ Creating a person is similar to a creating a Bundle. There is a PersonHelper to create a person ```python -import json from copy import deepcopy from requests.exceptions import HTTPError from pprint import pprint @@ -458,7 +532,7 @@ except Exception as e: # Perform a partial update to change the name again third_name = {"name": "Third Name"} try: - result = client.persons.partial_update(result.data.id, third_name) + result = client.persons.update(result.data.id, third_name, True) pprint(f"Result Partial Update: {result.status}: {result.data}") except HTTPError as e: print(e) @@ -498,8 +572,6 @@ except HTTPError as e: except Exception as e: print("Error:") print(e) - - ``` ### Packets @@ -536,3 +608,103 @@ template_response = client.templates.retrieve(template_id) ``` +### Webhooks + +Webhooks can be interacted with via several methods. Webhooks also have related objects, such as +```WebhookExtraHeaders```, ```WebhookEvents```, and ```WebhookDeliveries``` which have their own +methods to interact with. + +```python +from copy import deepcopy + +from requests import HTTPError +from src.blueink import Client +from src.blueink.constants import EVENT_TYPE + +WEBHOOK_01 = { + "url": "https://www.example.com/01/", + "enabled": True, + "json": True, + "event_types": [ + EVENT_TYPE.EVENT_BUNDLE_LAUNCHED, + EVENT_TYPE.EVENT_BUNDLE_COMPLETE, + ] +} + +WEBHOOK_01_UPDATE = { + "enabled": False, + "event_types": [ + EVENT_TYPE.EVENT_PACKET_VIEWED + ] +} + +WEBHOOK_01_EXTRA_HEADER_A = { + "name": "Courage_The_Cowardly_Webhook", + "value": "Muriel Bagge", + "order": 0, +} + +client = Client() + +# -------------- +# Attempt posting a new Webhook +# -------------- +try: + create_resp = client.webhooks.create(data=WEBHOOK_01) + webhook = create_resp.data + print(f"Created webhook {webhook.id}") +except HTTPError as e: + print("Error Creating Webhook: ") + print(e) + +# -------------- +# Update Webhook +# -------------- +try: + update_resp = client.webhooks.update(webhook.id, WEBHOOK_01_UPDATE) + webhook = update_resp.data + print(f"Updated webhook {webhook.id}") +except HTTPError as e: + print("Error Updating Webhook: ") + print(e) + +# -------------- +# Create and add an ExtraHeader to the above Webhook +# -------------- +extra_header_data = deepcopy(WEBHOOK_01_EXTRA_HEADER_A) +extra_header_data["webhook"] = webhook.id +try: + header_create_resp = client.webhooks.create_header(data=extra_header_data) + header = header_create_resp.data + print(f"Added ExtraHeader {header} to {webhook.id}") +except HTTPError as e: + print("Error Creating ExtraHeader: ") + print(e) + + +# -------------- +# List Webhooks +# -------------- +try: + list_resp = client.webhooks.list() + webhook_list = list_resp.data + + print(f"Found {len(webhook_list)} Webhooks") + for wh in webhook_list: + print(f" - Webhook ID: {wh.id} to {wh.url}") + + +except HTTPError as e: + print("Error Listing Webhooks: ") + print(e) + +# -------------- +# Delete webhook +# -------------- +try: + delete_resp = client.webhooks.delete(webhook.id) + print(f"Deleted Webhook {webhook.id}") +except HTTPError as e: + print(f"Error Deleting Webhooks {webhook.id}") + print(e) +``` \ No newline at end of file diff --git a/src/blueink/subclients/webhook.py b/src/blueink/subclients/webhook.py index 7a01439..bda2664 100644 --- a/src/blueink/subclients/webhook.py +++ b/src/blueink/subclients/webhook.py @@ -10,31 +10,31 @@ def __init__(self, base_url, private_api_key): # ---------- # Webhooks # ---------- - def create_webhook(self, data: dict): + def create(self, data: dict): url = self.build_url(endpoint=endpoints.WEBHOOKS.CREATE) return self._requests.post(url, data=data) - def list_webhooks(self, **query_params) -> NormalizedResponse: + def list(self, **query_params) -> NormalizedResponse: url = self.build_url(endpoint=endpoints.WEBHOOKS.LIST) return self._requests.get( url, params=self.build_params(query_params=query_params) ) - def retrieve_webhook(self, webhook_id: str) -> NormalizedResponse: + def retrieve(self, webhook_id: str) -> NormalizedResponse: url = self.build_url( endpoint=endpoints.WEBHOOKS.RETRIEVE, webhook_id=webhook_id ) return self._requests.get(url) - def delete_webhook(self, webhook_id: str) -> NormalizedResponse: + def delete(self, webhook_id: str) -> NormalizedResponse: url = self.build_url(endpoint=endpoints.WEBHOOKS.DELETE, webhook_id=webhook_id) return self._requests.delete(url) - def update_webhook(self, webhook_id: str, data: dict) -> NormalizedResponse: + def update(self, webhook_id: str, data: dict) -> NormalizedResponse: url = self.build_url(endpoint=endpoints.WEBHOOKS.UPDATE, webhook_id=webhook_id) return self._requests.patch(url, data=data) diff --git a/src/blueink/tests/test_client.py b/src/blueink/tests/test_client.py index fce2f21..161872d 100644 --- a/src/blueink/tests/test_client.py +++ b/src/blueink/tests/test_client.py @@ -218,79 +218,79 @@ def test_webhook_creation_raw(self): data = self.WEBHOOK_01 client = Client(raise_exceptions=False) - resp1 = client.webhooks.create_webhook(data=data) + resp1 = client.webhooks.create(data=data) self.assert_equal(resp1.status, 201, resp1.data) - resp_clean1 = client.webhooks.delete_webhook(resp1.data.id) + resp_clean1 = client.webhooks.delete(resp1.data.id) self.assert_equal(resp_clean1.status, 204, resp_clean1.data) def test_webhook_listing(self): # Known to fail if > 50 webhooks exist; list_webhooks gives up to 50. client = Client(raise_exceptions=False) - pre_create = client.webhooks.list_webhooks() + pre_create = client.webhooks.list() self.assert_equal(pre_create.status, 200) pre_create_len = len(pre_create.data) data1 = self.WEBHOOK_01 - resp1 = client.webhooks.create_webhook(data=data1) + resp1 = client.webhooks.create(data=data1) self.assert_equal(resp1.status, 201, resp1.data) data2 = self.WEBHOOK_02 - resp2 = client.webhooks.create_webhook(data=data2) + resp2 = client.webhooks.create(data=data2) self.assert_equal(resp1.status, 201, resp2.data) - post_create = client.webhooks.list_webhooks() + post_create = client.webhooks.list() self.assert_equal(post_create.status, 200) post_create_len = len(post_create.data) self.assert_equal(pre_create_len + 2, post_create_len) # Cleanup - resp_clean1 = client.webhooks.delete_webhook(resp1.data.id) + resp_clean1 = client.webhooks.delete(resp1.data.id) self.assert_equal(resp_clean1.status, 204, resp_clean1.data) - resp_clean2 = client.webhooks.delete_webhook(resp2.data.id) + resp_clean2 = client.webhooks.delete(resp2.data.id) self.assert_equal(resp_clean2.status, 204, resp_clean2.data) def test_webhook_retrieval(self): client = Client(raise_exceptions=False) data1 = self.WEBHOOK_01 - resp1 = client.webhooks.create_webhook(data=data1) + resp1 = client.webhooks.create(data=data1) self.assert_equal(resp1.status, 201, resp1.data) self.assert_equal(resp1.data["url"], data1["url"]) - resp2 = client.webhooks.retrieve_webhook(resp1.data.id) + resp2 = client.webhooks.retrieve(resp1.data.id) self.assert_equal(resp2.status, 200, resp2.data) self.assert_equal(resp2.data["url"], data1["url"]) # Cleanup - resp_clean1 = client.webhooks.delete_webhook(resp1.data.id) + resp_clean1 = client.webhooks.delete(resp1.data.id) self.assert_equal(resp_clean1.status, 204, resp_clean1.data) def test_webhook_delete(self): # Known to fail if > 50 webhooks exist; list_webhooks gives up to 50. client = Client(raise_exceptions=False) - resp0 = client.webhooks.list_webhooks() + resp0 = client.webhooks.list() self.assert_equal(resp0.status, 200) pre_create_len = len(resp0.data) data1 = self.WEBHOOK_01 - resp1 = client.webhooks.create_webhook(data=data1) + resp1 = client.webhooks.create(data=data1) self.assert_equal(resp1.status, 201, resp1.data) - resp2 = client.webhooks.list_webhooks() + resp2 = client.webhooks.list() self.assert_equal(resp2.status, 200) post_create_len = len(resp2.data) self.assert_equal(pre_create_len + 1, post_create_len) - resp3 = client.webhooks.delete_webhook(resp1.data.id) + resp3 = client.webhooks.delete(resp1.data.id) self.assert_equal(resp3.status, 204, resp3.data) - resp4 = client.webhooks.list_webhooks() + resp4 = client.webhooks.list() self.assert_equal(resp4.status, 200) post_delete_len = len(resp4.data) @@ -300,11 +300,11 @@ def test_webhook_update(self): client = Client(raise_exceptions=False) data1 = self.WEBHOOK_01 - resp1 = client.webhooks.create_webhook(data=data1) + resp1 = client.webhooks.create(data=data1) self.assert_equal(resp1.status, 201, resp1.data) self.assert_equal(resp1.data["url"], data1["url"]) - resp2 = client.webhooks.retrieve_webhook(resp1.data.id) + resp2 = client.webhooks.retrieve(resp1.data.id) self.assert_equal(resp2.status, 200, resp2.data) self.assert_equal(resp2.data["url"], data1["url"]) @@ -315,13 +315,13 @@ def test_webhook_update(self): "enabled": False, "event_types": [EVENT_TYPE.EVENT_PACKET_VIEWED], } - resp3 = client.webhooks.update_webhook(resp1.data.id, update_data) + resp3 = client.webhooks.update(resp1.data.id, update_data) self.assert_equal(resp3.status, 200, resp3.data) self.assert_equal(resp3.data["enabled"], update_data["enabled"]) self.assert_len(resp3.data["event_types"], len(update_data["event_types"])) # Cleanup - resp_clean1 = client.webhooks.delete_webhook(resp1.data.id) + resp_clean1 = client.webhooks.delete(resp1.data.id) self.assert_equal(resp_clean1.status, 204, resp_clean1.data) # ----------------- @@ -331,7 +331,7 @@ def test_extraheader_creation_raw(self): data = self.WEBHOOK_01 client = Client(raise_exceptions=False) - resp1 = client.webhooks.create_webhook(data=data) + resp1 = client.webhooks.create(data=data) self.assert_equal(resp1.status, 201, resp1.data) eh_data = deepcopy(self.WEBHOOK_01_EXTRA_HEADER_A) @@ -341,7 +341,7 @@ def test_extraheader_creation_raw(self): self.assert_equal(resp2.status, 201, resp2.data) # Cleanup - resp_clean1 = client.webhooks.delete_webhook(resp1.data.id) + resp_clean1 = client.webhooks.delete(resp1.data.id) self.assert_equal(resp_clean1.status, 204, resp_clean1.data) def test_extraheader_listing(self): @@ -350,10 +350,10 @@ def test_extraheader_listing(self): client = Client(raise_exceptions=False) # Create parent webhooks - resp1a = client.webhooks.create_webhook(data=data1) + resp1a = client.webhooks.create(data=data1) self.assert_equal(resp1a.status, 201, resp1a.data) - resp1b = client.webhooks.create_webhook(data=data2) + resp1b = client.webhooks.create(data=data2) self.assert_equal(resp1b.status, 201, resp1b.data) # setup and create headers under wh 1 @@ -376,10 +376,10 @@ def test_extraheader_listing(self): self.assert_len(resp4b.data, 0) # Cleanup - resp_clean1 = client.webhooks.delete_webhook(resp1a.data.id) + resp_clean1 = client.webhooks.delete(resp1a.data.id) self.assert_equal(resp_clean1.status, 204, resp_clean1.data) - resp_clean2 = client.webhooks.delete_webhook(resp1b.data.id) + resp_clean2 = client.webhooks.delete(resp1b.data.id) self.assert_equal(resp_clean2.status, 204, resp_clean2.data) # ----------------- From 1fffea191190ad17cfb4ed293d13c6166d0b70a0 Mon Sep 17 00:00:00 2001 From: Joe Sheldon Date: Thu, 17 Nov 2022 16:35:53 -0700 Subject: [PATCH 06/17] fixed merge issue webhook.py --- src/blueink/subclients/webhook.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/blueink/subclients/webhook.py b/src/blueink/subclients/webhook.py index c21c20a..bda2664 100644 --- a/src/blueink/subclients/webhook.py +++ b/src/blueink/subclients/webhook.py @@ -10,16 +10,6 @@ def __init__(self, base_url, private_api_key): # ---------- # Webhooks # ---------- - def create_webhook( - self, - url: str, - event_types: List[str], - extra_headers: List[WebhookExtraHeader], - enabled: bool = True, - json: bool = True, - ): - raise RuntimeError("Not Implemented") - def create(self, data: dict): url = self.build_url(endpoint=endpoints.WEBHOOKS.CREATE) From 2be418bde548331666ebcc44358ecbb4d838156d Mon Sep 17 00:00:00 2001 From: Joe Sheldon Date: Thu, 17 Nov 2022 16:43:05 -0700 Subject: [PATCH 07/17] fixed merge issue bundle_helper.py regarding b64 --- src/blueink/bundle_helper.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/blueink/bundle_helper.py b/src/blueink/bundle_helper.py index 1d81fd5..0987ab0 100644 --- a/src/blueink/bundle_helper.py +++ b/src/blueink/bundle_helper.py @@ -79,10 +79,9 @@ def add_document_by_path(self, file_path: str, **additional_data) -> str: def add_document_by_b64(self, filename: str, b64str: str, **additional_data): file_index = len(self.files) - - self.files.append({"file_b64": b64str, "filename": filename}) - document = Document.create(file_index=file_index, **additional_data) - print(f"doc -- {document.key}") + document = Document.create( + filename=filename, file_b64=b64str, **additional_data + ) self._documents[document.key] = document return document.key From 5434b1fc1d1e330c94960b047fbf521fd70f6fa0 Mon Sep 17 00:00:00 2001 From: Joe Sheldon Date: Thu, 17 Nov 2022 16:54:26 -0700 Subject: [PATCH 08/17] github actions --- .github/workflows/black.yml | 14 ++++++++ .github/workflows/pytest-helpers.yml | 50 ++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 .github/workflows/black.yml create mode 100644 .github/workflows/pytest-helpers.yml diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml new file mode 100644 index 0000000..3cdc4e1 --- /dev/null +++ b/.github/workflows/black.yml @@ -0,0 +1,14 @@ +name: Black Linting + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: psf/black@stable + with: + options: "--check --verbose" + src: "./src" + version: "21.5b1" diff --git a/.github/workflows/pytest-helpers.yml b/.github/workflows/pytest-helpers.yml new file mode 100644 index 0000000..6211ddd --- /dev/null +++ b/.github/workflows/pytest-helpers.yml @@ -0,0 +1,50 @@ +name: PyTests + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.9"] + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + + with: + python-version: ${{ matrix.python-version }} + + - name: Cache pip modules + id: cache-pip + uses: actions/cache@v3 + env: + cache-name: cache-pip-modules + with: + path: ~/.cache/pip + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('requirements.txt') }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + + - name: Test BundleHelper + run: | + pytest ./src/blueink/tests/test_bundle_helper.py + + - name: Test PersonHelper + run: | + pytest ./src/blueink/tests/test_person_helper.py From 07de6c65cfa99842bb687bb47b804e94d0fe36e5 Mon Sep 17 00:00:00 2001 From: Joe Sheldon Date: Thu, 17 Nov 2022 16:56:05 -0700 Subject: [PATCH 09/17] github action -- test several python versions --- .github/workflows/pytest-helpers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pytest-helpers.yml b/.github/workflows/pytest-helpers.yml index 6211ddd..61ada0c 100644 --- a/.github/workflows/pytest-helpers.yml +++ b/.github/workflows/pytest-helpers.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9"] + python-version: ["3.7", "3.8", "3.9", "3.10"] steps: - uses: actions/checkout@v3 From 0cccb117c71b09fcaa11f2c2a4e5cb5cd716340f Mon Sep 17 00:00:00 2001 From: Joe Sheldon Date: Thu, 17 Nov 2022 16:56:54 -0700 Subject: [PATCH 10/17] tweak black action version --- .github/workflows/black.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml index 3cdc4e1..8952e69 100644 --- a/.github/workflows/black.yml +++ b/.github/workflows/black.yml @@ -11,4 +11,4 @@ jobs: with: options: "--check --verbose" src: "./src" - version: "21.5b1" + version: "~= 22.0" From 53c045c6673118858a8dc665d126d95b56d1f649 Mon Sep 17 00:00:00 2001 From: Joe Sheldon Date: Thu, 17 Nov 2022 16:59:51 -0700 Subject: [PATCH 11/17] tweak black action name --- .github/workflows/black.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml index 8952e69..84ecd8a 100644 --- a/.github/workflows/black.yml +++ b/.github/workflows/black.yml @@ -1,4 +1,4 @@ -name: Black Linting +name: Style Checks on: [push, pull_request] From 0bf571027f49707011b7fcfd3e6f8431f2accf8f Mon Sep 17 00:00:00 2001 From: Joe Sheldon Date: Thu, 17 Nov 2022 17:00:22 -0700 Subject: [PATCH 12/17] rename actions --- .github/workflows/{pytest-helpers.yml => helper-tests.yml} | 0 .github/workflows/{black.yml => style-checks.yml} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{pytest-helpers.yml => helper-tests.yml} (100%) rename .github/workflows/{black.yml => style-checks.yml} (100%) diff --git a/.github/workflows/pytest-helpers.yml b/.github/workflows/helper-tests.yml similarity index 100% rename from .github/workflows/pytest-helpers.yml rename to .github/workflows/helper-tests.yml diff --git a/.github/workflows/black.yml b/.github/workflows/style-checks.yml similarity index 100% rename from .github/workflows/black.yml rename to .github/workflows/style-checks.yml From 9e675d9850c8cbbcbc8eb33375526eb533019eaa Mon Sep 17 00:00:00 2001 From: Joe Sheldon Date: Thu, 17 Nov 2022 17:04:14 -0700 Subject: [PATCH 13/17] build wheel action --- .github/workflows/build-wheel.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/build-wheel.yml diff --git a/.github/workflows/build-wheel.yml b/.github/workflows/build-wheel.yml new file mode 100644 index 0000000..602a721 --- /dev/null +++ b/.github/workflows/build-wheel.yml @@ -0,0 +1,31 @@ +on: + push: + branches: + - main + - develop + +jobs: + build_wheel: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Build wheel and install + run: | + python -m pip install --user --upgrade build + python -m build +# #pip install . +# find ./dist/*.whl | xargs pip install +# python simple_test.py +# - name: Configure Git +# run: | +# git config --global user.email "apwheele@gmail.com" +# git config --global user.name "apwheele" +# - name: Commit and push wheel +# run: | +# git add -f ./dist/*.whl +# git commit -m 'pushing new wheel' +# git push From 39d18deb702f7701165668ec172335b12a0d66da Mon Sep 17 00:00:00 2001 From: Joe Sheldon Date: Thu, 17 Nov 2022 17:06:14 -0700 Subject: [PATCH 14/17] build wheel action, upload dist files --- .github/workflows/build-wheel.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build-wheel.yml b/.github/workflows/build-wheel.yml index 602a721..784428b 100644 --- a/.github/workflows/build-wheel.yml +++ b/.github/workflows/build-wheel.yml @@ -17,6 +17,11 @@ jobs: run: | python -m pip install --user --upgrade build python -m build + - name: archive build artifacts + uses: actions/upload-artifact@v3 + with: + name: dist-files + path: dist/* # #pip install . # find ./dist/*.whl | xargs pip install # python simple_test.py From df350968f7709f1d9c31439b4c9ee01121e3b87b Mon Sep 17 00:00:00 2001 From: Joe Sheldon Date: Thu, 17 Nov 2022 17:22:28 -0700 Subject: [PATCH 15/17] badges --- README.md | 72 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index bc40a4b..68eb6c7 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,14 @@ # blueink-client-python +![Tests](https://github.com/blueinkhq/blueink-client-python/actions/workflows/helper-tests.yml/badge.svg) +![Style Checks](https://github.com/blueinkhq/blueink-client-python/actions/workflows/style-checks.yml/badge.svg) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) A Python client library for the BlueInk eSignature API. ## Overview This README provides a narrative overview of using the Blueink Python client, and -includes examples for many common use cases. +includes examples for many common use cases. Additional resources that might be useful include: @@ -31,7 +34,7 @@ pip install blueink-client-python Requests to the Blueink API are made via an instance of the `blueink.Client` class. The `blueink.Client` needs a Blueink private API key. By default the Client looks for -the private key in an environment variable named `BLUEINK_PRIVATE_API_KEY`. +the private key in an environment variable named `BLUEINK_PRIVATE_API_KEY`. ```bash # In your shell, or in .bashrc or similar @@ -61,8 +64,8 @@ exceptions whenever there's an issue between the client and server (eg. HTTP4xx, HTTP5xx errors). These come from the `requests` module. If within your application you already handle exceptions coming out of `requests` then you should be good. If you set `raise_exceptions = False` then these will be returned as -`NormalizedResponse` objects which are also used for successful communications. See -below for information about these objects. +`NormalizedResponse` objects which are also used for successful communications. See +below for information about these objects. ```python # In your python code, create an instance of the blueink Client @@ -84,16 +87,17 @@ for bundle in bundles: print(bundle.id) ``` -The Client class follows a RESTful pattern for making API calls, like so: +The Client class follows a RESTful pattern for making API calls, like so: `client.[resource].[method]`. The supported "resources" are: * `bundles` * `persons` - * `packets` + * `packets` * `templates` + * `webhooks` - The methods correspond to common REST operations: + The methods correspond to common REST operations: * `list()` * `retrieve()` * `create()` @@ -101,9 +105,9 @@ The supported "resources" are: * `delete()` However, note that: -* Not all resources support all methods. -* Some resources support one-off methods that are unique to that resource. - For example the `bundles` resource allows you to retrieve a list of events on +* Not all resources support all methods. +* Some resources support one-off methods that are unique to that resource. + For example the `bundles` resource allows you to retrieve a list of events on the Bundle by calling `client.bundles.list_events()`. Detailed documentation for each resource is below. @@ -115,18 +119,18 @@ the following attributes. * **response.data** - The json data returned via the API call is accessible via the `data` attribute. The - `data` attribute supports dictionary access and dot-notation access (for convenience + The json data returned via the API call is accessible via the `data` attribute. The + `data` attribute supports dictionary access and dot-notation access (for convenience and less typing). ```python response = client.bundles.retrieve("some bundle ID") - + bundle_data = response.data print(bundle_data['id']) # dictionary-style access print(bundle_data.id) # dot notation access ``` - + * **response.request** The request that led to this response. Under-the-hood, the Blueink client library @@ -135,19 +139,19 @@ the following attributes. * **response.original_response** - Similarly, if you need access to the original response as returned by + Similarly, if you need access to the original response as returned by the Python Requests library, it's accessible as `original_response`. * **response.pagination** - Most API calls that return a list of data returned paginated results. If so, - information about the pagination is included in the `pagination` attribute. + Most API calls that return a list of data returned paginated results. If so, + information about the pagination is included in the `pagination` attribute. Pagination Example: ```python response = client.persons.list() - + pagination = response.pagination print(pagination.page_number, ' - current page number') print(pagination.total_pages, ' - total pages') @@ -162,7 +166,7 @@ See "Requests that Return Lists > Pagination" below. #### Filtering and Searching Some Blueink [API endpoints](https://blueink.com/r/api-docs/) support searching and / or -filtering. In those cases, you can pass querystring parameters to the `list(...)` or +filtering. In those cases, you can pass querystring parameters to the `list(...)` or `paged_list(...)` method on those resources. For example: @@ -184,7 +188,7 @@ statuses = ",".join([ response = client.bundles.list(status__in=statuses) complete_or_started_bundles = response.data -# Retrieve Bundles matching a search of "example.com" (which will match signer email +# Retrieve Bundles matching a search of "example.com" (which will match signer email # addresses). We can pass pagination parameters too. response = client.bundles.list(per_page=10, page=2, search="example.com") matching_bundles = response.data @@ -199,19 +203,19 @@ for paged_response in iterator: #### Pagination Blueink API calls that return a list of results are paginated - ie, if there -are a lot of results, you need to make multiple requests to retrieve all of those -results, including a `page_number` parameter (and optionally a `page_size` parameter) +are a lot of results, you need to make multiple requests to retrieve all of those +results, including a `page_number` parameter (and optionally a `page_size` parameter) in each request. -The details of Blueink pagination scheme can be found in the +The details of Blueink pagination scheme can be found in the [API documentation](https://blueink.com/r/api-docs/pagination/): This client library provides convenience methods to make looping over paginated results easier. Whenever there is a `list()` method available for a resource, there is a corresponding `paged_list()` method available that returns a -`PaginatedIterator` helper class to make processing the results easier. +`PaginatedIterator` helper class to make processing the results easier. -You can mostly ignore the details of how the `PaginatedIterator` works. Instead, here +You can mostly ignore the details of how the `PaginatedIterator` works. Instead, here is an example of looping over a paginated set of Bundles: ```python @@ -227,13 +231,13 @@ for paged_response in iterator: ## Client Method Index Parameters can be found using autocomplete within your IDE. Creates/Updates take a -Python dictionary as the data field, unless special named methods like -```create_from_bundle_helper``` indicate otherwise. List methods can take query params +Python dictionary as the data field, unless special named methods like +```create_from_bundle_helper``` indicate otherwise. List methods can take query params as kwargs. ### Bundle Related * Create via ```client.bundles.create(...)``` or ```client.bundles.create_from_bundle_helper(...)``` -* List via ```client.bundles.list(...)``` or ```client.bundles.paged_list(...)``` +* List via ```client.bundles.list(...)``` or ```client.bundles.paged_list(...)``` * Retrieve via ```client.bundles.retrieve(...)``` * Cancel via ```client.bundles.cancel(...)``` * List Events via ```client.bundles.list_events(...)``` @@ -242,7 +246,7 @@ as kwargs. ### Person Related * Create via ```client.persons.create(...)``` or ```client.persons.create_from_person_helper(...)``` -* List via ```client.persons.list(...)``` or ```client.persons.paged_list(...)``` +* List via ```client.persons.list(...)``` or ```client.persons.paged_list(...)``` * Retrieve via ```client.persons.retrieve(...)``` * Delete via ```client.persons.delete(...)``` * Update via ```client.persons.update(...)``` @@ -254,7 +258,7 @@ as kwargs. * Remind via ```client.packets.remind(...)``` ### Template Related -* List via ```client.templates.list(...)``` or ```client.templates.paged_list(...)``` +* List via ```client.templates.list(...)``` or ```client.templates.paged_list(...)``` * Retrieve via ```client.templates.retrieve(...)``` ### Webhook Related @@ -287,8 +291,8 @@ as kwargs. #### Creating Bundles with the BundleHelper -When creating a Bundle via the API, you need to pass quite a bit of data in the -`client.bundle.create(...)` request. To ease the construction of that data, this +When creating a Bundle via the API, you need to pass quite a bit of data in the +`client.bundle.create(...)` request. To ease the construction of that data, this library provides a `BundleHelper` class. Below is an example of using `BundleHelper` to create a Bundle with 1 document, @@ -610,7 +614,7 @@ template_response = client.templates.retrieve(template_id) ``` ### Webhooks -Webhooks can be interacted with via several methods. Webhooks also have related objects, such as +Webhooks can be interacted with via several methods. Webhooks also have related objects, such as ```WebhookExtraHeaders```, ```WebhookEvents```, and ```WebhookDeliveries``` which have their own methods to interact with. @@ -707,4 +711,4 @@ try: except HTTPError as e: print(f"Error Deleting Webhooks {webhook.id}") print(e) -``` \ No newline at end of file +``` From 1249fc4af4ed9046129132d5a340bf7a71837551 Mon Sep 17 00:00:00 2001 From: Joe Sheldon Date: Thu, 17 Nov 2022 17:25:30 -0700 Subject: [PATCH 16/17] badges --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 68eb6c7..07c4302 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # blueink-client-python ![Tests](https://github.com/blueinkhq/blueink-client-python/actions/workflows/helper-tests.yml/badge.svg) ![Style Checks](https://github.com/blueinkhq/blueink-client-python/actions/workflows/style-checks.yml/badge.svg) -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +[![PyPI version](https://badge.fury.io/py/blueink-client-python.svg)](https://pypi.org/project/blueink-client-python/) +![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg) A Python client library for the BlueInk eSignature API. From 3e42694a5656585de773e29637fd29e4c174c7ab Mon Sep 17 00:00:00 2001 From: Joe Sheldon Date: Fri, 18 Nov 2022 15:29:58 -0700 Subject: [PATCH 17/17] Fix packaging issue --- .github/workflows/helper-tests.yml | 2 +- __init__.py | 0 setup.cfg | 2 +- src/__init__.py | 0 src/blueink/__init__.py | 8 ++++---- src/blueink/bundle_helper.py | 2 +- src/blueink/client.py | 14 +++++++------- src/blueink/model/bundles.py | 5 +++-- src/blueink/model/webhook.py | 5 +++-- src/blueink/paginator.py | 2 +- src/blueink/person_helper.py | 2 +- src/blueink/request_helper.py | 3 +-- src/blueink/subclients/bundle.py | 12 ++++++------ src/blueink/subclients/packet.py | 6 +++--- src/blueink/subclients/person.py | 10 +++++----- src/blueink/subclients/subclient.py | 4 ++-- src/blueink/subclients/template.py | 8 ++++---- src/blueink/subclients/webhook.py | 6 +++--- src/blueink/tests/test_bundle_helper.py | 7 ++----- src/blueink/tests/test_client.py | 6 +++--- src/blueink/tests/test_person_helper.py | 4 ++-- 21 files changed, 53 insertions(+), 55 deletions(-) delete mode 100644 __init__.py delete mode 100644 src/__init__.py diff --git a/.github/workflows/helper-tests.yml b/.github/workflows/helper-tests.yml index 61ada0c..c6b1e4e 100644 --- a/.github/workflows/helper-tests.yml +++ b/.github/workflows/helper-tests.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10"] steps: - uses: actions/checkout@v3 diff --git a/__init__.py b/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/setup.cfg b/setup.cfg index 9a97d1b..6993d4a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = blueink-client-python -version = 0.9.3 +version = 0.9.4 author = Blueink author_email = pypi@blueink.com description = Python Client for Blueink eSignature API diff --git a/src/__init__.py b/src/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/blueink/__init__.py b/src/blueink/__init__.py index bffa4c0..43671b0 100644 --- a/src/blueink/__init__.py +++ b/src/blueink/__init__.py @@ -1,8 +1,8 @@ from requests import exceptions -from .client import Client -from .bundle_helper import BundleHelper -from .person_helper import PersonHelper -from . import constants +import blueink.constants +from blueink.bundle_helper import BundleHelper +from blueink.client import Client +from blueink.person_helper import PersonHelper __all__ = ["Client", "BundleHelper", "PersonHelper", "exceptions", "constants"] diff --git a/src/blueink/bundle_helper.py b/src/blueink/bundle_helper.py index 0987ab0..c14ae53 100644 --- a/src/blueink/bundle_helper.py +++ b/src/blueink/bundle_helper.py @@ -3,7 +3,7 @@ from os.path import basename from typing import List -from .model.bundles import ( +from blueink.model.bundles import ( Bundle, Document, Field, diff --git a/src/blueink/client.py b/src/blueink/client.py index 9606027..867cb46 100644 --- a/src/blueink/client.py +++ b/src/blueink/client.py @@ -1,16 +1,16 @@ from os import environ -from src.blueink.constants import ( +from blueink.constants import ( DEFAULT_BASE_URL, ENV_BLUEINK_API_URL, ENV_BLUEINK_PRIVATE_API_KEY, ) -from src.blueink.request_helper import RequestHelper -from src.blueink.subclients.bundle import BundleSubClient -from src.blueink.subclients.packet import PacketSubClient -from src.blueink.subclients.person import PersonSubClient -from src.blueink.subclients.template import TemplateSubClient -from src.blueink.subclients.webhook import WebhookSubClient +from blueink.request_helper import RequestHelper +from blueink.subclients.bundle import BundleSubClient +from blueink.subclients.packet import PacketSubClient +from blueink.subclients.person import PersonSubClient +from blueink.subclients.template import TemplateSubClient +from blueink.subclients.webhook import WebhookSubClient class Client: diff --git a/src/blueink/model/bundles.py b/src/blueink/model/bundles.py index 029cfb3..b8b9f61 100644 --- a/src/blueink/model/bundles.py +++ b/src/blueink/model/bundles.py @@ -1,9 +1,10 @@ import random import string from typing import List, Optional -from pydantic import BaseModel, validator, EmailStr -from ..constants import DELIVER_VIA, FIELD_KIND +from pydantic import BaseModel, EmailStr, validator + +from blueink.constants import DELIVER_VIA, FIELD_KIND class ValidationError(RuntimeError): diff --git a/src/blueink/model/webhook.py b/src/blueink/model/webhook.py index 630c95b..9420a8e 100644 --- a/src/blueink/model/webhook.py +++ b/src/blueink/model/webhook.py @@ -1,7 +1,8 @@ from typing import List, Optional -from pydantic import BaseModel, validator, Field -from src.blueink.constants import EVENT_TYPE +from pydantic import BaseModel, Field, validator + +from blueink.constants import EVENT_TYPE class WebhookExtraHeaderSchema(BaseModel): diff --git a/src/blueink/paginator.py b/src/blueink/paginator.py index 18dad2b..8ac51a7 100644 --- a/src/blueink/paginator.py +++ b/src/blueink/paginator.py @@ -1,6 +1,6 @@ from requests.exceptions import HTTPError -from .request_helper import NormalizedResponse +from blueink.request_helper import NormalizedResponse class PaginatedIterator: diff --git a/src/blueink/person_helper.py b/src/blueink/person_helper.py index c316015..b189b7b 100644 --- a/src/blueink/person_helper.py +++ b/src/blueink/person_helper.py @@ -1,6 +1,6 @@ from typing import List -from .model.persons import ContactChannelSchema, PersonSchema +from blueink.model.persons import ContactChannelSchema, PersonSchema class PersonHelper: diff --git a/src/blueink/request_helper.py b/src/blueink/request_helper.py index 6b54667..b39a687 100644 --- a/src/blueink/request_helper.py +++ b/src/blueink/request_helper.py @@ -1,9 +1,8 @@ import requests - from munch import munchify from requests import Request, Session -from .constants import BLUEINK_PAGINATION_HEADER +from blueink.constants import BLUEINK_PAGINATION_HEADER class Pagination: diff --git a/src/blueink/subclients/bundle.py b/src/blueink/subclients/bundle.py index e70ef07..ed41ac3 100644 --- a/src/blueink/subclients/bundle.py +++ b/src/blueink/subclients/bundle.py @@ -4,12 +4,12 @@ from munch import Munch -from src.blueink import endpoints -from src.blueink.bundle_helper import BundleHelper -from src.blueink.constants import BUNDLE_STATUS -from src.blueink.paginator import PaginatedIterator -from src.blueink.request_helper import NormalizedResponse -from src.blueink.subclients.subclient import SubClient +from blueink import endpoints +from blueink.bundle_helper import BundleHelper +from blueink.constants import BUNDLE_STATUS +from blueink.paginator import PaginatedIterator +from blueink.request_helper import NormalizedResponse +from blueink.subclients.subclient import SubClient class BundleSubClient(SubClient): diff --git a/src/blueink/subclients/packet.py b/src/blueink/subclients/packet.py index b8ff17a..06cc7c8 100644 --- a/src/blueink/subclients/packet.py +++ b/src/blueink/subclients/packet.py @@ -1,6 +1,6 @@ -from src.blueink import endpoints -from src.blueink.request_helper import NormalizedResponse -from src.blueink.subclients.subclient import SubClient +from blueink import endpoints +from blueink.request_helper import NormalizedResponse +from blueink.subclients.subclient import SubClient class PacketSubClient(SubClient): diff --git a/src/blueink/subclients/person.py b/src/blueink/subclients/person.py index 2904235..6189c4b 100644 --- a/src/blueink/subclients/person.py +++ b/src/blueink/subclients/person.py @@ -1,8 +1,8 @@ -from src.blueink import endpoints -from src.blueink.paginator import PaginatedIterator -from src.blueink.person_helper import PersonHelper -from src.blueink.request_helper import NormalizedResponse -from src.blueink.subclients.subclient import SubClient +from blueink import endpoints +from blueink.paginator import PaginatedIterator +from blueink.person_helper import PersonHelper +from blueink.request_helper import NormalizedResponse +from blueink.subclients.subclient import SubClient class PersonSubClient(SubClient): diff --git a/src/blueink/subclients/subclient.py b/src/blueink/subclients/subclient.py index ed67650..13f9523 100644 --- a/src/blueink/subclients/subclient.py +++ b/src/blueink/subclients/subclient.py @@ -1,5 +1,5 @@ -from src.blueink import endpoints -from src.blueink.request_helper import RequestHelper +from blueink import endpoints +from blueink.request_helper import RequestHelper class SubClient: diff --git a/src/blueink/subclients/template.py b/src/blueink/subclients/template.py index 961d943..0613b9a 100644 --- a/src/blueink/subclients/template.py +++ b/src/blueink/subclients/template.py @@ -1,7 +1,7 @@ -from src.blueink import endpoints -from src.blueink.paginator import PaginatedIterator -from src.blueink.request_helper import NormalizedResponse -from src.blueink.subclients.subclient import SubClient +from blueink import endpoints +from blueink.paginator import PaginatedIterator +from blueink.request_helper import NormalizedResponse +from blueink.subclients.subclient import SubClient class TemplateSubClient(SubClient): diff --git a/src/blueink/subclients/webhook.py b/src/blueink/subclients/webhook.py index bda2664..1adac16 100644 --- a/src/blueink/subclients/webhook.py +++ b/src/blueink/subclients/webhook.py @@ -1,6 +1,6 @@ -from src.blueink import endpoints -from src.blueink.request_helper import NormalizedResponse -from src.blueink.subclients.subclient import SubClient +from blueink import endpoints +from blueink.request_helper import NormalizedResponse +from blueink.subclients.subclient import SubClient class WebhookSubClient(SubClient): diff --git a/src/blueink/tests/test_bundle_helper.py b/src/blueink/tests/test_bundle_helper.py index 9c2dc35..0d4dbd4 100644 --- a/src/blueink/tests/test_bundle_helper.py +++ b/src/blueink/tests/test_bundle_helper.py @@ -1,12 +1,9 @@ import copy -from base64 import b64encode -from os.path import basename from munch import Munch -from src.blueink import BundleHelper, Client -from src.blueink.person_helper import PersonHelper -from src.blueink.utils.testcase import TestCase +from blueink.bundle_helper import BundleHelper +from blueink.utils.testcase import TestCase class TestBundleHelper(TestCase): diff --git a/src/blueink/tests/test_client.py b/src/blueink/tests/test_client.py index cc9330d..67a5ff4 100644 --- a/src/blueink/tests/test_client.py +++ b/src/blueink/tests/test_client.py @@ -5,9 +5,9 @@ from munch import Munch -from src.blueink import BundleHelper, Client, PersonHelper -from src.blueink.constants import EVENT_TYPE -from src.blueink.utils.testcase import TestCase +from blueink import BundleHelper, Client, PersonHelper +from blueink.constants import EVENT_TYPE +from blueink.utils.testcase import TestCase class TestClient(TestCase): diff --git a/src/blueink/tests/test_person_helper.py b/src/blueink/tests/test_person_helper.py index 1ac1329..7939029 100644 --- a/src/blueink/tests/test_person_helper.py +++ b/src/blueink/tests/test_person_helper.py @@ -1,5 +1,5 @@ -from src.blueink.person_helper import PersonHelper -from src.blueink.utils.testcase import TestCase +from blueink.person_helper import PersonHelper +from blueink.utils.testcase import TestCase class TestPersonHelper(TestCase):