Skip to content

Commit 537b64f

Browse files
committed
Version 3.19.0
1 parent dde8b5f commit 537b64f

File tree

7 files changed

+80
-65
lines changed

7 files changed

+80
-65
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Changelog
22

33
## master - CURRENT
4+
5+
## 3.19.0 - 06/12/2024
46
### Added
57
* Add `regfish` provider (#2102)
68
* Add `ionos` provider (#2127)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "dns-lexicon"
7-
version = "3.18.0"
7+
version = "3.19.0"
88
description = "Manipulate DNS records on various DNS providers in a standardized/agnostic way"
99
license = "MIT"
1010
keywords = [

src/lexicon/_private/providers/ionos.py

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,65 +2,69 @@
22

33
from lexicon.interfaces import Provider as BaseProvider
44

5-
6-
_ZONES_API = 'https://api.hosting.ionos.com/dns/v1/zones'
5+
_ZONES_API = "https://api.hosting.ionos.com/dns/v1/zones"
76

87

98
class Provider(BaseProvider):
109

1110
@staticmethod
1211
def get_nameservers():
13-
return ['ui-dns.com', 'ui-dns.org', 'ui-dns.de', 'ui-dns.biz']
12+
return ["ui-dns.com", "ui-dns.org", "ui-dns.de", "ui-dns.biz"]
1413

1514
@staticmethod
1615
def configure_parser(parser):
1716
parser.add_argument(
18-
'--api-key',
17+
"--api-key",
1918
required=True,
20-
help='IONOS api key: public prefix + period + key proper',
19+
help="IONOS api key: public prefix + period + key proper",
2120
)
2221

2322
def authenticate(self):
2423
zones = self._get(_ZONES_API)
2524
for zone in zones:
26-
if zone['name'] == self.domain:
27-
self.domain_id = zone['id']
25+
if zone["name"] == self.domain:
26+
self.domain_id = zone["id"]
2827
return
29-
raise Exception('domain not found: ' + self.domain)
28+
raise Exception("domain not found: " + self.domain)
3029

3130
def create_record(self, rtype, name, content):
3231
self._post(
33-
_ZONES_API + '/' + self.domain_id + '/records',
34-
data=[{
35-
'name': self._full_name(name),
36-
'type': rtype,
37-
'content': content,
38-
'ttl': self._get_lexicon_option('ttl'),
39-
'prio': 0,
40-
'disabled': False,
41-
}],
32+
_ZONES_API + "/" + self.domain_id + "/records",
33+
data=[
34+
{
35+
"name": self._full_name(name),
36+
"type": rtype,
37+
"content": content,
38+
"ttl": self._get_lexicon_option("ttl"),
39+
"prio": 0,
40+
"disabled": False,
41+
}
42+
],
4243
)
4344
return True
4445

4546
def list_records(self, rtype=None, name=None, content=None):
4647
query_params = {}
4748
if rtype:
48-
query_params['recordType'] = rtype
49+
query_params["recordType"] = rtype
4950
if name:
50-
query_params['recordName'] = self._full_name(name)
51-
data = self._get(_ZONES_API + '/' + self.domain_id, query_params)
52-
records = data['records']
53-
records = [{
54-
'type': r['type'],
55-
'name': r['name'],
56-
'ttl': r['ttl'],
57-
'content': r['content'],
58-
'id': r['id'],
59-
} for r in records]
51+
query_params["recordName"] = self._full_name(name)
52+
data = self._get(_ZONES_API + "/" + self.domain_id, query_params)
53+
records = data["records"]
54+
records = [
55+
{
56+
"type": r["type"],
57+
"name": r["name"],
58+
"ttl": r["ttl"],
59+
"content": r["content"],
60+
"id": r["id"],
61+
}
62+
for r in records
63+
]
6064
for record in records:
6165
self._clean_TXT_record(record)
6266
if content:
63-
records = [r for r in records if r['content'] == content]
67+
records = [r for r in records if r["content"] == content]
6468
return records
6569

6670
def update_record(self, identifier, rtype, name, content):
@@ -71,25 +75,20 @@ def delete_record(self, identifier=None, rtype=None, name=None, content=None):
7175
if identifier:
7276
identifiers = [identifier]
7377
else:
74-
identifiers = [
75-
r['id']
76-
for r in self.list_records(rtype, name, content)
77-
]
78+
identifiers = [r["id"] for r in self.list_records(rtype, name, content)]
7879
for identifier in identifiers:
79-
self._delete(
80-
_ZONES_API + '/' + self.domain_id + '/records/' + identifier
81-
)
80+
self._delete(_ZONES_API + "/" + self.domain_id + "/records/" + identifier)
8281
return True
8382

84-
def _request(self, action='GET', url='/', data=None, query_params=None):
83+
def _request(self, action="GET", url="/", data=None, query_params=None):
8584
response = requests.request(
8685
action,
8786
url,
8887
params=query_params,
8988
json=data,
9089
headers={
91-
'accept': 'application/json',
92-
'x-api-key': self._get_provider_option('api_key'),
90+
"accept": "application/json",
91+
"x-api-key": self._get_provider_option("api_key"),
9392
},
9493
)
9594
response.raise_for_status()

src/lexicon/_private/providers/regfish.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ def create_record(self, rtype, name, content):
6060
payload = self._post("/dns/rr", data)
6161
except requests.exceptions.HTTPError as err:
6262
json_res = err.response.json()
63-
if not json_res["code"] if "code" in json_res else 0 != 15003: # ResourceRecordAlreadyExists
63+
if (
64+
not json_res["code"] if "code" in json_res else 0 != 15003
65+
): # ResourceRecordAlreadyExists
6466
raise
6567

6668
if payload["code"] if "code" in payload else 0 != 0:
@@ -91,10 +93,14 @@ def list_records(self, rtype=None, name=None, content=None):
9193
# Apply filters on rtype, name, and content if they are provided
9294
if rtype or name or content:
9395
records = [
94-
record for record in records
95-
if (not rtype or record['type'] == rtype) and
96-
(not name or record['name'] == self._full_name(name)) and
97-
(not content or record['content'] == self._format_content(rtype, content))
96+
record
97+
for record in records
98+
if (not rtype or record["type"] == rtype)
99+
and (not name or record["name"] == self._full_name(name))
100+
and (
101+
not content
102+
or record["content"] == self._format_content(rtype, content)
103+
)
98104
]
99105

100106
LOGGER.debug("list_records after filtering: %s", records)
@@ -108,9 +114,13 @@ def update_record(self, identifier, rtype=None, name=None, content=None):
108114
if len(records) == 1:
109115
identifier = records[0]["id"]
110116
elif len(records) < 1:
111-
raise Exception("No records found matching type and name - won't update")
117+
raise Exception(
118+
"No records found matching type and name - won't update"
119+
)
112120
else:
113-
raise Exception("Multiple records found matching type and name - won't update")
121+
raise Exception(
122+
"Multiple records found matching type and name - won't update"
123+
)
114124

115125
content = self._format_content(rtype, content)
116126
data = {}

tests/providers/integration_tests.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,9 @@ def __init__(self):
9797
self.provider_module = None
9898

9999
def setup_method(self, _):
100-
warnings.filterwarnings("ignore", category=ResourceWarning,
101-
message="unclosed.*<ssl.SSLSocket.*>")
100+
warnings.filterwarnings(
101+
"ignore", category=ResourceWarning, message="unclosed.*<ssl.SSLSocket.*>"
102+
)
102103
self.provider_module = load_provider_module(self.provider_name)
103104

104105
###########################################################################

tests/providers/test_ionos.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Integration tests for IONOS"""
2+
23
import json
34
import os
45
from unittest import TestCase
@@ -12,36 +13,38 @@
1213
class IONOSProviderTests(TestCase, IntegrationTestsV2):
1314
"""Integration tests for IONOS provider"""
1415

15-
provider_name = 'ionos'
16-
domain = os.environ.get('LEXICON_IONOS_DOMAIN', 'example.com')
16+
provider_name = "ionos"
17+
domain = os.environ.get("LEXICON_IONOS_DOMAIN", "example.com")
1718

1819
def _filter_request(self, request):
19-
request.uri = request.uri.replace(self.domain, 'example.com')
20+
request.uri = request.uri.replace(self.domain, "example.com")
2021
if request.body:
21-
body = request.body.decode('utf-8')
22-
body = body.replace(self.domain, 'example.com')
23-
request.body = body.encode('utf-8')
22+
body = request.body.decode("utf-8")
23+
body = body.replace(self.domain, "example.com")
24+
request.body = body.encode("utf-8")
2425
return request
2526

2627
def _filter_headers(self):
27-
return ['x-api-key']
28+
return ["x-api-key"]
2829

2930
def _filter_response(self, response):
30-
for key in ['Set-Cookie', 'x-b3-traceid']:
31-
response['headers'].pop(key, None)
32-
body = response['body']['string'].decode('utf-8')
31+
for key in ["Set-Cookie", "x-b3-traceid"]:
32+
response["headers"].pop(key, None)
33+
body = response["body"]["string"].decode("utf-8")
3334
try:
3435
data = json.loads(body)
3536
if isinstance(data, list):
3637
data = [e for e in data if not self._is_unrelated_zone(e)]
3738
body = json.dumps(data)
3839
except json.JSONDecodeError:
3940
pass
40-
body = body.replace(self.domain, 'example.com')
41-
response['body']['string'] = body.encode('utf-8')
41+
body = body.replace(self.domain, "example.com")
42+
response["body"]["string"] = body.encode("utf-8")
4243
return response
4344

4445
def _is_unrelated_zone(self, entry):
45-
return isinstance(entry, dict) \
46-
and 'name' in entry \
47-
and not entry['name'].endswith(self.domain)
46+
return (
47+
isinstance(entry, dict)
48+
and "name" in entry
49+
and not entry["name"].endswith(self.domain)
50+
)

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)