Repository: StackExchange/dnscontrol
Pull request #1283 (2021-10-04 15:52:07)

HEXONET: Support long TXT records and fix whitespace bug
The implementation of TXT records for Hexonet had some bugs

* Trailing whitespace wasn't supported.  This was due to a bug. The new code supports whitespace at the front, middle, and end.
* TXT records with multiple sub-strings wasn't supported.  The code now supports this
* Long TXT records were rejected. The code now splits them properly.

This builds on https://github.com/StackExchange/dnscontrol/pull/1276 by @buraktamturk, who figured out the key issue. To quote @papakai , "many thanks for contributing - lack of time on my end, very appreciated! Let me forward my deepest gratitude for caring."

CC @papakai @buraktamturk


Patch:
diff --git a/providers/hexonet/auditrecords.go b/providers/hexonet/auditrecords.go
index 41d39c99..4d2a3edd 100644
--- a/providers/hexonet/auditrecords.go
+++ b/providers/hexonet/auditrecords.go
@@ -9,25 +9,10 @@ import (
 // supportable by this provider.
 func AuditRecords(records []*models.RecordConfig) error {
 
-	// if err := recordaudit.TxtNoLongStrings(records); err != nil {
-	// 	return err
-	// }
-	// Not needed as of 2021-03-26
-
-	if err := recordaudit.TxtNoMultipleStrings(records); err != nil {
-		return err
-	}
-	// Still needed as of 2021-03-07
-
 	if err := recordaudit.TxtNotEmpty(records); err != nil {
 		return err
 	}
-	// Still needed as of 2021-03-01
-
-	if err := recordaudit.TxtNoTrailingSpace(records); err != nil {
-		return err
-	}
-	// Still needed as of 2021-03-01
+	// Still needed as of 2021-10-01
 
 	return nil
 }
diff --git a/providers/hexonet/records.go b/providers/hexonet/records.go
index 0d251b3f..5753de41 100644
--- a/providers/hexonet/records.go
+++ b/providers/hexonet/records.go
@@ -10,6 +10,7 @@ import (
 
 	"github.com/StackExchange/dnscontrol/v3/models"
 	"github.com/StackExchange/dnscontrol/v3/pkg/diff"
+	"github.com/StackExchange/dnscontrol/v3/pkg/txtutil"
 )
 
 // HXRecord covers an individual DNS resource record.
@@ -69,6 +70,7 @@ func (n *HXClient) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Corr
 
 	// Normalize
 	models.PostProcessRecords(actual)
+	txtutil.SplitSingleLongTxt(dc.Records)
 
 	differ := diff.New(dc)
 	_, create, del, mod, err := differ.IncrementalDiff(actual)
@@ -276,15 +278,12 @@ func (n *HXClient) deleteRecordString(record *HXRecord, domain string) string {
 
 // encodeTxt encodes TxtStrings for sending in the CREATE/MODIFY API:
 func encodeTxt(txts []string) string {
-	ans := txts[0]
-
-	if len(txts) > 1 {
-		ans = ""
-		for _, t := range txts {
-			ans += `"` + strings.Replace(t, `"`, `\"`, -1) + `"`
-		}
+	var r []string
+	for _, txt := range txts {
+		n := `"` + strings.Replace(txt, `"`, `\"`, -1) + `"`
+		r = append(r, n)
 	}
-	return ans
+	return strings.Join(r, " ")
 }
 
 // finds a string surrounded by quotes that might contain an escaped quote character.


Test patch:
diff --git a/providers/hexonet/records_test.go b/providers/hexonet/records_test.go
index 3f9db2b4..8b242931 100644
--- a/providers/hexonet/records_test.go
+++ b/providers/hexonet/records_test.go
@@ -9,16 +9,16 @@ var txtData = []struct {
 	decoded []string
 	encoded string
 }{
-	{[]string{`simple`}, `simple`},
-	{[]string{`changed`}, `changed`},
-	{[]string{`with spaces`}, `with spaces`},
-	{[]string{`with whitespace`}, `with whitespace`},
-	{[]string{"one", "two"}, `"one""two"`},
-	{[]string{"eh", "bee", "cee"}, `"eh""bee""cee"`},
-	{[]string{"o\"ne", "tw\"o"}, `"o\"ne""tw\"o"`},
-	{[]string{"dimple"}, `dimple`},
-	{[]string{"fun", "two"}, `"fun""two"`},
-	{[]string{"eh", "bzz", "cee"}, `"eh""bzz""cee"`},
+	{[]string{`simple`}, `"simple"`},
+	{[]string{`changed`}, `"changed"`},
+	{[]string{`with spaces`}, `"with spaces"`},
+	{[]string{`with  whitespace`}, `"with  whitespace"`},
+	{[]string{"one", "two"}, `"one" "two"`},
+	{[]string{"eh", "bee", "cee"}, `"eh" "bee" "cee"`},
+	{[]string{"o\"ne", "tw\"o"}, `"o\"ne" "tw\"o"`},
+	{[]string{"dimple"}, `"dimple"`},
+	{[]string{"fun", "two"}, `"fun" "two"`},
+	{[]string{"eh", "bzz", "cee"}, `"eh" "bzz" "cee"`},
 }
 
 func TestEncodeTxt(t *testing.T) {
@@ -26,7 +26,7 @@ func TestEncodeTxt(t *testing.T) {
 	for i, test := range txtData {
 		enc := encodeTxt(test.decoded)
 		if enc != test.encoded {
-			t.Errorf("%v: txt\n    data: []string{%v}\nexpected: %s\n     got: %s",
+			t.Errorf("%v: txt\n    data: []string{%v}\nexpected: %q\n     got: %q",
 				i, "`"+strings.Join(test.decoded, "`, `")+"`", test.encoded, enc)
 		}
 	}
@@ -39,11 +39,11 @@ func TestDecodeTxt(t *testing.T) {
 		got := decodeTxt(data)
 		wanted := test.decoded
 		if len(got) != len(wanted) {
-			t.Errorf("%v: txt\n  decode: %v\nexpected: `%v`\n     got: `%v`\n", i, data, strings.Join(wanted, "`, `"), strings.Join(got, "`, `"))
+			t.Errorf("%v: txt\n  decode: %v\nexpected: %q\n     got: %q\n", i, data, strings.Join(wanted, "`, `"), strings.Join(got, "`, `"))
 		} else {
 			for j := range got {
 				if got[j] != wanted[j] {
-					t.Errorf("%v: txt\n  decode: %v\nexpected: `%v`\n     got: `%v`\n", i, data, strings.Join(wanted, "`, `"), strings.Join(got, "`, `"))
+					t.Errorf("%v: txt\n  decode: %v\nexpected: %q\n     got: %q\n", i, data, strings.Join(wanted, "`, `"), strings.Join(got, "`, `"))
 				}
 			}
 		}