Repository: AnalogJ/lexicon

Issue:
DNSimple crash with team member that does not have a plan
Here's the error I get (I have redacted my account number, domain and auth token)

```sh
$ lexicon dnsimple --auth-token REDACTED list REDACTED A
Traceback (most recent call last):
  File "/usr/local/bin/lexicon", line 11, in <module>
    sys.exit(main())
  File "/usr/local/lib/python2.7/dist-packages/lexicon/cli.py", line 117, in main
    results = client.execute()
  File "/usr/local/lib/python2.7/dist-packages/lexicon/client.py", line 64, in execute
    self.provider.authenticate()
  File "/usr/local/lib/python2.7/dist-packages/lexicon/providers/dnsimple.py", line 43, in authenticate
    '/{0}/domains'.format(account['id']), query_params={'name_like': self.domain})
  File "/usr/local/lib/python2.7/dist-packages/lexicon/providers/base.py", line 87, in _get
    return self._request('GET', url, query_params=query_params)
  File "/usr/local/lib/python2.7/dist-packages/lexicon/providers/dnsimple.py", line 189, in _request
    r.raise_for_status()
  File "/usr/lib/python2.7/dist-packages/requests/models.py", line 935, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 402 Client Error: Payment Required for url: https://api.dnsimple.com/v2/REDACTED/domains?name_like=REDACTED
```

I believe that this happens because of the way the domain I have is configured. We have a domain configured with a team. There is one account that has a professional plan (with our domain) and we have other users as team members of that account. The only account that has a plan is the one mentioned. Other users have personal accounts by default but they don't have a plan configured at all.

I believe that the code responsible is this: https://github.com/AnalogJ/lexicon/blob/master/lexicon/providers/dnsimple.py#L37-L52

The dnsimple provider iterates through all accounts that the token has access to and tried to see if they have a domain similar to the one specified. When it tries to do this on an account that is not configured it will crash with the http error `402 Client Error: Payment Required for url: ...`.

This is consistent with the documentation from dnsimple: https://developer.dnsimple.com/v2/#response-codes

> `402 Payment Required` - Your account is not subscribed or not in good standing.

I think that it's possible to figure out if an account is not configured by using the `GET /accounts` endpoint. Accounts that are not configured have their `plan_identifier` set to `null`. This does not seem to be documented in the API references tho.

When I do this request I get the following json as output

```json
{
    "data": [
        {
            "created_at": "2018-06-05T19:23:59Z",
            "email": "REDACTED",
            "id": "REDACTED",
            "plan_identifier": "dnsimple-professional",
            "updated_at": "2019-01-06T21:16:40Z"
        },
        {
            "created_at": "2018-06-08T19:48:59Z",
            "email": "REDACTED",
            "id": "REDACTED",
            "plan_identifier": null,
            "updated_at": "2018-06-08T19:48:59Z"
        }
    ]
}
```


Discussion:
Hey @dotboris 

Thanks for doing the hard work of debugging this issue. Could you open a PR with a fix? I'd be happy to merge it. 

Resolving patch:
diff --git a/lexicon/providers/dnsimple.py b/lexicon/providers/dnsimple.py
--- a/lexicon/providers/dnsimple.py
+++ b/lexicon/providers/dnsimple.py
@@ -42,6 +42,13 @@ def _authenticate(self):
             raise Exception('No account id found')
 
         for account in payload:
+            if account['plan_identifier'] is None:
+                logging.warning(
+                    'Skipping unconfigured account %s (%d). ' \
+                    'To use this account, you must select a plan.',
+                    account['email'], account['id'])
+                continue
+
             dompayload = self._get(
                 '/{0}/domains'.format(account['id']), query_params={'name_like': self.domain})
             if dompayload and dompayload[0]['id']:


Test patch: