mirror of
https://github.com/github/octodns.git
synced 2024-05-11 05:55:00 +00:00
Treat value/values interchangably when configuring records
- All of the `if data isn't a list turn it into one in the value type validates are no longer needed, they'll always be passed a list now` - Special case to handle PTR/target values since it previously was single value and is now values See README for more information
This commit is contained in:
@@ -8,9 +8,12 @@
|
||||
* Support added for config env variable expansion on nested levels, not just
|
||||
top-level provider/processor keys
|
||||
* _ChunkedValue ASCII validation added, SPF & TXT
|
||||
* Minor validation improvement to catch cases where Record values is provided a
|
||||
single a-single-value by accident instead of a list, a common type-o that
|
||||
can results in each character being a value in the resulting Record data
|
||||
* Re-work value/values handling to always try and do the "right" thing based on
|
||||
the content, so both singular values and lists will be handled identically
|
||||
regardless of whether the key is value or values. This may result in
|
||||
changes/fixes on the first sync after updating IFF you currently have
|
||||
`values: a-single-thing`, which would have previously been pushed up as bunch
|
||||
of single character values.
|
||||
|
||||
## v1.4.0 - 2023-12-04 - Minor Meta
|
||||
|
||||
|
||||
@@ -277,14 +277,8 @@ class ValuesMixin(object):
|
||||
def validate(cls, name, fqdn, data):
|
||||
reasons = super().validate(name, fqdn, data)
|
||||
|
||||
try:
|
||||
values = data['values']
|
||||
if isinstance(values, str):
|
||||
reasons.append(
|
||||
f'single value provided under values key, "{values}"'
|
||||
)
|
||||
except KeyError:
|
||||
values = data.get('value', [])
|
||||
values = data.get('values', data.get('value', []))
|
||||
values = values if isinstance(values, (list, tuple)) else [values]
|
||||
|
||||
reasons.extend(cls._value_type.validate(values, cls._type))
|
||||
|
||||
@@ -300,13 +294,9 @@ class ValuesMixin(object):
|
||||
|
||||
def __init__(self, zone, name, data, source=None, context=None):
|
||||
super().__init__(zone, name, data, source=source, context=context)
|
||||
try:
|
||||
values = data['values']
|
||||
except KeyError:
|
||||
try:
|
||||
values = [data['value']]
|
||||
except KeyError:
|
||||
values = []
|
||||
|
||||
values = data.get('values', data.get('value', []))
|
||||
values = values if isinstance(values, (list, tuple)) else [values]
|
||||
self.values = sorted(self._value_type.process(values))
|
||||
|
||||
def changes(self, other, target):
|
||||
|
||||
@@ -26,8 +26,6 @@ class CaaValue(EqualityTupleMixin, dict):
|
||||
|
||||
@classmethod
|
||||
def validate(cls, data, _type):
|
||||
if not isinstance(data, (list, tuple)):
|
||||
data = (data,)
|
||||
reasons = []
|
||||
for value in data:
|
||||
try:
|
||||
|
||||
@@ -110,8 +110,6 @@ class LocValue(EqualityTupleMixin, dict):
|
||||
|
||||
direction_keys = ['lat_direction', 'long_direction']
|
||||
|
||||
if not isinstance(data, (list, tuple)):
|
||||
data = (data,)
|
||||
reasons = []
|
||||
for value in data:
|
||||
for key in int_keys:
|
||||
|
||||
@@ -26,8 +26,6 @@ class MxValue(EqualityTupleMixin, dict):
|
||||
|
||||
@classmethod
|
||||
def validate(cls, data, _type):
|
||||
if not isinstance(data, (list, tuple)):
|
||||
data = (data,)
|
||||
reasons = []
|
||||
for value in data:
|
||||
try:
|
||||
|
||||
@@ -43,8 +43,6 @@ class NaptrValue(EqualityTupleMixin, dict):
|
||||
|
||||
@classmethod
|
||||
def validate(cls, data, _type):
|
||||
if not isinstance(data, (list, tuple)):
|
||||
data = (data,)
|
||||
reasons = []
|
||||
for value in data:
|
||||
try:
|
||||
|
||||
@@ -41,8 +41,6 @@ class SrvValue(EqualityTupleMixin, dict):
|
||||
|
||||
@classmethod
|
||||
def validate(cls, data, _type):
|
||||
if not isinstance(data, (list, tuple)):
|
||||
data = (data,)
|
||||
reasons = []
|
||||
for value in data:
|
||||
# TODO: validate algorithm and fingerprint_type values
|
||||
|
||||
@@ -34,8 +34,6 @@ class SshfpValue(EqualityTupleMixin, dict):
|
||||
|
||||
@classmethod
|
||||
def validate(cls, data, _type):
|
||||
if not isinstance(data, (list, tuple)):
|
||||
data = (data,)
|
||||
reasons = []
|
||||
for value in data:
|
||||
try:
|
||||
|
||||
@@ -51,10 +51,8 @@ class _TargetsValue(str):
|
||||
|
||||
@classmethod
|
||||
def validate(cls, data, _type):
|
||||
if not data:
|
||||
if not data or all(not d for d in data):
|
||||
return ['missing value(s)']
|
||||
elif not isinstance(data, (list, tuple)):
|
||||
data = (data,)
|
||||
reasons = []
|
||||
for value in data:
|
||||
value = idna_encode(value)
|
||||
|
||||
@@ -41,8 +41,6 @@ class TlsaValue(EqualityTupleMixin, dict):
|
||||
|
||||
@classmethod
|
||||
def validate(cls, data, _type):
|
||||
if not isinstance(data, (list, tuple)):
|
||||
data = (data,)
|
||||
reasons = []
|
||||
for value in data:
|
||||
try:
|
||||
|
||||
@@ -13,8 +13,6 @@ class UrlfwdValue(EqualityTupleMixin, dict):
|
||||
|
||||
@classmethod
|
||||
def validate(cls, data, _type):
|
||||
if not isinstance(data, (list, tuple)):
|
||||
data = (data,)
|
||||
reasons = []
|
||||
for value in data:
|
||||
try:
|
||||
|
||||
@@ -683,42 +683,56 @@ class TestRecordValidation(TestCase):
|
||||
lenient=True,
|
||||
)
|
||||
|
||||
def test_values_is_single_value(self):
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(
|
||||
self.zone,
|
||||
'thing',
|
||||
{'type': 'TXT', 'ttl': 42, 'values': 'just one'},
|
||||
)
|
||||
self.assertEqual(
|
||||
['single value provided under values key, "just one"'],
|
||||
ctx.exception.reasons,
|
||||
)
|
||||
|
||||
# same thing is fine as `value`
|
||||
txt = Record.new(
|
||||
def test_values_and_value(self):
|
||||
# value w/one
|
||||
r = Record.new(
|
||||
self.zone, 'thing', {'type': 'TXT', 'ttl': 42, 'value': 'just one'}
|
||||
)
|
||||
self.assertEqual(1, len(txt.values))
|
||||
self.assertEqual('just one', txt.values[0])
|
||||
self.assertEqual(['just one'], r.values)
|
||||
|
||||
# same thing is fine when a list
|
||||
txt = Record.new(
|
||||
# value w/multiple
|
||||
r = Record.new(
|
||||
self.zone,
|
||||
'thing',
|
||||
{'type': 'TXT', 'ttl': 42, 'values': ['just one']},
|
||||
{'type': 'TXT', 'ttl': 42, 'value': ['the first', 'the second']},
|
||||
)
|
||||
self.assertEqual(1, len(txt.values))
|
||||
self.assertEqual('just one', txt.values[0])
|
||||
self.assertEqual(['the first', 'the second'], r.values)
|
||||
|
||||
# or tuple
|
||||
txt = Record.new(
|
||||
# values w/one
|
||||
r = Record.new(
|
||||
self.zone, 'thing', {'type': 'TXT', 'ttl': 42, 'values': 'just one'}
|
||||
)
|
||||
self.assertEqual(['just one'], r.values)
|
||||
|
||||
# values w/multiple
|
||||
r = Record.new(
|
||||
self.zone,
|
||||
'thing',
|
||||
{'type': 'TXT', 'ttl': 42, 'values': ('just one',)},
|
||||
{'type': 'TXT', 'ttl': 42, 'values': ['the first', 'the second']},
|
||||
)
|
||||
self.assertEqual(1, len(txt.values))
|
||||
self.assertEqual('just one', txt.values[0])
|
||||
self.assertEqual(['the first', 'the second'], r.values)
|
||||
|
||||
# tuples work too
|
||||
r = Record.new(
|
||||
self.zone,
|
||||
'thing',
|
||||
{'type': 'TXT', 'ttl': 42, 'values': ('the first', 'the second')},
|
||||
)
|
||||
self.assertEqual(['the first', 'the second'], r.values)
|
||||
|
||||
# values is preferred over value
|
||||
# values w/multiple
|
||||
r = Record.new(
|
||||
self.zone,
|
||||
'thing',
|
||||
{
|
||||
'type': 'TXT',
|
||||
'ttl': 42,
|
||||
'values': ['the first', 'the second'],
|
||||
'value': ['not used', 'not used'],
|
||||
},
|
||||
)
|
||||
self.assertEqual(['the first', 'the second'], r.values)
|
||||
|
||||
def test_validation_context(self):
|
||||
# fails validation, no context
|
||||
|
||||
Reference in New Issue
Block a user