diff --git a/.cursor/rules/comment-standards.mdc b/.cursor/rules/comment-standards.mdc new file mode 100644 index 0000000..4cd4584 --- /dev/null +++ b/.cursor/rules/comment-standards.mdc @@ -0,0 +1,25 @@ +--- +description: Standards for code comments in the SDK +alwaysApply: true +--- + +# Comment Standards + +Never label comments with process artifacts like "BUG FIX", "HACK", "WORKAROUND", "HOTFIX", or similar. Comments should explain *why* the code is the way it is, not narrate the history of how it got there. The git history already tracks that. + +```python +# BAD +# BUG FIX: was using snake_case keys, JSON API uses camelCase +attributes.get("yearOfIncorporation") + +# BAD +# WORKAROUND: the API returns null sometimes +value = attributes.get("field") or default + +# GOOD (no comment needed if the code is self-explanatory) +attributes.get("yearOfIncorporation") + +# GOOD (explains a non-obvious decision) +# Unit's JSON API returns null for this field on v1 responses +value = attributes.get("field") or default +``` diff --git a/.cursor/rules/keyword-arguments.mdc b/.cursor/rules/keyword-arguments.mdc new file mode 100644 index 0000000..a46f0ac --- /dev/null +++ b/.cursor/rules/keyword-arguments.mdc @@ -0,0 +1,29 @@ +--- +description: Always use keyword arguments when calling constructors and factory methods +globs: unit/**/*.py +alwaysApply: false +--- + +# Keyword Arguments Required + +Always use keyword arguments when calling constructors (`__init__`) and factory methods (e.g. `from_json_api`) in this SDK. Positional arguments make it easy to silently pass the wrong value when parameter lists are long. + +```python +# BAD — positional arguments +return IndividualApplicationDTO( + _id, + date_utils.to_datetime(attributes["createdAt"]), + FullName.from_json_api(attributes["fullName"]), + attributes["status"], +) + +# GOOD — keyword arguments +return IndividualApplicationDTO( + id=_id, + created_at=date_utils.to_datetime(attributes["createdAt"]), + full_name=FullName.from_json_api(attributes["fullName"]), + status=attributes["status"], +) +``` + +This applies to all DTO classes, request classes, and any other class instantiation in the SDK. diff --git a/unit/models/application.py b/unit/models/application.py index 853c697..e0a047f 100644 --- a/unit/models/application.py +++ b/unit/models/application.py @@ -302,6 +302,14 @@ def __init__( business_vertical: Optional[BusinessVertical], tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]], + # V2 response fields + account_purpose: Optional[str] = None, + source_of_funds: Optional[str] = None, + transaction_volume: Optional[str] = None, + business_industry: Optional[str] = None, + is_incorporated: Optional[bool] = None, + website: Optional[str] = None, + countries_of_operation: Optional[List[str]] = None, ): self.id = id self.type = "individualApplication" @@ -321,29 +329,44 @@ def __init__( "soleProprietorship": sole_proprietorship, "businessVertical": business_vertical, "tags": tags, + # V2 fields + "accountPurpose": account_purpose, + "sourceOfFunds": source_of_funds, + "transactionVolume": transaction_volume, + "businessIndustry": business_industry, + "isIncorporated": is_incorporated, + "website": website, + "countriesOfOperation": countries_of_operation, } self.relationships = relationships @staticmethod def from_json_api(_id, _type, attributes, relationships): return IndividualApplicationDTO( - _id, - date_utils.to_datetime(attributes["createdAt"]), - FullName.from_json_api(attributes["fullName"]), - Address.from_json_api(attributes["address"]), - date_utils.to_date(attributes["dateOfBirth"]), - attributes["email"], - Phone.from_json_api(attributes["phone"]), - attributes["status"], - attributes.get("ssn"), - attributes.get("message"), - attributes.get("ip"), - attributes.get("ein"), - attributes.get("dba"), - attributes.get("soleProprietorship"), - attributes.get("businessVertical"), - attributes.get("tags"), - relationships, + id=_id, + created_at=date_utils.to_datetime(attributes["createdAt"]), + full_name=FullName.from_json_api(attributes["fullName"]), + address=Address.from_json_api(attributes["address"]), + date_of_birth=date_utils.to_date(attributes["dateOfBirth"]), + email=attributes["email"], + phone=Phone.from_json_api(attributes["phone"]), + status=attributes["status"], + ssn=attributes.get("ssn"), + message=attributes.get("message"), + ip=attributes.get("ip"), + ein=attributes.get("ein"), + dba=attributes.get("dba"), + sole_proprietorship=attributes.get("soleProprietorship"), + business_vertical=attributes.get("businessVertical"), + tags=attributes.get("tags"), + relationships=relationships, + account_purpose=attributes.get("accountPurpose"), + source_of_funds=attributes.get("sourceOfFunds"), + transaction_volume=attributes.get("transactionVolume"), + business_industry=attributes.get("businessIndustry"), + is_incorporated=attributes.get("isIncorporated"), + website=attributes.get("website"), + countries_of_operation=attributes.get("countriesOfOperation"), ) @@ -376,6 +399,13 @@ def __init__( operating_address: Optional[Address], tags: Optional[Dict[str, str]], relationships: Optional[Dict[str, Relationship]], + # V2 response fields + account_purpose: Optional[str] = None, + source_of_funds: Optional[str] = None, + transaction_volume: Optional[str] = None, + business_industry: Optional[str] = None, + business_description: Optional[str] = None, + is_regulated: Optional[bool] = None, ): self.id = id self.type = "businessApplication" @@ -404,38 +434,56 @@ def __init__( "beneficialOwners": beneficial_owners, "operatingAddress": operating_address, "tags": tags, + # V2 fields + "accountPurpose": account_purpose, + "sourceOfFunds": source_of_funds, + "transactionVolume": transaction_volume, + "businessIndustry": business_industry, + "businessDescription": business_description, + "isRegulated": is_regulated, } self.relationships = relationships @staticmethod def from_json_api(_id, _type, attributes, relationships): + operating_address = ( + Address.from_json_api(attributes["operatingAddress"]) + if attributes.get("operatingAddress") + else None + ) return BusinessApplicationDTO( - _id, - date_utils.to_datetime(attributes["createdAt"]), - attributes.get("name"), - Address.from_json_api(attributes["address"]), - Phone.from_json_api(attributes["phone"]), - attributes["status"], - attributes.get("stateOfIncorporation"), - attributes.get("entityType"), - BusinessContact.from_json_api(attributes["contact"]), - Officer.from_json_api(attributes["officer"]), - BeneficialOwner.from_json_api(attributes["beneficialOwners"]), - attributes.get("ssn"), - attributes.get("message"), - attributes.get("ip"), - attributes.get("ein"), - attributes.get("dba"), - attributes.get("website"), - attributes.get("year_of_incorporation"), - attributes.get("business_vertical"), - attributes.get("annual_revenue"), - attributes.get("number_of_employees"), - attributes.get("cash_flow"), - attributes.get("countries_of_operation"), - attributes.get("operating_address"), - attributes.get("tags"), - relationships, + id=_id, + created_at=date_utils.to_datetime(attributes["createdAt"]), + name=attributes.get("name"), + address=Address.from_json_api(attributes["address"]), + phone=Phone.from_json_api(attributes["phone"]), + status=attributes["status"], + state_of_incorporation=attributes.get("stateOfIncorporation"), + entity_type=attributes.get("entityType"), + contact=BusinessContact.from_json_api(attributes["contact"]), + officer=Officer.from_json_api(attributes["officer"]), + beneficial_owners=BeneficialOwner.from_json_api(attributes["beneficialOwners"]), + ssn=attributes.get("ssn"), + message=attributes.get("message"), + ip=attributes.get("ip"), + ein=attributes.get("ein"), + dba=attributes.get("dba"), + website=attributes.get("website"), + year_of_incorporation=attributes.get("yearOfIncorporation"), + business_vertical=attributes.get("businessVertical"), + annual_revenue=attributes.get("annualRevenue"), + number_of_employees=attributes.get("numberOfEmployees"), + cash_flow=attributes.get("cashFlow"), + countries_of_operation=attributes.get("countriesOfOperation"), + operating_address=operating_address, + tags=attributes.get("tags"), + relationships=relationships, + account_purpose=attributes.get("accountPurpose"), + source_of_funds=attributes.get("sourceOfFunds"), + transaction_volume=attributes.get("transactionVolume"), + business_industry=attributes.get("businessIndustry"), + business_description=attributes.get("businessDescription"), + is_regulated=attributes.get("isRegulated"), )