Repository: cloudflare/cloudflare-go
Pull request #3740 (2024-12-16 23:03:10)

Feat/certificate authorities
## Description

Add methods to interact with Certificate Authorities Hostname Associations API

## Has your change been tested?

Tested using automated tests

## Types of changes

What sort of change does your code introduce/modify?

- [ ] 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.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [x] I have added tests to cover my changes.
- [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.

[1]: https://help.github.com/articles/closing-issues-using-keywords/



Patch:
diff --git a/.changelog/3740.txt b/.changelog/3740.txt
new file mode 100644
index 000000000..50dc548e0
--- /dev/null
+++ b/.changelog/3740.txt
@@ -0,0 +1,3 @@
+```release-note:enhancement
+certificate_authorities: add new methods to interact with Certificate Authorities Hostname Associations API
+```
\ No newline at end of file
diff --git a/certificate_authorities.go b/certificate_authorities.go
new file mode 100644
index 000000000..771199e10
--- /dev/null
+++ b/certificate_authorities.go
@@ -0,0 +1,74 @@
+package cloudflare
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+
+	"github.com/goccy/go-json"
+)
+
+type ListCertificateAuthoritiesHostnameAssociationsParams struct {
+	MTLSCertificateID string `url:"mtls_certificate_id,omitempty"`
+}
+
+type UpdateCertificateAuthoritiesHostnameAssociationsParams struct {
+	Hostnames         []HostnameAssociation `json:"hostnames,omitempty"`
+	MTLSCertificateID string                `json:"mtls_certificate_id,omitempty"`
+}
+
+type HostnameAssociationsResponse struct {
+	Response
+	Result []HostnameAssociation `json:"result"`
+}
+
+type HostnameAssociation = string
+
+// List Hostname Associations
+//
+// API Reference: https://developers.cloudflare.com/api/operations/client-certificate-for-a-zone-list-hostname-associations
+func (api *API) ListCertificateAuthoritiesHostnameAssociations(ctx context.Context, rc *ResourceContainer, params ListCertificateAuthoritiesHostnameAssociationsParams) ([]HostnameAssociation, error) {
+
+	uri := fmt.Sprintf(
+		"/%s/%s/certificate_authorities/hostname_associations",
+		rc.Level,
+		rc.Identifier,
+	)
+
+	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
+	if err != nil {
+		return []HostnameAssociation{}, fmt.Errorf("%s: %w", errMakeRequestError, err)
+	}
+
+	var hostnameAssociationsResponse HostnameAssociationsResponse
+	err = json.Unmarshal(res, &hostnameAssociationsResponse)
+	if err != nil {
+		return []HostnameAssociation{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return hostnameAssociationsResponse.Result, nil
+}
+
+// Replace Hostname Associations
+//
+// API Reference: https://developers.cloudflare.com/api/operations/client-certificate-for-a-zone-put-hostname-associations
+func (api *API) UpdateCertificateAuthoritiesHostnameAssociations(ctx context.Context, rc *ResourceContainer, params UpdateCertificateAuthoritiesHostnameAssociationsParams) ([]HostnameAssociation, error) {
+	uri := fmt.Sprintf(
+		"/%s/%s/certificate_authorities/hostname_associations",
+		rc.Level,
+		rc.Identifier,
+	)
+
+	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params)
+	if err != nil {
+		return []HostnameAssociation{}, fmt.Errorf("%s: %w", errMakeRequestError, err)
+	}
+
+	var hostnameAssociationsResponse HostnameAssociationsResponse
+	err = json.Unmarshal(res, &hostnameAssociationsResponse)
+	if err != nil {
+		return []HostnameAssociation{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
+	}
+
+	return hostnameAssociationsResponse.Result, nil
+}


Test patch:
diff --git a/certificate_authorities_test.go b/certificate_authorities_test.go
new file mode 100644
index 000000000..940524a5b
--- /dev/null
+++ b/certificate_authorities_test.go
@@ -0,0 +1,85 @@
+package cloudflare
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestListCertificateAuthoritiesHostnameAssociations(t *testing.T) {
+	setup()
+	defer teardown()
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprintf(w, `{
+			"success": true,
+			"errors": [],
+			"messages": [],
+			"result": [
+				"admin.example.com",
+				"foobar.example.com"
+			]
+		}`)
+	}
+
+	hostnameAssociations := ListCertificateAuthoritiesHostnameAssociationsParams{
+		MTLSCertificateID: "72ef4d06-4752-4493-a60a-7421470fd585",
+	}
+
+	want := []HostnameAssociation{
+		"admin.example.com",
+		"foobar.example.com",
+	}
+
+	mux.HandleFunc("/zones/"+testZoneID+"/certificate_authorities/hostname_associations", handler)
+
+	actual, err := client.ListCertificateAuthoritiesHostnameAssociations(context.Background(), testZoneRC, hostnameAssociations)
+
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, actual)
+	}
+}
+
+func TestUpdateCertificateAuthoritiesHostnameAssociations(t *testing.T) {
+	setup()
+	defer teardown()
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method)
+		w.Header().Set("content-type", "application/json")
+		fmt.Fprintf(w, `{
+			"success": true,
+			"errors": [],
+			"messages": [],
+			"result": [
+				"admin.example.com",
+				"foobar.example.com"
+			]
+		}`)
+	}
+
+	hostnameAssociations := UpdateCertificateAuthoritiesHostnameAssociationsParams{
+		Hostnames: []HostnameAssociation{
+			"admin.example.com",
+			"foobar.example.com",
+		},
+	}
+
+	want := []HostnameAssociation{
+		"admin.example.com",
+		"foobar.example.com",
+	}
+
+	mux.HandleFunc("/zones/"+testZoneID+"/certificate_authorities/hostname_associations", handler)
+
+	actual, err := client.UpdateCertificateAuthoritiesHostnameAssociations(context.Background(), testZoneRC, hostnameAssociations)
+
+	if assert.NoError(t, err) {
+		assert.Equal(t, want, actual)
+	}
+}