Repository: cloudflare/cloudflare-go (go)

Issue:
UpdateCustomHostnameSSL passes incorrect JSON to API endpoint
## Expected behavior

This function should update the CustomHostnames SSL settings or the entire CustomHostname

## Current behavior

The Library sends a PATCH with a CustomHostnameSSL Struct instead of a CustomHostname struct.

The API then returns a 202 but nothing actually gets updated. (So far as I can tell).
 
## Possible solution

Either update the UpdateCustomHostnameSSL to send a CustomHostnameSSL Struct wrapped so that the JSON is posted as a ssl: object  or create a new function that supports updating the whole customhostname struct.

## Steps to reproduce (for bugs)

Provide a link to a live example, or an unambiguous set of steps to
reproduce this bug. Include code to reproduce, if relevant.

1.
2.
3.
4.

## Context

Cannot update CustomHostname settings via the library

## Your Environment

Include as many relevant details about the environment you experienced
the bug in:

* Version used: master 
* Environment name and version (e.g. Go 1.11, MacOS 10.14.1): go1.13.10 MacOS 10.15.3



Pull request description:
Fixes #453 updating CustomHostnames
## Description

This fixes #453  updating custom hostnames. The API expects a full CustomHostname NOT a CustomHostnameSSL struct as per here - https://api.cloudflare.com/#custom-hostname-for-a-zone-edit-custom-hostname

I've updated UpdateCustomHostnameSSL to send a CustomHostname struct whilst keeping the availability to just pass a CustomHostnameSSL struct.

Along with that I've added a UpdateCustomHostname function which lets you update the whole CustomHostname.

Could probably remove the UpdateCustomHostnameSSL function altogether however to keep it backward compatible I've left it in place.

## Has your change been tested?

I've updated the tests for both functions.

## Types of changes

What sort of change does your code introduce/modify?

- [x] Bug fix (non-breaking change which fixes an issue)
- [x] 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.
- [x] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly. (I've updated the notes? I presume that auto updates the documentation?)
- [x] I have added tests to cover my changes.
- [x] All new and existing tests passed.


Patch:
diff --git a/custom_hostname.go b/custom_hostname.go
index 70b2dfb29..69a3004e3 100644
--- a/custom_hostname.go
+++ b/custom_hostname.go
@@ -97,7 +97,29 @@ type CustomHostnameFallbackOriginResponse struct {
 // API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-update-custom-hostname-configuration
 func (api *API) UpdateCustomHostnameSSL(zoneID string, customHostnameID string, ssl CustomHostnameSSL) (*CustomHostnameResponse, error) {
 	uri := "/zones/" + zoneID + "/custom_hostnames/" + customHostnameID
-	res, err := api.makeRequest("PATCH", uri, ssl)
+	ch := CustomHostname{
+		SSL: ssl,
+	}
+	res, err := api.makeRequest("PATCH", uri, ch)
+	if err != nil {
+		return nil, errors.Wrap(err, errMakeRequestError)
+	}
+
+	var response *CustomHostnameResponse
+	err = json.Unmarshal(res, &response)
+	if err != nil {
+		return nil, errors.Wrap(err, errUnmarshalError)
+	}
+	return response, nil
+}
+
+// UpdateCustomHostname modifies configuration for the given custom
+// hostname in the given zone.
+//
+// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-update-custom-hostname-configuration
+func (api *API) UpdateCustomHostname(zoneID string, customHostnameID string, ch CustomHostname) (*CustomHostnameResponse, error) {
+	uri := "/zones/" + zoneID + "/custom_hostnames/" + customHostnameID
+	res, err := api.makeRequest("PATCH", uri, ch)
 	if err != nil {
 		return nil, errors.Wrap(err, errMakeRequestError)
 	}


Test patch:
diff --git a/custom_hostname_test.go b/custom_hostname_test.go
index 2082e6a8a..bf37615c1 100644
--- a/custom_hostname_test.go
+++ b/custom_hostname_test.go
@@ -349,6 +349,66 @@ func TestCustomHostname_UpdateCustomHostnameSSL(t *testing.T) {
 	}
 }
 
+func TestCustomHostname_UpdateCustomHostname(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/zones/foo/custom_hostnames/0d89c70d-ad9f-4843-b99f-6cc0252067e9", func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, "PATCH", r.Method, "Expected method 'PATCH', got %s", r.Method)
+
+		w.Header().Set("content-type", "application/json")
+		w.WriteHeader(http.StatusCreated)
+		fmt.Fprintf(w, `
+{
+	"success": true,
+	"errors": [],
+	"messages": [],
+	"result": {
+		"id": "0d89c70d-ad9f-4843-b99f-6cc0252067e9",
+		"hostname": "app.example.com",
+		"custom_origin_server": "example.app.com",
+		"ssl": {
+			"status": "pending_validation",
+			"method": "cname",
+			"type": "dv",
+			"cname_target": "dcv.digicert.com",
+			"cname": "810b7d5f01154524b961ba0cd578acc2.app.example.com",
+			"settings": {
+			"http2": "off",
+			"tls_1_3": "on"
+			}
+		}
+  	}
+}`)
+	})
+
+	response, err := client.UpdateCustomHostname("foo", "0d89c70d-ad9f-4843-b99f-6cc0252067e9", CustomHostname{Hostname: "app.example.com", CustomOriginServer: "example.app.com", SSL: CustomHostnameSSL{Method: "cname", Type: "dv"}})
+
+	want := &CustomHostnameResponse{
+		Result: CustomHostname{
+			ID:                 "0d89c70d-ad9f-4843-b99f-6cc0252067e9",
+			Hostname:           "app.example.com",
+			CustomOriginServer: "example.app.com",
+			SSL: CustomHostnameSSL{
+				Type:        "dv",
+				Method:      "cname",
+				Status:      "pending_validation",
+				CnameTarget: "dcv.digicert.com",
+				CnameName:   "810b7d5f01154524b961ba0cd578acc2.app.example.com",
+				Settings: CustomHostnameSSLSettings{
+					HTTP2: "off",
+					TLS13: "on",
+				},
+			},
+		},
+		Response: Response{Success: true, Errors: []ResponseInfo{}, Messages: []ResponseInfo{}},
+	}
+
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, response)
+	}
+}
+
 func TestCustomHostname_CustomHostnameFallbackOrigin(t *testing.T) {
 	setup()
 	defer teardown()