Repository: hetznercloud/hcloud-go (go)

Issue:
feat: Make sure all Clients have .AllWithOpts method
We recently realized that some clients are missing the `.AllWithOpts` method (see #254). This is still missing for other clients:

- `DatacenterClient`
- `LoadBalancerTypeClient`
- `LocationClient`
- `ServerTypeClient`

Pull request:
feat: provide `.AllWithOpts` method for all clients
This PR adds an implementation of the `.AllWithOpts` method to following clients:
- `DatacenterClient`
- `LoadBalancerTypeClient`
- `LocationClient`
- `ServerTypeClient`

Fixes #256 

Patch:
diff --git a/hcloud/datacenter.go b/hcloud/datacenter.go
index 5a841f9..369ac94 100644
--- a/hcloud/datacenter.go
+++ b/hcloud/datacenter.go
@@ -47,7 +47,7 @@ func (c *DatacenterClient) GetByID(ctx context.Context, id int) (*Datacenter, *R
 	return DatacenterFromSchema(body.Datacenter), resp, nil
 }
 
-// GetByName retrieves an datacenter by its name. If the datacenter does not exist, nil is returned.
+// GetByName retrieves a datacenter by its name. If the datacenter does not exist, nil is returned.
 func (c *DatacenterClient) GetByName(ctx context.Context, name string) (*Datacenter, *Response, error) {
 	if name == "" {
 		return nil, nil, nil
@@ -111,10 +111,12 @@ func (c *DatacenterClient) List(ctx context.Context, opts DatacenterListOpts) ([
 
 // All returns all datacenters.
 func (c *DatacenterClient) All(ctx context.Context) ([]*Datacenter, error) {
-	allDatacenters := []*Datacenter{}
+	return c.AllWithOpts(ctx, DatacenterListOpts{ListOpts: ListOpts{PerPage: 50}})
+}
 
-	opts := DatacenterListOpts{}
-	opts.PerPage = 50
+// AllWithOpts returns all datacenters for the given options.
+func (c *DatacenterClient) AllWithOpts(ctx context.Context, opts DatacenterListOpts) ([]*Datacenter, error) {
+	var allDatacenters []*Datacenter
 
 	err := c.client.all(func(page int) (*Response, error) {
 		opts.Page = page
diff --git a/hcloud/load_balancer_type.go b/hcloud/load_balancer_type.go
index 5ac7481..348d4bf 100644
--- a/hcloud/load_balancer_type.go
+++ b/hcloud/load_balancer_type.go
@@ -108,10 +108,12 @@ func (c *LoadBalancerTypeClient) List(ctx context.Context, opts LoadBalancerType
 
 // All returns all Load Balancer types.
 func (c *LoadBalancerTypeClient) All(ctx context.Context) ([]*LoadBalancerType, error) {
-	allLoadBalancerTypes := []*LoadBalancerType{}
+	return c.AllWithOpts(ctx, LoadBalancerTypeListOpts{ListOpts: ListOpts{PerPage: 50}})
+}
 
-	opts := LoadBalancerTypeListOpts{}
-	opts.PerPage = 50
+// AllWithOpts returns all Load Balancer types for the given options.
+func (c *LoadBalancerTypeClient) AllWithOpts(ctx context.Context, opts LoadBalancerTypeListOpts) ([]*LoadBalancerType, error) {
+	var allLoadBalancerTypes []*LoadBalancerType
 
 	err := c.client.all(func(page int) (*Response, error) {
 		opts.Page = page
diff --git a/hcloud/location.go b/hcloud/location.go
index c749bdf..9577830 100644
--- a/hcloud/location.go
+++ b/hcloud/location.go
@@ -108,10 +108,12 @@ func (c *LocationClient) List(ctx context.Context, opts LocationListOpts) ([]*Lo
 
 // All returns all locations.
 func (c *LocationClient) All(ctx context.Context) ([]*Location, error) {
-	allLocations := []*Location{}
+	return c.AllWithOpts(ctx, LocationListOpts{ListOpts: ListOpts{PerPage: 50}})
+}
 
-	opts := LocationListOpts{}
-	opts.PerPage = 50
+// AllWithOpts returns all locations for the given options.
+func (c *LocationClient) AllWithOpts(ctx context.Context, opts LocationListOpts) ([]*Location, error) {
+	var allLocations []*Location
 
 	err := c.client.all(func(page int) (*Response, error) {
 		opts.Page = page
diff --git a/hcloud/server_type.go b/hcloud/server_type.go
index 2a5c90a..1ac0128 100644
--- a/hcloud/server_type.go
+++ b/hcloud/server_type.go
@@ -134,10 +134,12 @@ func (c *ServerTypeClient) List(ctx context.Context, opts ServerTypeListOpts) ([
 
 // All returns all server types.
 func (c *ServerTypeClient) All(ctx context.Context) ([]*ServerType, error) {
-	allServerTypes := []*ServerType{}
+	return c.AllWithOpts(ctx, ServerTypeListOpts{ListOpts: ListOpts{PerPage: 50}})
+}
 
-	opts := ServerTypeListOpts{}
-	opts.PerPage = 50
+// AllWithOpts returns all server types for the given options.
+func (c *ServerTypeClient) AllWithOpts(ctx context.Context, opts ServerTypeListOpts) ([]*ServerType, error) {
+	var allServerTypes []*ServerType
 
 	err := c.client.all(func(page int) (*Response, error) {
 		opts.Page = page


Test patch:
diff --git a/hcloud/datacenter_test.go b/hcloud/datacenter_test.go
index a5ef7ad..c258f37 100644
--- a/hcloud/datacenter_test.go
+++ b/hcloud/datacenter_test.go
@@ -227,4 +227,47 @@ func TestDatacenterClient(t *testing.T) {
 			t.Errorf("unexpected datacenters")
 		}
 	})
+
+	t.Run("AllWithOpts", func(t *testing.T) {
+		env := newTestEnv()
+		defer env.Teardown()
+
+		env.Mux.HandleFunc("/datacenters", func(w http.ResponseWriter, r *http.Request) {
+			if name := r.URL.Query().Get("name"); name != "my-datacenter" {
+				t.Errorf("unexpected name: %s", name)
+			}
+			w.Header().Set("Content-Type", "application/json")
+			json.NewEncoder(w).Encode(struct {
+				Datacenters []schema.Datacenter `json:"datacenters"`
+				Meta        schema.Meta         `json:"meta"`
+			}{
+				Datacenters: []schema.Datacenter{
+					{ID: 1},
+					{ID: 2},
+					{ID: 3},
+				},
+				Meta: schema.Meta{
+					Pagination: &schema.MetaPagination{
+						Page:         1,
+						LastPage:     1,
+						PerPage:      3,
+						TotalEntries: 3,
+					},
+				},
+			})
+		})
+
+		ctx := context.Background()
+		opts := DatacenterListOpts{Name: "my-datacenter"}
+		datacenters, err := env.Client.Datacenter.AllWithOpts(ctx, opts)
+		if err != nil {
+			t.Fatalf("Datacenter.List failed: %s", err)
+		}
+		if len(datacenters) != 3 {
+			t.Fatalf("expected 3 datacenters; got %d", len(datacenters))
+		}
+		if datacenters[0].ID != 1 || datacenters[1].ID != 2 || datacenters[2].ID != 3 {
+			t.Errorf("unexpected datacenters")
+		}
+	})
 }
diff --git a/hcloud/load_balancer_type_test.go b/hcloud/load_balancer_type_test.go
index 5f906b6..7f4610f 100644
--- a/hcloud/load_balancer_type_test.go
+++ b/hcloud/load_balancer_type_test.go
@@ -223,4 +223,47 @@ func TestLoadBalancerTypeClient(t *testing.T) {
 			t.Errorf("unexpected load balancer types")
 		}
 	})
+
+	t.Run("AllWithOpts", func(t *testing.T) {
+		env := newTestEnv()
+		defer env.Teardown()
+
+		env.Mux.HandleFunc("/load_balancer_types", func(w http.ResponseWriter, r *http.Request) {
+			if name := r.URL.Query().Get("name"); name != "my-load-balancer-type" {
+				t.Errorf("unexpected name: %s", name)
+			}
+			w.Header().Set("Content-Type", "application/json")
+			json.NewEncoder(w).Encode(struct {
+				LoadBalancerTypes []schema.LoadBalancerType `json:"load_balancer_types"`
+				Meta              schema.Meta               `json:"meta"`
+			}{
+				LoadBalancerTypes: []schema.LoadBalancerType{
+					{ID: 1},
+					{ID: 2},
+					{ID: 3},
+				},
+				Meta: schema.Meta{
+					Pagination: &schema.MetaPagination{
+						Page:         1,
+						LastPage:     1,
+						PerPage:      3,
+						TotalEntries: 3,
+					},
+				},
+			})
+		})
+
+		ctx := context.Background()
+		opts := LoadBalancerTypeListOpts{Name: "my-load-balancer-type"}
+		loadBalancerTypes, err := env.Client.LoadBalancerType.AllWithOpts(ctx, opts)
+		if err != nil {
+			t.Fatalf("LoadBalancerType.List failed: %s", err)
+		}
+		if len(loadBalancerTypes) != 3 {
+			t.Fatalf("expected 3 load balancer types; got %d", len(loadBalancerTypes))
+		}
+		if loadBalancerTypes[0].ID != 1 || loadBalancerTypes[1].ID != 2 || loadBalancerTypes[2].ID != 3 {
+			t.Errorf("unexpected load balancer types")
+		}
+	})
 }
diff --git a/hcloud/location_test.go b/hcloud/location_test.go
index 3b91a35..4f6d7fb 100644
--- a/hcloud/location_test.go
+++ b/hcloud/location_test.go
@@ -227,4 +227,47 @@ func TestLocationClient(t *testing.T) {
 			t.Errorf("unexpected locations")
 		}
 	})
+
+	t.Run("AllWithOpts", func(t *testing.T) {
+		env := newTestEnv()
+		defer env.Teardown()
+
+		env.Mux.HandleFunc("/locations", func(w http.ResponseWriter, r *http.Request) {
+			if name := r.URL.Query().Get("name"); name != "my-location" {
+				t.Errorf("unexpected name: %s", name)
+			}
+			w.Header().Set("Content-Type", "application/json")
+			json.NewEncoder(w).Encode(struct {
+				Locations []schema.Location `json:"locations"`
+				Meta      schema.Meta       `json:"meta"`
+			}{
+				Locations: []schema.Location{
+					{ID: 1},
+					{ID: 2},
+					{ID: 3},
+				},
+				Meta: schema.Meta{
+					Pagination: &schema.MetaPagination{
+						Page:         1,
+						LastPage:     1,
+						PerPage:      3,
+						TotalEntries: 3,
+					},
+				},
+			})
+		})
+
+		ctx := context.Background()
+		opts := LocationListOpts{Name: "my-location"}
+		locations, err := env.Client.Location.AllWithOpts(ctx, opts)
+		if err != nil {
+			t.Fatalf("Location.List failed: %s", err)
+		}
+		if len(locations) != 3 {
+			t.Fatalf("expected 3 locations; got %d", len(locations))
+		}
+		if locations[0].ID != 1 || locations[1].ID != 2 || locations[2].ID != 3 {
+			t.Errorf("unexpected locations")
+		}
+	})
 }
diff --git a/hcloud/server_type_test.go b/hcloud/server_type_test.go
index b2fbfea..acb40ac 100644
--- a/hcloud/server_type_test.go
+++ b/hcloud/server_type_test.go
@@ -223,4 +223,43 @@ func TestServerTypeClient(t *testing.T) {
 			t.Errorf("unexpected server types")
 		}
 	})
+
+	t.Run("AllWithOpts", func(t *testing.T) {
+		env := newTestEnv()
+		defer env.Teardown()
+
+		env.Mux.HandleFunc("/server_types", func(w http.ResponseWriter, r *http.Request) {
+			w.Header().Set("Content-Type", "application/json")
+			json.NewEncoder(w).Encode(struct {
+				ServerTypes []schema.ServerType `json:"server_types"`
+				Meta        schema.Meta         `json:"meta"`
+			}{
+				ServerTypes: []schema.ServerType{
+					{ID: 1},
+					{ID: 2},
+					{ID: 3},
+				},
+				Meta: schema.Meta{
+					Pagination: &schema.MetaPagination{
+						Page:         1,
+						LastPage:     3,
+						PerPage:      3,
+						TotalEntries: 3,
+					},
+				},
+			})
+		})
+
+		ctx := context.Background()
+		serverTypes, err := env.Client.ServerType.All(ctx)
+		if err != nil {
+			t.Fatalf("ServerTypes.List failed: %s", err)
+		}
+		if len(serverTypes) != 3 {
+			t.Fatalf("expected 3 server types; got %d", len(serverTypes))
+		}
+		if serverTypes[0].ID != 1 || serverTypes[1].ID != 2 || serverTypes[2].ID != 3 {
+			t.Errorf("unexpected server types")
+		}
+	})
 }