Repository: cloudflare/cloudflare-go (go)

Issue:
Zero values of UpdateDNSRecordParams should keep the comments
### Confirmation

- [X] My issue isn't already found on the issue tracker.
- [X] I have replicated my issue using the latest version of the library and it is still present.

### cloudflare-go version

0.75

### Go environment

N/A

### Expected output

Previously, the Go interface will keep the original comments because of `omitempty`. It's not ideal because there's no easy way to clear comments, where an empty string will keep the original comment because `omitempty` will omit empty strings. The API has since then changed to clear comments when an empty string is used, and the Go library was updated, but then one cannot easily keep the current comments. I believe the expected behavior of `Update` with zero values is to keep everything, and using strings cannot distinguish the two important cases: (1) keep the current comment and (2) clear the current comment.

Suggested solution: use `*string` for the `Comment` field and add back `omitempty`.

### Actual output

See above

### Code demonstrating the issue

```go
// This will clear the comments
c.UpdateDNSRecord(ctx, ZoneIdentifier(zone), UpdateDNSRecordParams{
		ID:      id,
		Content: ip,
	}
)
```

### Steps to reproduce

See above

### References

- https://github.com/cloudflare/cloudflare-go/pull/1166
- https://github.com/cloudflare/cloudflare-go/pull/1194

Pull request:
fix(dns): keep comments intact with zero values of UpdateDNSRecordParams
## Description

Calling `UpdateDNSRecord` with an (almost) zero value of `UpdateDNSRecordParams` will cause the existing comments to be erased. The solution is straightforward---change the type of the field `Comment` from `string` to `&string`, just like how the field `Proxy` has the type `&bool` instead of `bool`.

Closes #1371.

## Has your change been tested?

Yes, but the test must be done against the real server. It is difficult to include a real test in the Go library.

## Types of changes

What sort of change does your code introduce/modify?

- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)

## Checklist:

- [x] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [x] I have added tests to cover my changes. **CAVEAT: A real test can only be done against the real API server, not against the mock server within the Go library.**
- [x] All new and existing tests passed.
- [x] This change is using publicly documented in [cloudflare/api-schemas](https://github.com/cloudflare/api-schemas) 
      and relies on stable APIs.


Patch:
diff --git a/.changelog/1393.txt b/.changelog/1393.txt
new file mode 100644
index 000000000..a3ef6c8ae
--- /dev/null
+++ b/.changelog/1393.txt
@@ -0,0 +1,3 @@
+```release-note:bug
+dns: keep comments when calling UpdateDNSRecord with zero values of UpdateDNSRecordParams
+```
diff --git a/dns.go b/dns.go
index 842fa2f2f..d69e2cb2c 100644
--- a/dns.go
+++ b/dns.go
@@ -34,7 +34,7 @@ type DNSRecord struct {
 	Proxied    *bool       `json:"proxied,omitempty"`
 	Proxiable  bool        `json:"proxiable,omitempty"`
 	Locked     bool        `json:"locked,omitempty"`
-	Comment    string      `json:"comment,omitempty"`
+	Comment    string      `json:"comment,omitempty"` // the server will omit the comment field when the comment is empty
 	Tags       []string    `json:"tags,omitempty"`
 }
 
@@ -57,8 +57,8 @@ type ListDNSRecordsParams struct {
 	Name      string        `url:"name,omitempty"`
 	Content   string        `url:"content,omitempty"`
 	Proxied   *bool         `url:"proxied,omitempty"`
-	Comment   string        `url:"comment,omitempty"`
-	Tags      []string      `url:"tag,omitempty"` // potentially multiple `tag=`
+	Comment   string        `url:"comment,omitempty"` // currently, the server does not support searching for records with an empty comment
+	Tags      []string      `url:"tag,omitempty"`     // potentially multiple `tag=`
 	TagMatch  string        `url:"tag-match,omitempty"`
 	Order     string        `url:"order,omitempty"`
 	Direction ListDirection `url:"direction,omitempty"`
@@ -77,7 +77,7 @@ type UpdateDNSRecordParams struct {
 	Priority *uint16     `json:"priority,omitempty"`
 	TTL      int         `json:"ttl,omitempty"`
 	Proxied  *bool       `json:"proxied,omitempty"`
-	Comment  string      `json:"comment"`
+	Comment  *string     `json:"comment,omitempty"` // nil will keep the current comment, while StringPtr("") will empty it
 	Tags     []string    `json:"tags"`
 }
 
@@ -193,7 +193,7 @@ type CreateDNSRecordParams struct {
 	Proxied    *bool       `json:"proxied,omitempty" url:"proxied,omitempty"`
 	Proxiable  bool        `json:"proxiable,omitempty"`
 	Locked     bool        `json:"locked,omitempty"`
-	Comment    string      `json:"comment,omitempty" url:"comment,omitempty"`
+	Comment    string      `json:"comment,omitempty" url:"comment,omitempty"` // to the server, there's no difference between "no comment" and "empty comment"
 	Tags       []string    `json:"tags,omitempty"`
 }
 


Test patch:
diff --git a/dns_test.go b/dns_test.go
index 1c575288d..97e034a57 100644
--- a/dns_test.go
+++ b/dns_test.go
@@ -616,7 +616,63 @@ func TestUpdateDNSRecord_ClearComment(t *testing.T) {
 
 	_, err := client.UpdateDNSRecord(context.Background(), ZoneIdentifier(testZoneID), UpdateDNSRecordParams{
 		ID:      dnsRecordID,
-		Comment: "",
+		Comment: StringPtr(""),
+	})
+	require.NoError(t, err)
+}
+
+func TestUpdateDNSRecord_KeepComment(t *testing.T) {
+	setup()
+	defer teardown()
+
+	input := DNSRecord{
+		ID: "372e67954025e0ba6aaa6d586b9e0b59",
+	}
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodPatch, r.Method, "Expected method 'PATCH', got %s", r.Method)
+
+		var v DNSRecord
+		err := json.NewDecoder(r.Body).Decode(&v)
+		require.NoError(t, err)
+		v.ID = "372e67954025e0ba6aaa6d586b9e0b59"
+		assert.Equal(t, input, v)
+
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprint(w, `{
+			"success": true,
+			"errors": [],
+			"messages": [],
+			"result": {
+				"id": "372e67954025e0ba6aaa6d586b9e0b59",
+				"type": "A",
+				"name": "example.com",
+				"content": "198.51.100.4",
+				"proxiable": true,
+				"proxied": false,
+				"ttl": 120,
+				"locked": false,
+				"zone_id": "d56084adb405e0b7e32c52321bf07be6",
+				"zone_name": "example.com",
+				"created_on": "2014-01-01T05:20:00Z",
+				"modified_on": "2014-01-01T05:20:00Z",
+				"comment":null,
+				"tags":[],
+				"data": {},
+				"meta": {
+					"auto_added": true,
+					"source": "primary"
+				}
+			}
+		}`)
+	}
+
+	dnsRecordID := "372e67954025e0ba6aaa6d586b9e0b59"
+
+	mux.HandleFunc("/zones/"+testZoneID+"/dns_records/"+dnsRecordID, handler)
+
+	_, err := client.UpdateDNSRecord(context.Background(), ZoneIdentifier(testZoneID), UpdateDNSRecordParams{
+		ID: dnsRecordID,
 	})
 	require.NoError(t, err)
 }