diff --git a/README.md b/README.md index be155a0..49f7ecf 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Root’s developers and our community are expected to abide by the [Contributor ``` pip install rootsdk ``` -or +or for active development ``` git clone git@github.com:root-community/root-insurance-python.git pip install -e root-insurance-python @@ -34,16 +34,15 @@ pip install -e root-insurance-python ## Environment Variables ``` -ROOT_APP_ID -ROOT_APP_SECRET +ROOT_API_KEY ``` ## Code ```python -from root import insurance +from root.insurance import InsuranceClient,GadgetCover -client = insurance.Client() +client = InsuranceClient(cover=GadgetCover) phone_brands = client.gadgets.list_phone_brands() ``` diff --git a/env.sh b/env.sh index c7619f5..1ffbd8c 100644 --- a/env.sh +++ b/env.sh @@ -1,3 +1,2 @@ # ENV variables for app -export ROOT_APP_SECRET="__replace__"; -export ROOT_APP_ID="__replace__"; +export ROOT_API_KEY="__replace__"; diff --git a/example.py b/example.py new file mode 100644 index 0000000..d4c79fd --- /dev/null +++ b/example.py @@ -0,0 +1,45 @@ +from pprint import pprint +from root.insurance import InsuranceClient, GadgetCover, Policyholder, Beneficiary + + +def main(): + # 0. Create an insurance client + client = InsuranceClient(cover=GadgetCover) + phone = "iPhone 6 Plus 128GB LTE" + phone_value = client.quotes.get_phone_value(phone) + print("The value of a {} is R{}".format(phone, phone_value)) + # 1. Issue a quote for the thing to be insured. + quotes_data = client.quotes.generate(model_name=phone) + print("The quotes for {} are".format(phone)) + pprint(quotes_data) + + # 2. Create a policy holder + policyholder = Policyholder(Policyholder.identification("id", "6801015800084", "ZA"), "Erlich", "Bachman") + policyholder_data = client.policyholders.create(policyholder=policyholder) + print("Person holding policy is") + pprint(policyholder_data) + # 3. Create an application. For this, a generated quote is required. + chosen_quote = quotes_data[0] + chosen_quote_id = chosen_quote['quote_package_id'] + policyholder_id = policyholder_data['policyholder_id'] + application_data = client.applications.create(policyholder_id=policyholder_id, + quote_package_id=chosen_quote_id, + monthly_premium=chosen_quote['suggested_premium'], + serial_number='random_serial') + print("Application data to insure {} is ".format(phone)) + pprint(application_data) + # 4. Issue policy, if application is approved (this check is not done here) + policy_data = client.policies.issue(application_id=application_data['application_id']) + print("Congratulations, your policy looks as follows:") + pprint(policy_data) + # 5. Add beneficiaries (if supported, which root_gadgets do not) + # updated_policy_data = client.policies.add_beneficiary(policy_data['policy_id'], + # [Beneficiary( + # Policyholder.identification("id", "6801015800084", "ZA"), + # "Erlich", "Bachman", percentage=100.0)]) + # 6. Link credit card + # See API docs https://app.root.co.za/docs/insurance/api#linking-credit-card + + +if __name__ == "__main__": + main() diff --git a/root/exceptions.py b/root/exceptions.py deleted file mode 100644 index 09d6c4a..0000000 --- a/root/exceptions.py +++ /dev/null @@ -1,12 +0,0 @@ -class RootException(Exception): - """Base class for exceptions in this module.""" - pass - -class RootCredentialsException(RootException): - """Raised when library/wrapper is used without credentials set in the env variables. - - Attributes: - message -- explanation of why the specific transition is not allowed - """ - def __init__(self, message="No APP_ID and APP_SECRET set in environment variables"): - pass diff --git a/root/insurance.py b/root/insurance.py deleted file mode 100644 index 61507c4..0000000 --- a/root/insurance.py +++ /dev/null @@ -1,240 +0,0 @@ -import requests -import logging -import os -from .exceptions import RootCredentialsException - -logging.basicConfig(level=logging.DEBUG) - -class Client: - def __init__(self): - self.sandBox = os.environ.get('ROOT_SANDBOX', True) - self.production = False if self.sandBox else True - self.prodUrl = "https://api.root.co.za/v1/insurance" - self.baseURL = "https://sandbox.root.co.za/v1/insurance" if self.sandBox else self.prodUrl - self.appSecret = os.environ.get('ROOT_APP_SECRET') if not self.sandBox else "" - self.appID = os.environ.get('ROOT_APP_ID') if not self.sandBox else os.environ.get('ROOT_APP_SECRET') - self.applications = Applications(self) - self.claims = Claims(self) - self.policyholders = PolicyHolders(self) - self.policies = Policies(self) - self.gadgets = Gadgets(self) - self.quotes = Quotes(self) - if not self.appID and not self.appSecret: - raise RootCredentialsException - print("[WARNING] Running in production mode: {mode}".format(mode=self.production)) - - def call(self, method, path, params=None, **kwargs): - resp = requests.request(method, - f'{self.baseURL}/{path}', - params=params, - headers={"Content-Type": "application/json"}, - auth=(self.appID, self.appSecret), - **kwargs - ) - if resp.status_code == 200 or resp.status_code == 201: - return resp.json() - raise Exception(resp.status_code, resp.json()) - - -class Resource: - def __init__(self, client): - self.client = client - - def call(self, method, path, params=None, **kwargs): - return self.client.call(method, path, params, **kwargs) - - -class Applications(Resource): - def __init__(self, client): - super().__init__(client) - - def create(self, policyholder_id, quote_package_id, monthly_premium, serial_number=None): - data = { - "policyholder_id": policyholder_id, - "quote_package_id": quote_package_id, - "monthly_premium": monthly_premium, - "serial_number": serial_number - } - return self.call("post", "applications", json=data) - - -class Claims(Resource): - def __init__(self, client): - super().__init__(client) - - def list(self, status=None, approval=None): - params = {} - if status: - params["claim_status"] = status - params = {} - if approval: - params["approval_status"] = approval - - return self.call("get", "claims", params=params) - - def get(self, id): - return self.call("get", f'claims/{id}') - - def open(self, policy_id=None, policy_holder_id=None): - data = { - "policy_id": policy_id, - "policy_holder_id": policy_holder_id - } - return self.call("post", "claims", json=data) - - def link_policy(self, claim_id, policy_id): - data = { - "policy_id": policy_id - } - return self.call("post", f'claims/{claim_id}/policy', json=data) - - def link_policy_holder(self, claim_id, policy_holder_id): - data = { - "policy_holder_id": policy_holder_id - } - return self.call("post", f'claims/{claim_id}/policyholder', json=data) - - def link_events(self, claim_id): - return self.call("post", f'claims/{claim_id}/events') - - - -class PolicyHolders(Resource): - def __init__(self, client): - super().__init__(client) - - def create(self, id, first_name, last_name, email=None, date_of_birth=None, cellphone=None): - data = { - "id": id, - "first_name": first_name, - "last_name": last_name, - "date_of_birth": date_of_birth, - "email": email, - "cellphone": cellphone - } - return self.call("post", "policyholders", json=data) - - def list(self): - return self.call("get", "policyholders") - - def get(self, id): - return self.call("get", f'policyholders/{id}') - - def update(self, id, email=None, cellphone=None): - data = { - "email": email, - "cellphone": cellphone - } - return self.call("patch", f'policyholders/{id}', json=data) - - def list_events(self, id): - return self.call("get", f'policyholders/{id}/events') - - -class Policies(Resource): - def __init__(self, client): - super().__init__(client) - - def issue(self, application_id): - data = { - "application_id": application_id, - } - return self.call("post", "policies", json=data) - - def add_beneficiary(self, policy_id, beneficiary_id, first_name, last_name, percentage): - data = { - "id": beneficiary_id, - "first_name": first_name, - "last_name": last_name, - "percentage": percentage - } - return self.call("put", f'policies/{policy_id}/beneficiaries', json=data) - - def list(self): - return self.call("get", "policies") - - def get(self, policy_id): - return self.call("get", f'policies/{policy_id}') - - def cancel(self, policy_id, reason): - data = {"reason": reason} - return self.call("post", f'policies/{policy_id}/cancel', json=data) - - def replace(self, policy_id, quote_package_id): - data = {"quote_package_id": quote_package_id} - return self.call("post", f'policies/{policy_id}/replace', json=data) - - def update_billing_amount(self, policy_id, billing_amount): - data = {"billing_amount": billing_amount} - return self.call("post", f'policies/{policy_id}/billing_amount', json=data) - - def list_beneficiaries(self, policy_id): - return self.call("get", f'policies/{policy_id}/beneficiaries') - - def list_events(self, policy_id): - return self.call("get", f'policies/{policy_id}/events') - - -class Quotes(Resource): - def __init__(self, client): - super().__init__(client) - - def create(self, opts): - data = {} - type_ = opts["type"] - if type_ == "root_gadgets": - data = self._gadget_quote(opts) - elif type_ == "root_term": - data = self._term_quote(opts) - elif type_ == "root_funeral": - data = self._funeral_quote(opts) - else: - raise Exception("invalid quote type") - return self.call("post", "quotes", json=data) - - def _gadget_quote(self, opts): - return { - "type": "root_gadgets", - "model_name": opts["model_name"] - } - - def _term_quote(self, opts): - return { - "type": "root_term", - "cover_amount": opts["cover_amount"], - "cover_period": opts["cover_period"], - "education_status": opts["education_status"], - "smoker": opts["smoker"], - "gender": opts["gender"], - "age": opts["age"], - "basic_income_per_month": opts["basic_income_per_month"], - } - - def _funeral_quote(self, opts): - return { - "type": "root_funeral", - "cover_amount": opts["cover_amount"], - "has_spouse": opts["has_spouse"], - "number_of_children": opts["number_of_children"], - "extended_family_ages": opts["extended_family_ages"] - } - - -class Gadgets(Resource): - def __init__(self, client): - super().__init__(client) - - def list_models(self): - return self.call("get", "gadgets/models") - - def list_phone_brands(self): - models = self.list_models() - return set([phone['make'] for phone in models]) - - def list_phones_by_brand(self, brand): - models = self.list_models() - return set([phone['name'] for phone in models if phone['make'] == brand]) - - def get_phone_value(self, phone): - models = self.list_models() - return list(filter(lambda p: p['name'] == phone, models))[0]['value']/100 diff --git a/root/insurance/__init__.py b/root/insurance/__init__.py new file mode 100644 index 0000000..71f585b --- /dev/null +++ b/root/insurance/__init__.py @@ -0,0 +1,3 @@ +from root.insurance.insurance import InsuranceClient +from root.insurance.policyholder import Policyholder, Beneficiary +from root.insurance.resources import GadgetCover, TermCover, FuneralCover diff --git a/root/insurance/exceptions.py b/root/insurance/exceptions.py new file mode 100644 index 0000000..a065075 --- /dev/null +++ b/root/insurance/exceptions.py @@ -0,0 +1,34 @@ +class RootException(Exception): + """Base class for exceptions in this module.""" + pass + + +class RootCredentialsException(RootException): + """Raised when library/wrapper is used without credentials set in the env variables. + + Attributes: + message -- explanation of why the specific transition is not allowed + """ + + def __init__(self, message="No ROOT_API_KEY set in environment variables"): + print(message) + + +class RootIdentificationException(RootException): + """Raised when given identification is not syntactically correct + Attributes: + message -- explanation of why syntax was wrong + """ + + def __init__(self, message="identification provided was ill-formed"): + print(message) + + +class RootInsufficientDataException(RootException): + """Raised when not enough data is given to a function/method/api call + Attributes: + message -- explanation of why the data was not fully-formed + """ + + def __init__(self, message="not all data fields present"): + print(message) diff --git a/root/insurance/insurance.py b/root/insurance/insurance.py new file mode 100644 index 0000000..6916e21 --- /dev/null +++ b/root/insurance/insurance.py @@ -0,0 +1,83 @@ +import requests +import logging +import os +from root.insurance.exceptions import RootCredentialsException +from root.insurance.policyholder import Policyholder +from root.insurance.resources import GadgetCover, Applications, Claims, PolicyHolders, Policies + +logging.basicConfig(level=logging.DEBUG) + + +class InsuranceClient(object): + def __init__(self, api_key=False, cover=GadgetCover): + """Provide an interface to access the Root Insurance API. + + With an interface, client=InsuranceClient(), one can get + 1. Quotes (client.quotes), such as for GadgetCover + 2. Policy Holders (client.policyholders) + 3. Applications (client.applications) + 4. Policies (client.policies) + + To issue a policy follow these steps: + 0. Create an insurance client + >>> client = InsuranceClient() + 1. Issue a quote for the thing to be insured. + >>> quotes_data = client.quotes.generate(model_name="iPhone 6s 64GB LTE") + 2. Create a policy holder + >>> policyholder = Policyholder(Policyholder.identification(...),...) + >>> policyholder_data = client.policyholders.create(policyholder=policyholder) + 3. Create an application. For this, a generated quote is required. + >>> chosen_quote = quotes_data[0] + >>> chosen_quote_id = chosen_quote['quote_package_id'] + >>> policyholder_id = policyholder_data['policyholder_id'] + >>> application_data = client.applications.create(policyholder_id=policyholder_id, + quote_package_id=chosen_quote_id, + monthly_premium=chosen_quote['suggested_premium'], + serial_number='random_serial') + 4. Issue policy, if application is approved (this check is not done here) + >>> policy_data = client.policies.issue(application_id=application_data['application_id']) + 5. Add beneficiaries + >>> updated_policy_data = client.policies.add_beneficiary(policy_data['policy_id'], + [Beneficiary(percentage=100.0,...)]) + 6. Link credit card + See API docs https://app.root.co.za/docs/insurance/api#linking-credit-card + + + :param api_key: for those struggling with ENVIRONMENT VARIABLES, the key can be passed directly + WARNING: do *NOT* make the API_KEY publicly visible + """ + self.sandbox = os.environ.get('ROOT_SANDBOX', True) + self.production = False if self.sandbox else True + self.prod_url = "https://api.root.co.za/v1/insurance" + self.base_url = "https://sandbox.root.co.za/v1/insurance" if self.sandbox else self.prod_url + self.api_key = os.environ.get('ROOT_API_KEY', api_key) + self.applications = Applications(self) + self.claims = Claims(self) + self.policyholders = PolicyHolders(self) + self.policies = Policies(self) + self.quotes = cover(self) + if not self.api_key: + raise RootCredentialsException + print("[{warning}] Running in production mode: {mode}".format(warning="WARNING" if self.production else "INFO", + mode=self.production)) + + def call(self, method: str, path: str, params: str = None, **kwargs): + """Send a request to the Root API. + :param method: any method supported by requests lib. + :param path: url of api from root.co.za/{version}/insurance/{path} + :param params: optional params + :param kwargs: other keywords of note include 'json' for payload + :return: JSON-like response + """ + resp = requests.request(method, + '{base_url}/{path}'.format(base_url=self.base_url, path=path), + params=params, + headers={"Content-Type": "application/json"}, + auth=(self.api_key, ""), + **kwargs + ) + if resp.status_code == requests.codes.ok: + return resp.json() + logging.error('{} {}'.format(resp.status_code, resp.text)) + if self.sandbox: + resp.raise_for_status() diff --git a/root/insurance/policyholder.py b/root/insurance/policyholder.py new file mode 100644 index 0000000..5c8f737 --- /dev/null +++ b/root/insurance/policyholder.py @@ -0,0 +1,66 @@ +from root.insurance.exceptions import RootIdentificationException + + +class Policyholder(object): + """An object for a person's details in the format the Root Insurance API wants + A person's ID, first name, and last name are always required. + + Example: + + # create object + >>> person = Policyholder(Policyholder.identification('id','6801015800084','ZA'),...) + # send in correct format using + >>> data = person.details() + >>> data['extra'] = 'extra details' + >>> from root.resources import Resource + >>> Resource.call(json=data) + + """ + + def __init__(self, id_object, first_name, last_name, date_of_birth=None, gender=None, email=None, cellphone=None, + *args, **kwargs): + if not ('type' in id_object and 'number' in id_object and 'country' in id_object): + raise RootIdentificationException() + + self._details = {'id': id_object, + 'first_name': first_name, + 'last_name': last_name} + if date_of_birth is not None: + self._details['date_of_birth'] = date_of_birth + if gender is not None: + self._details['gender'] = gender + if email is not None: + self._details['email'] = email + if cellphone is not None: + self._details['cellphone'] = cellphone + for kw, val in kwargs.items(): + self._details[kw] = val + + @staticmethod + def identification(id_type: str, id_num: str, country_code: str): + """Helper method to correctly form the 'id' object dict + + Example: + person = Policyholder(Policyholder.identification('id','6801015800084','ZA'),...) + + API docs: + type string. Either 'id' or 'passport'. + number string. The ID or passport number. + country string. The ISO Alpha-2 country code of the country of the id/passport number. + """ + if id_type != 'id' and id_type != 'passport': + raise RootIdentificationException("identification id_type was neither 'id' nor 'passport'") + assert len(country_code) == 2 + return {'type': id_type, + 'number': id_num, + 'country': country_code} + + def details(self): + return self._details + + +class Beneficiary(Policyholder): + def __init__(self, id_object, first_name, last_name, percentage=0.0, *args, **kwargs): + super().__init__(id_object, first_name, last_name, *args, **kwargs) + assert 0 <= percentage <= 100 + self._details['percentage'] = percentage diff --git a/root/insurance/resources.py b/root/insurance/resources.py new file mode 100644 index 0000000..64c4c8b --- /dev/null +++ b/root/insurance/resources.py @@ -0,0 +1,232 @@ +from root.insurance.exceptions import RootInsufficientDataException +from root.insurance.policyholder import Policyholder, Beneficiary + + +class Resource(object): + """Super class for resources within the API + Each resource should be its own sub-class which is then instantiated within the main InsuranceClient class, thus + the InsuranceClient remains the interface for the API + Each sub-class can conveniently set a default REST method for API calls, as well as it's hierarchical URL path + There are a number of possible ways to validate data (at UI, in a new class (such as Policyholder), + in a Resource sub-class explicitly, or in sub-class by overriding validate method). + The choice shall be left up to the user depending on requirements. + """ + + def __init__(self, client, method: str = 'get', path: str = ''): + self.client = client + self.method = method + self.path = path + + def call(self, method=None, sub_path="", params=None, path=None, **kwargs): + if method is None: + method = self.method + if path is None: + path = "{}/{}".format(self.path, sub_path) if sub_path != "" else self.path + return self.client.call(method, path, params, **kwargs) + + +class Quotes(Resource): + """Super class for generating quotes of the insurance product. Sub-classes can be bare-bones with just a simple + init method. But can be usefully expanded to have extra useful features for the user, see GadgetCover. + """ + def __init__(self, client, module_id: str, data_fields: list): + super().__init__(client, "post", "quotes") + if "type" not in data_fields: + data_fields.insert(0, "type") + self.data_fields = set(data_fields) + self.module_id = module_id + + def generate(self, data: dict = None, **kwargs) -> dict: + """Generate a quote using either a dictionary or keyword arguments as data + (using both will merge them keyword precedence). + Raises a `RootInsufficientDataException` if not all required fields were provided. E + xtra fields are ignored by the API + :param data: data required for the quote in dictionary form + :param kwargs: data required for the quote in keyword form + :return: A quote in dictionary format + """ + if data is None: + data = {} + for kw, val in kwargs.items(): + data[kw] = val + if "type" not in data: + data["type"] = self.module_id + if self.data_fields < set(data): + raise RootInsufficientDataException() + return self.call(json=data) + + +class GadgetCover(Quotes): + """Cover for smartphones. + All that is required is the model_name, which must be fully-formed to get a response from the API. + Additional methods are provided to list the models available to be insured. + """ + def __init__(self, client): + super().__init__(client, "root_gadgets", ["model_name"]) + + def list_models(self): + """ List the models available in the root_gadgets module + :return: list of models available, each model is in dict format with keys 'make','name','value' + """ + return self.call("get", path="modules/root_gadgets/models") + + def list_phone_brands(self): + models = self.list_models() + return set([phone['make'] for phone in models]) + + def list_phones_by_brand(self, brand): + models = self.list_models() + return set([phone['name'] for phone in models if phone['make'] == brand]) + + def get_phone_value(self, phone): + models = self.list_models() + return list(filter(lambda p: p['name'] == phone, models))[0]['value'] / 100 + + +class FuneralCover(Quotes): + def __init__(self, client): + super().__init__(client, "root_funeral", + ["cover_amount", "has_spouse", "number_of_children", "extended_family_ages"]) + + +class TermCover(Quotes): + def __init__(self, client): + super().__init__(client, "root_term", + ["cover_amount", "cover_period", "education_status", "smoker", "gender", "age", + "basic_income_per_month"]) + + +class Applications(Resource): + def __init__(self, client): + super().__init__(client, "post", "applications") + + def create(self, policyholder_id, quote_package_id, monthly_premium, serial_number=None): + data = { + "policyholder_id": policyholder_id, + "quote_package_id": quote_package_id, + "monthly_premium": monthly_premium, + "serial_number": serial_number + } + return self.call(json=data) + + +class Claims(Resource): + def __init__(self, client): + super().__init__(client, "get", "claims") + + def list(self, status=None, approval=None): + params = {} + if status: + params["claim_status"] = status + params = {} + if approval: + params["approval_status"] = approval + + return self.call(params=params) + + def get(self, claim_id): + return self.call('{claim_id}'.format(claim_id=claim_id)) + + def open(self, policy_id=None, policy_holder_id=None): + data = { + "policy_id": policy_id, + "policy_holder_id": policy_holder_id + } + return self.call("post", json=data) + + def link_policy(self, claim_id, policy_id): + data = { + "policy_id": policy_id + } + return self.call("post", '{claim_id}/policy'.format(claim_id=claim_id), json=data) + + def link_policy_holder(self, claim_id, policy_holder_id): + data = { + "policy_holder_id": policy_holder_id + } + return self.call("post", '{claim_id}/policyholder'.format(claim_id=claim_id), json=data) + + def link_events(self, claim_id): + return self.call("post", '{claim_id}/events'.format(claim_id=claim_id)) + + +class PolicyHolders(Resource): + def __init__(self, client): + super().__init__(client, "get", "policyholders") + + def list(self): + return self.call() + + def get(self, policyholder_id): + return self.call('{policyholder_id}'.format(policyholder_id=policyholder_id)) + + def list_events(self, policyholder_id): + return self.call('{policyholder_id}/events'.format(policyholder_id=policyholder_id)) + + def create(self, policyholder: Policyholder): + data = policyholder.details() + return self.call("post", json=data) + + def update(self, policyholder_id, email=None, cellphone=None): + data = { + "email": email, + "cellphone": cellphone + } + return self.call("patch", '{policyholder_id}'.format(policyholder_id=policyholder_id), json=data) + + +class Policies(Resource): + def __init__(self, client): + super().__init__(client, "get", "policies") + + def list(self): + return self.call() + + def get(self, policy_id): + return self.call('{policy_id}'.format(policy_id=policy_id)) + + def issue(self, application_id): + data = { + "application_id": application_id, + } + return self.call("post", json=data) + + def list_beneficiaries(self, policy_id): + return self.call('{policy_id}/beneficiaries'.format(policy_id=policy_id)) + + def list_events(self, policy_id): + return self.call('{policy_id}/events'.format(policy_id=policy_id)) + + def add_beneficiary(self, policy_id, beneficiaries: [Beneficiary]): + """ + + After a policy has been issued, the beneficiaries that will receive payment on a claim payout can be added. + Updating a policy's beneficiaries replaces any beneficiaries added to the policy in the past. + + The number of beneficiaries that can be added and the details required for beneficiaries may differ depending + on the insurance module type of the policy. + + When updating beneficiaries, the percentage of a claim payout + that each beneficiary should receive must be provided. The sum of percentages for beneficiaries added must be + 100. + + :param policy_id: + :param beneficiaries: + + """ + data = [beneficiary.details() for beneficiary in beneficiaries] + total = sum([datum['percentage'] for datum in data]) + assert total == 100, "Beneficiary percentages must add to 100" + return self.call("put", '{policy_id}/beneficiaries'.format(policy_id=policy_id), json=data) + + def cancel(self, policy_id, reason): + data = {"reason": reason} + return self.call("post", '{policy_id}/cancel'.format(policy_id=policy_id), json=data) + + def replace(self, policy_id, quote_package_id): + data = {"quote_package_id": quote_package_id} + return self.call("post", '{policy_id}/replace'.format(policy_id=policy_id), json=data) + + def update_billing_amount(self, policy_id, billing_amount): + data = {"billing_amount": billing_amount} + return self.call("post", '{policy_id}/billing_amount'.format(policy_id=policy_id), json=data) diff --git a/root/main.py b/root/main.py deleted file mode 100644 index 0a419df..0000000 --- a/root/main.py +++ /dev/null @@ -1,10 +0,0 @@ -from insurance import Client -import os - -def main(): - client = Client() - print(client.gadgets.get_phone_value("iPhone 6 Plus 128GB LTE")) - - -if __name__ == "__main__": - main() diff --git a/setup.py b/setup.py index 2915312..3a6227a 100644 --- a/setup.py +++ b/setup.py @@ -35,12 +35,12 @@ # For a discussion on single-sourcing the version across setup.py and the # project code, see # https://packaging.python.org/en/latest/single_source_version.html - version='0.1.3', # Required + version='0.2.0', # Required # This is a one-line description or tagline of what your project does. This # corresponds to the "Summary" metadata field: # https://packaging.python.org/specifications/core-metadata/#summary - description='A python SDK for the Root insurance API', # Required + description='A python SDK for the Root Insurance API', # Required # This should be a valid link to your project's main homepage. # @@ -50,7 +50,7 @@ # This should be your name or the name of the organization which owns the # project. - author='Brendan Ball', # Optional + author='Root Community, Brendan Ball, Christopher Currin', # Optional # Classifiers help users find your project by categorizing it. # diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/context.py b/tests/context.py index 9d67816..3ec8b29 100644 --- a/tests/context.py +++ b/tests/context.py @@ -1,3 +1,9 @@ +# +# Import this file at the top of every test to have 'root' in context +# e.g. +# >>> import context # import of 'root' won't work without this +# >>> from root.insurance import InsuranceClient +# import os import sys sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) diff --git a/tests/test_gadgets.py b/tests/test_gadgets.py index ace1a6f..6714dde 100644 --- a/tests/test_gadgets.py +++ b/tests/test_gadgets.py @@ -1,34 +1,41 @@ -from context import insurance -import json - -client = insurance.Client() - -def test_list_models(): - result = client.gadgets.list_models() - assert result - assert result.__len__() - assert len(result) > 0 - assert any(x for x in result if 'Apple' in x.get('make')) - -def test_list_phone_brands(): - result = client.gadgets.list_phone_brands() - assert result - assert result.__len__() - assert any(x for x in result if 'Apple' in x) - assert len(result) > 0 - -def test_list_phones_by_brand(): - result = client.gadgets.list_phones_by_brand('Apple') - assert result - assert result.__len__() - assert len(result) > 0 - assert any(x for x in result if 'iPhone' in x) - - -def test_get_phone_value(): - #phones = client.gadgets.list_phone_brands() - phones = client.gadgets.list_phones_by_brand('Apple') - print("Phones") - print(type(phones)) - result = client.gadgets.get_phone_value(list(phones)[0]) - assert int(result) > 0 +import unittest +import context +from root.insurance import InsuranceClient, GadgetCover + +class GadgetCoverTestCase(unittest.TestCase): + + def setUp(self): + self.client = InsuranceClient(cover=GadgetCover) + + def test_list_models(self): + result = self.client.quotes.list_models() + assert result + assert result.__len__() + assert len(result) > 0 + assert any(x for x in result if 'Apple' in x.get('make')) + + def test_list_phone_brands(self): + result = self.client.quotes.list_phone_brands() + assert result + assert result.__len__() + assert any(x for x in result if 'Apple' in x) + assert len(result) > 0 + + def test_list_phones_by_brand(self): + result = self.client.quotes.list_phones_by_brand('Apple') + assert result + assert result.__len__() + assert len(result) > 0 + assert any(x for x in result if 'iPhone' in x) + + def test_get_phone_value(self): + phones = self.client.quotes.list_phones_by_brand('Apple') + print("Phones") + print(type(phones)) + result = self.client.quotes.get_phone_value(list(phones)[0]) + assert int(result) > 0 + + def test_get_phone_value_unknown_model(self): + with self.assertRaises(IndexError) as context: + self.client.quotes.get_phone_value("iPhone 600 Plus 128TB LTD") +