Repository: hetznercloud/hcloud-go
Pull request #8 (2017-12-04 14:54:01)

Define and use meta schema


Patch:
diff --git a/hcloud/client.go b/hcloud/client.go
index b47bd70..77dacb1 100644
--- a/hcloud/client.go
+++ b/hcloud/client.go
@@ -13,6 +13,8 @@ import (
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/hetznercloud/hcloud-go/hcloud/schema"
 )
 
 // UserAgent is the value for the User-Agent header sent with each request.
@@ -202,7 +204,7 @@ func errorFromResponse(resp *http.Response, body []byte) error {
 // Response represents a response from the API. It embeds http.Response.
 type Response struct {
 	*http.Response
-	Meta ResponseMeta
+	Meta Meta
 }
 
 // ReadBody reads and returns the response's body. After reading the response's body
@@ -232,28 +234,27 @@ func (r *Response) readMeta(body []byte) error {
 	}
 
 	if strings.HasPrefix(r.Header.Get("Content-Type"), "application/json") {
-		var m struct {
-			Meta struct {
-				Pagination ResponseMetaPagination `json:"pagination"`
-			} `json:"meta"`
-		}
-		if err := json.Unmarshal(body, &m); err != nil {
+		var s schema.MetaResponse
+		if err := json.Unmarshal(body, &s); err != nil {
 			return err
 		}
-		r.Meta.Pagination = &m.Meta.Pagination
+		if s.Meta.Pagination != nil {
+			p := PaginationFromSchema(*s.Meta.Pagination)
+			r.Meta.Pagination = &p
+		}
 	}
 
 	return nil
 }
 
-// ResponseMeta represents meta information included in an API response.
-type ResponseMeta struct {
-	Pagination *ResponseMetaPagination
-	Ratelimit  ResponseMetaRatelimit
+// Meta represents meta information included in an API response.
+type Meta struct {
+	Pagination *Pagination
+	Ratelimit  Ratelimit
 }
 
-// ResponseMetaPagination represents pagination meta data.
-type ResponseMetaPagination struct {
+// Pagination represents pagination meta information.
+type Pagination struct {
 	Page         int
 	PerPage      int
 	PreviousPage int
@@ -262,33 +263,20 @@ type ResponseMetaPagination struct {
 	TotalEntries int
 }
 
-// UnmarshalJSON implements json.Unmarshaler.
-func (p *ResponseMetaPagination) UnmarshalJSON(data []byte) error {
-	var v struct {
-		Page         int `json:"page"`
-		PerPage      int `json:"per_page"`
-		PreviousPage int `json:"previous_page"`
-		NextPage     int `json:"next_page"`
-		LastPage     int `json:"last_page"`
-		TotalEntries int `json:"total_entries"`
-	}
-
-	if err := json.Unmarshal(data, &v); err != nil {
-		return err
+// PaginationFromSchema converts a schema.MetaPagination to a Pagination.
+func PaginationFromSchema(s schema.MetaPagination) Pagination {
+	return Pagination{
+		Page:         s.Page,
+		PerPage:      s.PerPage,
+		PreviousPage: s.PreviousPage,
+		NextPage:     s.NextPage,
+		LastPage:     s.LastPage,
+		TotalEntries: s.TotalEntries,
 	}
-
-	p.Page = v.Page
-	p.PerPage = v.PerPage
-	p.PreviousPage = v.PreviousPage
-	p.NextPage = v.NextPage
-	p.LastPage = v.LastPage
-	p.TotalEntries = v.TotalEntries
-
-	return nil
 }
 
-// ResponseMetaRatelimit represents ratelimit information.
-type ResponseMetaRatelimit struct {
+// Ratelimit represents ratelimit information.
+type Ratelimit struct {
 	Limit     int
 	Remaining int
 	Reset     time.Time
diff --git a/hcloud/schema/meta.go b/hcloud/schema/meta.go
new file mode 100644
index 0000000..9b06cda
--- /dev/null
+++ b/hcloud/schema/meta.go
@@ -0,0 +1,23 @@
+package schema
+
+// Meta defines the schema of meta information which may be included
+// in responses.
+type Meta struct {
+	Pagination *MetaPagination `json:"pagination"`
+}
+
+// MetaPagination defines the schema of pagination information.
+type MetaPagination struct {
+	Page         int `json:"page"`
+	PerPage      int `json:"per_page"`
+	PreviousPage int `json:"previous_page"`
+	NextPage     int `json:"next_page"`
+	LastPage     int `json:"last_page"`
+	TotalEntries int `json:"total_entries"`
+}
+
+// MetaResponse defines the schema of a response containing
+// meta information.
+type MetaResponse struct {
+	Meta Meta `json:"meta"`
+}


Test patch:
diff --git a/hcloud/client_test.go b/hcloud/client_test.go
index 31e3c0d..3f65ce5 100644
--- a/hcloud/client_test.go
+++ b/hcloud/client_test.go
@@ -9,6 +9,8 @@ import (
 	"strings"
 	"testing"
 	"time"
+
+	"github.com/hetznercloud/hcloud-go/hcloud/schema"
 )
 
 type testEnv struct {
@@ -154,73 +156,38 @@ func TestClientMetaNonJSON(t *testing.T) {
 	}
 }
 
-func TestResponseMetaPaginationUnmarshalJSON(t *testing.T) {
-	testCases := []struct {
-		Data       []byte
-		Pagination ResponseMetaPagination
-	}{
-		{
-			Data: []byte(`{
-				"page": 1,
-				"per_page": 25,
-				"previous_page": null,
-				"next_page": null,
-				"last_page": 1,
-				"total_entries": 5
-		        }`),
-			Pagination: ResponseMetaPagination{
-				Page:         1,
-				PerPage:      25,
-				PreviousPage: 0,
-				NextPage:     0,
-				LastPage:     1,
-				TotalEntries: 5,
-			},
-		},
-		{
-			Data: []byte(`{
-				"page": 2,
-				"per_page": 25,
-				"previous_page": 1,
-				"next_page": 3,
-				"last_page": 13,
-				"total_entries": 322
-		        }`),
-			Pagination: ResponseMetaPagination{
-				Page:         2,
-				PerPage:      25,
-				PreviousPage: 1,
-				NextPage:     3,
-				LastPage:     13,
-				TotalEntries: 322,
-			},
-		},
-	}
-
-	for i, testCase := range testCases {
-		t.Run(fmt.Sprintf("test case %d", i+1), func(t *testing.T) {
-			var p ResponseMetaPagination
-			if err := json.Unmarshal(testCase.Data, &p); err != nil {
-				t.Fatal(err)
-			}
-			if p.Page != testCase.Pagination.Page {
-				t.Errorf("unexpected page: %v", p.Page)
-			}
-			if p.PerPage != testCase.Pagination.PerPage {
-				t.Errorf("unexpected per page: %v", p.PerPage)
-			}
-			if p.PreviousPage != testCase.Pagination.PreviousPage {
-				t.Errorf("unexpected previous page: %v", p.PreviousPage)
-			}
-			if p.NextPage != testCase.Pagination.NextPage {
-				t.Errorf("unexpected next page: %d", p.NextPage)
-			}
-			if p.LastPage != testCase.Pagination.LastPage {
-				t.Errorf("unexpected last page: %d", p.LastPage)
-			}
-			if p.TotalEntries != testCase.Pagination.TotalEntries {
-				t.Errorf("unexpected total entries: %d", p.TotalEntries)
-			}
-		})
+func TestPaginationFromSchema(t *testing.T) {
+	data := []byte(`{
+		"page": 2,
+		"per_page": 25,
+		"previous_page": 1,
+		"next_page": 3,
+		"last_page": 13,
+		"total_entries": 322
+	}`)
+
+	var s schema.MetaPagination
+	if err := json.Unmarshal(data, &s); err != nil {
+		t.Fatal(err)
+	}
+	p := PaginationFromSchema(s)
+
+	if p.Page != 2 {
+		t.Errorf("unexpected page: %v", p.Page)
+	}
+	if p.PerPage != 25 {
+		t.Errorf("unexpected per page: %v", p.PerPage)
+	}
+	if p.PreviousPage != 1 {
+		t.Errorf("unexpected previous page: %v", p.PreviousPage)
+	}
+	if p.NextPage != 3 {
+		t.Errorf("unexpected next page: %d", p.NextPage)
+	}
+	if p.LastPage != 13 {
+		t.Errorf("unexpected last page: %d", p.LastPage)
+	}
+	if p.TotalEntries != 322 {
+		t.Errorf("unexpected total entries: %d", p.TotalEntries)
 	}
 }