Repository: mailgun/mailgun-go (go)

Issue:
The ListIPs call's return value is incomplete
As per the [List account IPs](https://documentation.mailgun.com/docs/mailgun/api-reference/openapi-final/tag/IPs/#tag/IPs/operation/GET-v3-ips) endpoint's documentation there are additional response fields which are currently unsupported by `mailgun-go`. Such fields are `assignable_to_pools`, and `is_on_warmup` within the `details` list.

There seems to be two ways to extend the current functionality:

1. Changing the return value of the `ListIPs` function's return type to resemble the API response more closely. This would yield a breaking change in the public API, so probably would come with a major version bump.
2. Introducing a new `IPAddress`-ish mtype which contains the `assignable_to_pools` and `is_on_warmup` flags. This would be separate from the existing `IPAddress` mtype which has the `dedicated` flag. Changing the return type to the new mtype would be just a minor change, as the `ListIPs` function currently only populates the `IP` property, even if the `dedicated` flag is set in the call.

Please let me know which is the desired way forward or if there is a third option which would be better. I am happy to do the implementation.

Pull request:
adding the is_on_warmup and assignable_to_pools status flags to the ListIP response
Closes #417 

Please see the conversation about the approach in the issue.

Patch:
diff --git a/ips.go b/ips.go
index 0ac25d7..2b7fc18 100644
--- a/ips.go
+++ b/ips.go
@@ -2,12 +2,13 @@ package mailgun
 
 import (
 	"context"
+	"slices"
 	"strconv"
 
 	"github.com/mailgun/mailgun-go/v5/mtypes"
 )
 
-// ListIPs returns a list of IPs assigned to your account
+// ListIPs returns a list of IPs assigned to your account, including their warmup and assignable to pools status if applicable.
 func (mg *Client) ListIPs(ctx context.Context, dedicated, enabled bool) ([]mtypes.IPAddress, error) {
 	r := newHTTPRequest(generateApiUrl(mg, 3, ipsEndpoint))
 	r.setClient(mg.HTTPClient())
@@ -25,7 +26,11 @@ func (mg *Client) ListIPs(ctx context.Context, dedicated, enabled bool) ([]mtype
 	}
 	var result []mtypes.IPAddress
 	for _, ip := range resp.Items {
-		result = append(result, mtypes.IPAddress{IP: ip})
+		assignableToPools := slices.Index(resp.AssignableToPools, ip) != -1
+		detailsIndex := slices.IndexFunc(resp.Details, func(d mtypes.IPAddressListResponseDetail) bool { return d.IP == ip })
+		isOnWarmup := resp.Details[detailsIndex].IsOnWarmup
+		ipState := mtypes.IPAddress{IP: ip, AssignableToPools: assignableToPools, IsOnWarmup: isOnWarmup}
+		result = append(result, ipState)
 	}
 	return result, nil
 }
diff --git a/mocks/mock_ips.go b/mocks/mock_ips.go
index 96025d4..584c322 100644
--- a/mocks/mock_ips.go
+++ b/mocks/mock_ips.go
@@ -21,8 +21,13 @@ func (ms *Server) addIPRoutes(r chi.Router) {
 
 func (ms *Server) listIPS(w http.ResponseWriter, _ *http.Request) {
 	toJSON(w, mtypes.IPAddressListResponse{
-		TotalCount: 2,
-		Items:      []string{"172.0.0.1", "192.168.1.1"},
+		TotalCount:        2,
+		Items:             []string{"172.0.0.1", "192.168.1.1"},
+		AssignableToPools: []string{"172.0.0.1", "192.168.1.1"},
+		Details: []mtypes.IPAddressListResponseDetail{
+			{IP: "172.0.0.1", IsOnWarmup: true},
+			{IP: "192.168.1.1"},
+		},
 	})
 }
 
diff --git a/mtypes/ips.go b/mtypes/ips.go
index a3a0fdf..9592cf0 100644
--- a/mtypes/ips.go
+++ b/mtypes/ips.go
@@ -1,14 +1,23 @@
 package mtypes
 
 type IPAddressListResponse struct {
-	TotalCount int      `json:"total_count"`
-	Items      []string `json:"items"`
+	TotalCount        int                           `json:"total_count"`
+	Items             []string                      `json:"items"`
+	Details           []IPAddressListResponseDetail `json:"details"`
+	AssignableToPools []string                      `json:"assignable_to_pools,omitempty"`
+}
+
+type IPAddressListResponseDetail struct {
+	IP         string `json:"ip"`
+	IsOnWarmup bool   `json:"is_on_warmup,omitempty"`
 }
 
 type IPAddress struct {
-	IP        string `json:"ip"`
-	RDNS      string `json:"rdns"`
-	Dedicated bool   `json:"dedicated"`
+	IP                string `json:"ip"`                            // Always present
+	RDNS              string `json:"rdns,omitempty"`                // Filled by GetIP
+	Dedicated         bool   `json:"dedicated,omitempty"`           // Filled by GetIP
+	AssignableToPools bool   `json:"assignable_to_pools,omitempty"` // Filled by ListIPS
+	IsOnWarmup        bool   `json:"is_on_warmup,omitempty"`        // Filled by ListIPS
 }
 
 type ListIPDomainsResponse struct {


Test patch:
diff --git a/ips_test.go b/ips_test.go
index b6cd608..685e66a 100644
--- a/ips_test.go
+++ b/ips_test.go
@@ -24,7 +24,8 @@ func TestListIPs(t *testing.T) {
 	require.NoError(t, err)
 
 	assert.Equal(t, list[0].IP, ip.IP)
-	assert.True(t, ip.Dedicated)
+	assert.True(t, list[0].IsOnWarmup)
+	assert.False(t, list[1].IsOnWarmup)
 	assert.Equal(t, "luna.mailgun.net", ip.RDNS)
 }