Browse Source

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
pull/1131/head
Ross McFarland 2 years ago
parent
commit
c04a320cfd
No known key found for this signature in database GPG Key ID: 943B179E15D3B22A
12 changed files with 51 additions and 62 deletions
  1. +6
    -3
      CHANGELOG.md
  2. +5
    -15
      octodns/record/base.py
  3. +0
    -2
      octodns/record/caa.py
  4. +0
    -2
      octodns/record/loc.py
  5. +0
    -2
      octodns/record/mx.py
  6. +0
    -2
      octodns/record/naptr.py
  7. +0
    -2
      octodns/record/srv.py
  8. +0
    -2
      octodns/record/sshfp.py
  9. +1
    -3
      octodns/record/target.py
  10. +0
    -2
      octodns/record/tlsa.py
  11. +0
    -2
      octodns/record/urlfwd.py
  12. +39
    -25
      tests/test_octodns_record.py

+ 6
- 3
CHANGELOG.md View File

@ -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


+ 5
- 15
octodns/record/base.py View File

@ -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):


+ 0
- 2
octodns/record/caa.py View File

@ -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:


+ 0
- 2
octodns/record/loc.py View File

@ -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:


+ 0
- 2
octodns/record/mx.py View File

@ -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:


+ 0
- 2
octodns/record/naptr.py View File

@ -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:


+ 0
- 2
octodns/record/srv.py View File

@ -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


+ 0
- 2
octodns/record/sshfp.py View File

@ -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:


+ 1
- 3
octodns/record/target.py View File

@ -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)


+ 0
- 2
octodns/record/tlsa.py View File

@ -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:


+ 0
- 2
octodns/record/urlfwd.py View File

@ -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:


+ 39
- 25
tests/test_octodns_record.py View File

@ -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,
def test_values_and_value(self):
# value w/one
r = Record.new(
self.zone, 'thing', {'type': 'TXT', 'ttl': 42, 'value': 'just one'}
)
self.assertEqual(['just one'], r.values)
# same thing is fine as `value`
txt = Record.new(
self.zone, 'thing', {'type': 'TXT', 'ttl': 42, 'value': 'just one'}
# value w/multiple
r = Record.new(
self.zone,
'thing',
{'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)
# same thing is fine when a list
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)
# or tuple
txt = Record.new(
# tuples work too
r = Record.new(
self.zone,
'thing',
{'type': 'TXT', 'ttl': 42, 'values': ('just one',)},
{'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(1, len(txt.values))
self.assertEqual('just one', txt.values[0])
self.assertEqual(['the first', 'the second'], r.values)
def test_validation_context(self):
# fails validation, no context


Loading…
Cancel
Save