Repository: hetznercloud/hcloud-go
Pull request #14 (2017-12-08 09:28:19)

Add server types client


Patch:
diff --git a/hcloud/client.go b/hcloud/client.go
index d07195c..4795dcd 100644
--- a/hcloud/client.go
+++ b/hcloud/client.go
@@ -54,6 +54,7 @@ type Client struct {
 	Action     ActionClient
 	FloatingIP FloatingIPClient
 	Server     ServerClient
+	ServerType ServerTypeClient
 	SSHKey     SSHKeyClient
 }
 
@@ -97,6 +98,7 @@ func NewClient(options ...ClientOption) *Client {
 	client.FloatingIP = FloatingIPClient{client: client}
 	client.Server = ServerClient{client: client}
 	client.SSHKey = SSHKeyClient{client: client}
+	client.ServerType = ServerTypeClient{client: client}
 
 	return client
 }
diff --git a/hcloud/schema.go b/hcloud/schema.go
index 86640c9..188beeb 100644
--- a/hcloud/schema.go
+++ b/hcloud/schema.go
@@ -139,7 +139,7 @@ func ServerPublicNetIPv6DNSPtrFromSchema(s schema.ServerPublicNetIPv6DNSPtr) Ser
 
 // ServerTypeFromSchema converts a schema.ServerType to a ServerType.
 func ServerTypeFromSchema(s schema.ServerType) *ServerType {
-	return &ServerType{
+	st := &ServerType{
 		ID:          s.ID,
 		Name:        s.Name,
 		Description: s.Description,
@@ -148,6 +148,8 @@ func ServerTypeFromSchema(s schema.ServerType) *ServerType {
 		Disk:        s.Disk,
 		StorageType: StorageType(s.StorageType),
 	}
+
+	return st
 }
 
 // SSHKeyFromSchema converts a schema.SSHKey to a SSHKey.
diff --git a/hcloud/schema/server_type.go b/hcloud/schema/server_type.go
index 9a2b703..8441a0d 100644
--- a/hcloud/schema/server_type.go
+++ b/hcloud/schema/server_type.go
@@ -10,3 +10,15 @@ type ServerType struct {
 	Disk        int     `json:"disk"`
 	StorageType string  `json:"storage_type"`
 }
+
+// ServerTypeListResponse defines the schema of the response when
+// listing server types.
+type ServerTypeListResponse struct {
+	ServerTypes []ServerType `json:"server_types"`
+}
+
+// ServerTypeGetResponse defines the schema of the response when
+// retrieving a single server type.
+type ServerTypeGetResponse struct {
+	ServerType ServerType `json:"server_type"`
+}
diff --git a/hcloud/server_type.go b/hcloud/server_type.go
index 0c7dee6..277e414 100644
--- a/hcloud/server_type.go
+++ b/hcloud/server_type.go
@@ -1,5 +1,12 @@
 package hcloud
 
+import (
+	"context"
+	"fmt"
+
+	"github.com/hetznercloud/hcloud-go/hcloud/schema"
+)
+
 // ServerType represents a server type in the Hetzner Cloud.
 type ServerType struct {
 	ID          int
@@ -21,3 +28,71 @@ const (
 	// StorageTypeCeph is the type for remote storage.
 	StorageTypeCeph = "ceph"
 )
+
+// ServerTypeClient is a client for the server types API.
+type ServerTypeClient struct {
+	client *Client
+}
+
+// Get retrieves a server type.
+func (c *ServerTypeClient) Get(ctx context.Context, id int) (*ServerType, *Response, error) {
+	req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/server_types/%d", id), nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var body schema.ServerTypeGetResponse
+	resp, err := c.client.Do(req, &body)
+	if err != nil {
+		return nil, nil, err
+	}
+	return ServerTypeFromSchema(body.ServerType), resp, nil
+}
+
+// ServerTypeListOpts specifies options for listing server types.
+type ServerTypeListOpts struct {
+	ListOpts
+}
+
+// List returns a list of server types for a specific page.
+func (c *ServerTypeClient) List(ctx context.Context, opts ServerTypeListOpts) ([]*ServerType, *Response, error) {
+	path := "/server_types?" + valuesForListOpts(opts.ListOpts).Encode()
+	req, err := c.client.NewRequest(ctx, "GET", path, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var body schema.ServerTypeListResponse
+	resp, err := c.client.Do(req, &body)
+	if err != nil {
+		return nil, nil, err
+	}
+	serverTypes := make([]*ServerType, 0, len(body.ServerTypes))
+	for _, s := range body.ServerTypes {
+		serverTypes = append(serverTypes, ServerTypeFromSchema(s))
+	}
+	return serverTypes, resp, nil
+}
+
+// All returns all server types.
+func (c *ServerTypeClient) All(ctx context.Context) ([]*ServerType, error) {
+	allServerTypes := []*ServerType{}
+
+	opts := ServerTypeListOpts{}
+	opts.PerPage = 50
+
+	_, err := c.client.all(func(page int) (*Response, error) {
+		opts.Page = page
+		serverTypes, resp, err := c.List(ctx, opts)
+		if err != nil {
+			return resp, err
+		}
+		allServerTypes = append(allServerTypes, serverTypes...)
+		return resp, nil
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	return allServerTypes, nil
+}


Test patch:
diff --git a/hcloud/server_type_test.go b/hcloud/server_type_test.go
new file mode 100644
index 0000000..f45122e
--- /dev/null
+++ b/hcloud/server_type_test.go
@@ -0,0 +1,163 @@
+package hcloud
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/hetznercloud/hcloud-go/hcloud/schema"
+)
+
+func TestServerTypeClient(t *testing.T) {
+	t.Run("Get", func(t *testing.T) {
+		env := newTestEnv()
+		defer env.Teardown()
+
+		env.Mux.HandleFunc("/server_types/1", func(w http.ResponseWriter, r *http.Request) {
+			json.NewEncoder(w).Encode(schema.ServerTypeGetResponse{
+				ServerType: schema.ServerType{
+					ID: 1,
+				},
+			})
+		})
+
+		ctx := context.Background()
+		serverType, _, err := env.Client.ServerType.Get(ctx, 1)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if serverType == nil {
+			t.Fatal("no server type")
+		}
+		if serverType.ID != 1 {
+			t.Errorf("unexpected server type ID: %v", serverType.ID)
+		}
+	})
+
+	t.Run("List", func(t *testing.T) {
+		env := newTestEnv()
+		defer env.Teardown()
+
+		env.Mux.HandleFunc("/server_types", func(w http.ResponseWriter, r *http.Request) {
+			if page := r.URL.Query().Get("page"); page != "2" {
+				t.Errorf("expected page 2; got %q", page)
+			}
+			if perPage := r.URL.Query().Get("per_page"); perPage != "50" {
+				t.Errorf("expected per_page 50; got %q", perPage)
+			}
+			json.NewEncoder(w).Encode(schema.ServerTypeListResponse{
+				ServerTypes: []schema.ServerType{
+					{ID: 1},
+					{ID: 2},
+				},
+			})
+		})
+
+		opts := ServerTypeListOpts{}
+		opts.Page = 2
+		opts.PerPage = 50
+
+		ctx := context.Background()
+		serverTypes, _, err := env.Client.ServerType.List(ctx, opts)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if len(serverTypes) != 2 {
+			t.Fatal("expected 2 server types")
+		}
+	})
+
+	t.Run("All", func(t *testing.T) {
+		env := newTestEnv()
+		defer env.Teardown()
+
+		firstRequest := true
+		env.Mux.HandleFunc("/server_types", func(w http.ResponseWriter, r *http.Request) {
+			w.Header().Set("Content-Type", "application/json")
+			if firstRequest {
+				firstRequest = false
+				w.WriteHeader(http.StatusTooManyRequests)
+				fmt.Fprint(w, `{
+					"error": {
+						"code": "limit_reached",
+						"message": "ratelimited"
+					}
+				}`)
+				return
+			}
+
+			switch page := r.URL.Query().Get("page"); page {
+			case "", "1":
+				fmt.Fprint(w, `{
+					"server_types": [
+						{
+							"id": 1
+						}
+					],
+					"meta": {
+						"pagination": {
+							"page": 1,
+							"per_page": 1,
+							"previous_page": null,
+							"next_page": 2,
+							"last_page": 3,
+							"total_entries": 3
+						}
+					}
+				}`)
+			case "2":
+				fmt.Fprint(w, `{
+					"server_types": [
+						{
+							"id": 2
+						}
+					],
+					"meta": {
+						"pagination": {
+							"page": 2,
+							"per_page": 1,
+							"previous_page": 1,
+							"next_page": 3,
+							"last_page": 3,
+							"total_entries": 3
+						}
+					}
+				}`)
+			case "3":
+				fmt.Fprint(w, `{
+					"server_types": [
+						{
+							"id": 3
+						}
+					],
+					"meta": {
+						"pagination": {
+							"page": 3,
+							"per_page": 1,
+							"previous_page": 2,
+							"next_page": null,
+							"last_page": 3,
+							"total_entries": 3
+						}
+					}
+				}`)
+			default:
+				panic("bad page")
+			}
+		})
+
+		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 {
+			t.Errorf("")
+		}
+	})
+}