diff --git a/namecheap.py b/namecheap.py
index 05911ca..e3e6c95 100644
--- a/namecheap.py
+++ b/namecheap.py
@@ -1,7 +1,7 @@
import sys
import time
import requests # pip install requests
-from xml.etree.ElementTree import fromstring
+from xml.etree.ElementTree import fromstring, Element
inPy3k = sys.version_info[0] == 3
@@ -29,7 +29,10 @@ def __init__(self, number, text):
self.text = text
+# noinspection PyPep8Naming
class Api(object):
+ ENDPOINTS = ENDPOINTS
+
# Follows API spec capitalization in variable names for consistency.
def __init__(self, ApiUser, ApiKey, UserName, ClientIP,
sandbox=True, debug=True,
@@ -50,13 +53,55 @@ def domains_create(
self,
DomainName, FirstName, LastName,
Address1, City, StateProvince, PostalCode, Country, Phone,
- EmailAddress, Address2=None, years=1, WhoisGuard=False
+ EmailAddress, Address2=None, years=1, WhoisGuard=False,
+ OrganizationName=None, JobTitle=None, PromotionCode=None,
+ Nameservers=None, **user_payload
):
+ # type: (str, str, str, str, str, str, str, str, str, str, str, int, bool, str, str, str, str or list, any) -> dict
"""
Registers a domain name with the given contact info.
Example of a working phone number: +81.123123123
- For simplicity assumes one person acts as all contact types."""
+ For simplicity assumes one person acts as all contact types.
+
+ If you're registering a domain as a company/organisation, specify the company name in ``OrganizationName``,
+ and specify the job role of the person named in ``FirstName`` / ``LastName`` in ``JobTitle``
+ e.g. ``domains_create(FirstName='John', LastName='Doe', OrganizationName='ExampleCorp', JobTitle='Chief Technical Officer')``
+
+ **Example Usage**::
+
+ >>> api = Api('user', 'key', 'user', '12.34.56.78')
+ >>> res = api.domains_create(
+ ... DomainName = 'somecooldomain.com',
+ ... FirstName = 'Jack',
+ ... LastName = 'Trotter',
+ ... Address1 = 'Ridiculously Big Mansion, Yellow Brick Road',
+ ... City = 'Tokushima',
+ ... StateProvince = 'Tokushima',
+ ... PostalCode = '771-0144',
+ ... Country = 'Japan',
+ ... Phone = '+81.123123123',
+ ... EmailAddress = 'jack.trotter@example.com'
+ ... )
+ >>> print(res)
+ {
+ 'Domain': 'somecooldomain.com', 'Registered': 'true', 'ChargedAmount': '12.1600',
+ 'DomainID': '615026', 'OrderID': '2139371', 'TransactionID': '4139125',
+ 'WhoisguardEnable': 'true', 'FreePositiveSSL': 'false', 'NonRealTimeDomain': 'false'
+ }
+
+ **Automatically set nameservers on purchase**::
+
+ >>> # For readability, we're not including the required contact information for these calls
+ >>> # You can specify Nameservers as either a list [] of nameservers as strings
+ >>> api.domains_create('anotherdomain.com', Nameservers=['ns1.example.com', 'ns2.example.com', 'ns3.example.com'])
+ >>> # OR you can specify them as a comma separated string - it's up to your preference
+ >>> # There is no difference in result whether you specify nameservers as a list or string
+ >>> api.domains_create('anotherdomain.com', Nameservers='ns1.example.com,ns2.example.com,ns3.example.com')
+
+ :raises ApiError: When ```` in the result is non-empty.
+ :return dict DomainCreateResult: The attributes of the result's ```` as a dictionary
+ """
contact_types = ['Registrant', 'Tech', 'Admin', 'AuxBilling']
@@ -70,7 +115,13 @@ def domains_create(
'AddFreeWhoisguard': 'yes',
'WGEnabled': 'yes',
})
-
+ if PromotionCode:
+ extra_payload['PromotionCode'] = PromotionCode
+ if Nameservers:
+ if isinstance(Nameservers, (list, set, tuple)):
+ Nameservers = ','.join(Nameservers)
+ extra_payload['Nameservers'] = Nameservers
+
for contact_type in contact_types:
extra_payload.update({
'%sFirstName' % contact_type: FirstName,
@@ -85,11 +136,31 @@ def domains_create(
})
if Address2:
extra_payload['%sAddress2' % contact_type] = Address2
-
- self._call('namecheap.domains.create', extra_payload)
-
- def _payload(self, Command, extra_payload={}):
+ if OrganizationName:
+ extra_payload['%sOrganizationName' % contact_type] = OrganizationName
+ if JobTitle:
+ extra_payload['%sJobTitle' % contact_type] = JobTitle
+
+ # Merge in any user payload key:value's on top of our generated payload dictionary
+ extra_payload = {**extra_payload, **user_payload}
+
+ xml = self._call('namecheap.domains.create', extra_payload)
+ return self.get_element_dict(xml, 'DomainCreateResult')
+
+ @staticmethod
+ def get_element(element, element_name):
+ # type: (Element, str) -> Element
+ return element.find('.//{%(ns)s}%(el)s' % {'ns': NAMESPACE, 'el': element_name})
+
+ @staticmethod
+ def get_element_dict(element, element_name):
+ # type: (Element, str) -> dict
+ return dict(Api.get_element(element, element_name).items())
+
+ def _payload(self, Command, extra_payload=None):
+ # type: (str, dict) -> (dict, dict)
"""Make dictionary for passing to requests.post"""
+ extra_payload = {} if extra_payload is None else extra_payload
payload = {
'ApiUser': self.ApiUser,
'ApiKey': self.ApiKey,
@@ -104,7 +175,8 @@ def _payload(self, Command, extra_payload={}):
extra_payload = {}
return payload, extra_payload
- def _fetch_xml(self, payload, extra_payload = None):
+ def _fetch_xml(self, payload, extra_payload=None):
+ # type: (dict, dict) -> Element
"""Make network call and return parsed XML element"""
attempts_left = self.attempts_count
while attempts_left > 0:
@@ -138,8 +210,10 @@ def _fetch_xml(self, payload, extra_payload = None):
return xml
- def _call(self, Command, extra_payload={}):
+ def _call(self, Command, extra_payload=None):
+ # type: (str, dict) -> Element
"""Call an API command"""
+ extra_payload = {} if extra_payload is None else extra_payload
payload, extra_payload = self._payload(Command, extra_payload)
xml = self._fetch_xml(payload, extra_payload)
return xml
@@ -178,14 +252,17 @@ def __next__(self):
# https://www.namecheap.com/support/api/methods/domains-dns/set-default.aspx
def domains_dns_setDefault(self, domain):
+ # type: (str) -> dict
sld, tld = domain.split(".")
- self._call("namecheap.domains.dns.setDefault", {
+ xml = self._call("namecheap.domains.dns.setDefault", {
'SLD': sld,
'TLD': tld
})
-
+ return self.get_element_dict(xml, 'DomainDNSSetDefaultResult')
+
# https://www.namecheap.com/support/api/methods/domains/check.aspx
def domains_check(self, domains):
+ # type: (str or list) -> dict
"""Checks the availability of domains.
For example
@@ -303,6 +380,7 @@ def domains_getContacts(self, DomainName):
# https://www.namecheap.com/support/api/methods/domains-dns/set-hosts.aspx
def domains_dns_setHosts(self, domain, host_records):
+ # type: (str, list) -> dict
"""Sets the DNS host records for a domain.
Example:
@@ -323,10 +401,11 @@ def domains_dns_setHosts(self, domain, host_records):
'SLD': sld,
'TLD': tld
})
- self._call("namecheap.domains.dns.setHosts", extra_payload)
+ return self.get_element_dict(self._call("namecheap.domains.dns.setHosts", extra_payload), 'DomainDNSSetHostsResult')
# https://www.namecheap.com/support/api/methods/domains-dns/set-custom.aspx
def domains_dns_setCustom(self, domain, host_records):
+ # type: (str, dict) -> dict
"""Sets the domain to use the supplied set of nameservers.
Example:
@@ -337,10 +416,11 @@ def domains_dns_setCustom(self, domain, host_records):
sld, tld = domain.split(".")
extra_payload['SLD'] = sld
extra_payload['TLD'] = tld
- self._call("namecheap.domains.dns.setCustom", extra_payload)
+ return self.get_element_dict(self._call("namecheap.domains.dns.setCustom", extra_payload), 'DomainDNSSetCustomResult')
# https://www.namecheap.com/support/api/methods/domains-dns/get-hosts.aspx
def domains_dns_getHosts(self, domain):
+ # type: (str) -> list
"""Retrieves DNS host record settings. Note that the key names are different from those
you use when setting the host records."""
sld, tld = domain.split(".")
@@ -385,9 +465,10 @@ def domains_dns_addHost(self, domain, host_record):
'SLD': sld,
'TLD': tld
})
- self._call("namecheap.domains.dns.setHosts", extra_payload)
+ return self.get_element_dict(self._call("namecheap.domains.dns.setHosts", extra_payload), 'DomainDNSSetHostsResult')
def domains_dns_delHost(self, domain, host_record):
+ # type: (str, dict) -> dict or bool
"""This method is absent in original API as well. It executes non-atomic
remove operation over the host record which has the following Type,
Hostname and Address.
@@ -436,7 +517,7 @@ def domains_dns_delHost(self, domain, host_record):
'SLD': sld,
'TLD': tld
})
- self._call("namecheap.domains.dns.setHosts", extra_payload)
+ return self.get_element_dict(self._call("namecheap.domains.dns.setHosts", extra_payload), 'DomainDNSSetHostsResult')
# https://www.namecheap.com/support/api/methods/domains-dns/get-list.aspx
def domains_getList(self, ListType=None, SearchTerm=None, PageSize=None, SortBy=None):
diff --git a/namecheap_tests.py b/namecheap_tests.py
index 97949bb..1edde40 100644
--- a/namecheap_tests.py
+++ b/namecheap_tests.py
@@ -12,6 +12,11 @@
except:
pass
+
+def is_true(val):
+ return val in ['true', True, 1, '1', 'yes', 'True', 'TRUE']
+
+
def random_domain_name():
import random
from time import gmtime, strftime
@@ -36,7 +41,7 @@ def test_register_domain():
# Try registering a random domain. Fails if exception raised.
domain_name = random_domain_name()
- api.domains_create(
+ res = api.domains_create(
DomainName=domain_name,
FirstName='Jack',
LastName='Trotter',
@@ -48,6 +53,11 @@ def test_register_domain():
Phone='+81.123123123',
EmailAddress='jack.trotter@example.com'
)
+
+ assert_equal(res['Domain'], domain_name)
+ ok_(int(res['DomainID']) > 0)
+ ok_(int(res['TransactionID']) > 0)
+ ok_(is_true(res['Registered']))
return domain_name
@@ -69,7 +79,9 @@ def test_domains_dns_setDefault_on_nonexisting_domain():
def test_domains_dns_setDefault_on_existing_domain():
api = Api(username, api_key, username, ip_address, sandbox=True)
domain_name = test_register_domain()
- api.domains_dns_setDefault(domain_name)
+ res = api.domains_dns_setDefault(domain_name)
+ assert_equal(res['Domain'], domain_name)
+ ok_(is_true(res['Updated']))
def test_domains_getContacts():
@@ -82,7 +94,7 @@ def test_domains_getContacts():
def test_domains_dns_setHosts():
api = Api(username, api_key, username, ip_address, sandbox=True)
domain_name = test_register_domain()
- api.domains_dns_setHosts(
+ res = api.domains_dns_setHosts(
domain_name,
[{
'HostName': '@',
@@ -92,6 +104,8 @@ def test_domains_dns_setHosts():
'TTL': '100'
}]
)
+ assert_equal(res['Domain'], domain_name)
+ ok_(is_true(res['IsSuccess']))
#
# I wasn't able to get this to work on any public name servers that I tried
@@ -244,7 +258,8 @@ def test_domains_dns_bulkAddHosts():
def test_domains_dns_delHost():
api = Api(username, api_key, username, ip_address, sandbox=True)
domain_name = test_register_domain()
- api.domains_dns_setHosts(
+
+ res = api.domains_dns_setHosts(
domain_name,
[{
'HostName': '@',
@@ -257,7 +272,10 @@ def test_domains_dns_delHost():
'Address': '1.2.3.4'
}]
)
- api.domains_dns_delHost(
+ assert_equal(res['Domain'], domain_name)
+ ok_(is_true(res['IsSuccess']))
+
+ res = api.domains_dns_delHost(
domain_name,
{
'Name': 'test',
@@ -265,7 +283,9 @@ def test_domains_dns_delHost():
'Address': '1.2.3.4'
}
)
-
+ assert_equal(res['Domain'], domain_name)
+ ok_(is_true(res['IsSuccess']))
+
hosts = api.domains_dns_getHosts(domain_name)
# these might change
diff --git a/requirements.txt b/requirements.txt
index 2c3b0d0..3a2da96 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1 +1,3 @@
setuptools>=39.2.0
+requests
+nose