Repository: digitalocean/godo

Issue:
request ListAssociatedResourcesForDeletion for Droplet
Hi Team,

Just like what have been done for Kubernetes at [here](https://github.com/digitalocean/godo/blob/main/kubernetes.go#L708), can you add the same function to Droplet?

[official API doc](https://docs.digitalocean.com/reference/api/digitalocean/#tag/Droplets/operation/droplets_list_associatedResources)


Discussion:
@anup-deka: Hi @fushuang001 ,
Thanks for raising this request 👍 @fyzanshaik: @anup-deka 👋 @aniketsingh1023: assign @anup-deka @fyzanshaik @spargy4050v: Hi @fyzanshaik can you assign me for the Hacktoberfest @Jatkingmodern: const (
    kubernetesClustersPath = "v2/kubernetes/clusters"
    kubernetesOptionsPath  = "v2/kubernetes/options"
)
can you assign me this issue could make the same service for Droplets.go@fyzanshaik: @aniketsingh1023 @spargy4050v @Jatkingmodern
there's an existing PR already please go through it!

Solution patch:
diff --git a/droplets.go b/droplets.go
index 2ddd7d6..61a3fb6 100644
--- a/droplets.go
+++ b/droplets.go
@@ -33,6 +33,7 @@ type DropletsService interface {
 	GetBackupPolicy(context.Context, int) (*DropletBackupPolicy, *Response, error)
 	ListBackupPolicies(context.Context, *ListOptions) (map[int]*DropletBackupPolicy, *Response, error)
 	ListSupportedBackupPolicies(context.Context) ([]*SupportedBackupPolicy, *Response, error)
+	ListAssociatedResourcesForDeletion(context.Context, int) (*DropletAssociatedResources, *Response, error)
 }
 
 // DropletsServiceOp handles communication with the Droplet related methods of the
@@ -263,6 +264,26 @@ type DropletBackupPolicyRequest struct {
 	Hour    *int   `json:"hour,omitempty"`
 }
 
+// DropletAssociatedResource represents a billable resource associated with a Droplet.
+type DropletAssociatedResource struct {
+	ID   string `json:"id"`
+	Name string `json:"name"`
+	Cost string `json:"cost"`
+}
+
+// DropletAssociatedResources represents the associated billable resources that can be destroyed along with a Droplet.
+type DropletAssociatedResources struct {
+	ReservedIPs     []*DropletAssociatedResource `json:"reserved_ips"`
+	FloatingIPs     []*DropletAssociatedResource `json:"floating_ips"`
+	Snapshots       []*DropletAssociatedResource `json:"snapshots"`
+	Volumes         []*DropletAssociatedResource `json:"volumes"`
+	VolumeSnapshots []*DropletAssociatedResource `json:"volume_snapshots"`
+}
+
+func (a DropletAssociatedResources) String() string {
+	return Stringify(a)
+}
+
 func (d DropletCreateRequest) String() string {
 	return Stringify(d)
 }
@@ -621,6 +642,27 @@ func (s *DropletsServiceOp) Neighbors(ctx context.Context, dropletID int) ([]Dro
 	return root.Droplets, resp, err
 }
 
+// ListAssociatedResourcesForDeletion lists a Droplet's associated resources that can be destroyed along with the Droplet.
+// Associated resources include reserved IPs, floating IPs, snapshots, volumes, and volume snapshots.
+func (s *DropletsServiceOp) ListAssociatedResourcesForDeletion(ctx context.Context, dropletID int) (*DropletAssociatedResources, *Response, error) {
+	if dropletID < 1 {
+		return nil, nil, NewArgError("dropletID", "cannot be less than 1")
+	}
+	path := fmt.Sprintf("%s/%d/destroy_with_associated_resources", dropletBasePath, dropletID)
+	req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	root := new(DropletAssociatedResources)
+	resp, err := s.client.Do(ctx, req, root)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return root, resp, nil
+}
+
 func (s *DropletsServiceOp) dropletActionStatus(ctx context.Context, uri string) (string, error) {
 	action, _, err := s.client.DropletActions.GetByURI(ctx, uri)
 



Test patch:
diff --git a/droplets_test.go b/droplets_test.go
index 2b510ac..528cfe8 100644
--- a/droplets_test.go
+++ b/droplets_test.go
@@ -1079,3 +1079,95 @@ func TestDroplets_ListSupportedBackupPolicies(t *testing.T) {
 	require.NoError(t, err)
 	assert.Equal(t, []*SupportedBackupPolicy{&testSupportedBackupPolicy}, policies)
 }
+
+func TestDroplets_ListAssociatedResourcesForDeletion(t *testing.T) {
+	setup()
+	defer teardown()
+
+	ctx := context.Background()
+
+	expectedResources := &DropletAssociatedResources{
+		ReservedIPs: []*DropletAssociatedResource{
+			{
+				ID:   "6186916",
+				Name: "45.55.96.47",
+				Cost: "4.00",
+			},
+		},
+		FloatingIPs: []*DropletAssociatedResource{
+			{
+				ID:   "6186916",
+				Name: "45.55.96.47",
+				Cost: "4.00",
+			},
+		},
+		Snapshots: []*DropletAssociatedResource{
+			{
+				ID:   "61486916",
+				Name: "ubuntu-s-1vcpu-1gb-nyc1-01-1585758823330",
+				Cost: "0.05",
+			},
+		},
+		Volumes: []*DropletAssociatedResource{
+			{
+				ID:   "ba49449a-7435-11ea-b89e-0a58ac14480f",
+				Name: "volume-nyc1-01",
+				Cost: "10.00",
+			},
+		},
+		VolumeSnapshots: []*DropletAssociatedResource{
+			{
+				ID:   "edb0478d-7436-11ea-86e6-0a58ac144b91",
+				Name: "volume-nyc1-01-1585758983629",
+				Cost: "0.04",
+			},
+		},
+	}
+
+	jBlob := `{
+		"reserved_ips": [
+			{
+				"id": "6186916",
+				"name": "45.55.96.47",
+				"cost": "4.00"
+			}
+		],
+		"floating_ips": [
+			{
+				"id": "6186916",
+				"name": "45.55.96.47",
+				"cost": "4.00"
+			}
+		],
+		"snapshots": [
+			{
+				"id": "61486916",
+				"name": "ubuntu-s-1vcpu-1gb-nyc1-01-1585758823330",
+				"cost": "0.05"
+			}
+		],
+		"volumes": [
+			{
+				"id": "ba49449a-7435-11ea-b89e-0a58ac14480f",
+				"name": "volume-nyc1-01",
+				"cost": "10.00"
+			}
+		],
+		"volume_snapshots": [
+			{
+				"id": "edb0478d-7436-11ea-86e6-0a58ac144b91",
+				"name": "volume-nyc1-01-1585758983629",
+				"cost": "0.04"
+			}
+		]
+	}`
+
+	mux.HandleFunc("/v2/droplets/12345/destroy_with_associated_resources", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		fmt.Fprint(w, jBlob)
+	})
+
+	resources, _, err := client.Droplets.ListAssociatedResourcesForDeletion(ctx, 12345)
+	require.NoError(t, err)
+	require.Equal(t, expectedResources, resources)
+}