Add missing APIs to Images service.

The following were added:

* Images.ListDistribution() for GET /v2/images?type=distribution
* Images.ListApplication() for GET /v2/images?type=application
* Images.ListUser() for GET /v2/images?private=true
* Images.GetByID() for GET /v2/images/12345
* Images.GetBySlug() for GET /v2/images/ubuntu
* Images.Update() for PUT /v2/images/12345
* Images.Delete() for DELETE /v2/images/12345
This commit is contained in:
Raphael Simon 2015-04-20 18:28:27 -07:00
parent 8dc1f54d1a
commit fc8ecd5bc6
3 changed files with 284 additions and 4 deletions

View File

@ -218,10 +218,12 @@ func TestDroplet_String(t *testing.T) {
image := &Image{
ID: 1,
Name: "Image",
Type: "snapshot",
Distribution: "Ubuntu",
Slug: "image",
Public: true,
Regions: []string{"one", "two"},
MinDiskSize: 20,
}
size := &Size{
@ -257,7 +259,7 @@ func TestDroplet_String(t *testing.T) {
}
stringified := droplet.String()
expected := `godo.Droplet{ID:1, Name:"droplet", Memory:123, Vcpus:456, Disk:789, Region:godo.Region{Slug:"region", Name:"Region", Sizes:["1" "2"], Available:true}, Image:godo.Image{ID:1, Name:"Image", Distribution:"Ubuntu", Slug:"image", Public:true, Regions:["one" "two"]}, Size:godo.Size{Slug:"size", Memory:0, Vcpus:0, Disk:0, PriceMonthly:123, PriceHourly:456, Regions:["1" "2"]}, BackupIDs:[1], SnapshotIDs:[1], Locked:false, Status:"active", Networks:godo.Networks{V4:[godo.NetworkV4{IPAddress:"192.168.1.2", Netmask:"255.255.255.0", Gateway:"192.168.1.1", Type:""}]}, ActionIDs:[1], Created:""}`
expected := `godo.Droplet{ID:1, Name:"droplet", Memory:123, Vcpus:456, Disk:789, Region:godo.Region{Slug:"region", Name:"Region", Sizes:["1" "2"], Available:true}, Image:godo.Image{ID:1, Name:"Image", Type:"snapshot", Distribution:"Ubuntu", Slug:"image", Public:true, Regions:["one" "two"], MinDiskSize:20}, Size:godo.Size{Slug:"size", Memory:0, Vcpus:0, Disk:0, PriceMonthly:123, PriceHourly:456, Regions:["1" "2"]}, BackupIDs:[1], SnapshotIDs:[1], Locked:false, Status:"active", Networks:godo.Networks{V4:[godo.NetworkV4{IPAddress:"192.168.1.2", Netmask:"255.255.255.0", Gateway:"192.168.1.1", Type:""}]}, ActionIDs:[1], Created:""}`
if expected != stringified {
t.Errorf("Droplet.String returned %+v, expected %+v", stringified, expected)
}

113
images.go
View File

@ -1,10 +1,21 @@
package godo
import "fmt"
const imageBasePath = "v2/images"
// ImagesService is an interface for interfacing with the images
// endpoints of the Digital Ocean API
// See: https://developers.digitalocean.com/documentation/v2#images
type ImagesService interface {
List(*ListOptions) ([]Image, *Response, error)
ListDistribution(opt *ListOptions) ([]Image, *Response, error)
ListApplication(opt *ListOptions) ([]Image, *Response, error)
ListUser(opt *ListOptions) ([]Image, *Response, error)
GetByID(int) (*Image, *Response, error)
GetBySlug(string) (*Image, *Response, error)
Update(int, *ImageUpdateRequest) (*Image, *Response, error)
Delete(int) (*Response, error)
}
// ImagesServiceOp handles communication with the image related methods of the
@ -19,10 +30,17 @@ var _ ImagesService = &ImagesServiceOp{}
type Image struct {
ID int `json:"id,float64,omitempty"`
Name string `json:"name,omitempty"`
Type string `json:"type,omitempty"`
Distribution string `json:"distribution,omitempty"`
Slug string `json:"slug,omitempty"`
Public bool `json:"public,omitempty"`
Regions []string `json:"regions,omitempty"`
MinDiskSize int `json:"min_disk_size,omitempty"`
}
// ImageUpdateRequest represents a request to update an image.
type ImageUpdateRequest struct {
Name string `json:"name"`
}
type imageRoot struct {
@ -34,17 +52,108 @@ type imagesRoot struct {
Links *Links `json:"links"`
}
type listImageOptions struct {
Private bool `url:"private,omitempty"`
Type string `url:"type,omitempty"`
}
func (i Image) String() string {
return Stringify(i)
}
// List all sizes
// List all images
func (s *ImagesServiceOp) List(opt *ListOptions) ([]Image, *Response, error) {
path := "v2/images"
return s.list(opt, nil)
}
// List distribution images
func (s *ImagesServiceOp) ListDistribution(opt *ListOptions) ([]Image, *Response, error) {
listOpt := listImageOptions{Type: "distribution"}
return s.list(opt, &listOpt)
}
// List application images
func (s *ImagesServiceOp) ListApplication(opt *ListOptions) ([]Image, *Response, error) {
listOpt := listImageOptions{Type: "application"}
return s.list(opt, &listOpt)
}
// List user images
func (s *ImagesServiceOp) ListUser(opt *ListOptions) ([]Image, *Response, error) {
listOpt := listImageOptions{Private: true}
return s.list(opt, &listOpt)
}
// Get individual image by id
func (s *ImagesServiceOp) GetByID(imageID int) (*Image, *Response, error) {
return s.get(interface{}(imageID))
}
// Get individual image by slug
func (s *ImagesServiceOp) GetBySlug(slug string) (*Image, *Response, error) {
return s.get(interface{}(slug))
}
// Update an image name
func (s *ImagesServiceOp) Update(imageID int, updateRequest *ImageUpdateRequest) (*Image, *Response, error) {
path := fmt.Sprintf("%s/%d", imageBasePath, imageID)
req, err := s.client.NewRequest("PUT", path, updateRequest)
if err != nil {
return nil, nil, err
}
root := new(imageRoot)
resp, err := s.client.Do(req, root)
if err != nil {
return nil, resp, err
}
return &root.Image, resp, err
}
// Delete image
func (s *ImagesServiceOp) Delete(imageID int) (*Response, error) {
path := fmt.Sprintf("%s/%d", imageBasePath, imageID)
req, err := s.client.NewRequest("DELETE", path, nil)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
return resp, err
}
// Helper method for getting an individual image
func (s *ImagesServiceOp) get(ID interface{}) (*Image, *Response, error) {
path := fmt.Sprintf("%s/%v", imageBasePath, ID)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
root := new(imageRoot)
resp, err := s.client.Do(req, root)
if err != nil {
return nil, resp, err
}
return &root.Image, resp, err
}
// Helper method for listing images
func (s *ImagesServiceOp) list(opt *ListOptions, listOpt *listImageOptions) ([]Image, *Response, error) {
path := imageBasePath
path, err := addOptions(path, opt)
if err != nil {
return nil, nil, err
}
path, err = addOptions(path, listOpt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {

View File

@ -1,6 +1,7 @@
package godo
import (
"encoding/json"
"fmt"
"net/http"
"reflect"
@ -27,6 +28,82 @@ func TestImages_List(t *testing.T) {
}
}
func TestImages_ListDistribution(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/v2/images", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
expected := "distribution"
actual := r.URL.Query().Get("type")
if actual != expected {
t.Errorf("'type' query = %v, expected %v", actual, expected)
}
fmt.Fprint(w, `{"images":[{"id":1},{"id":2}]}`)
})
images, _, err := client.Images.ListDistribution(nil)
if err != nil {
t.Errorf("Images.ListDistribution returned error: %v", err)
}
expected := []Image{{ID: 1}, {ID: 2}}
if !reflect.DeepEqual(images, expected) {
t.Errorf("Images.ListDistribution returned %+v, expected %+v", images, expected)
}
}
func TestImages_ListApplication(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/v2/images", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
expected := "application"
actual := r.URL.Query().Get("type")
if actual != expected {
t.Errorf("'type' query = %v, expected %v", actual, expected)
}
fmt.Fprint(w, `{"images":[{"id":1},{"id":2}]}`)
})
images, _, err := client.Images.ListApplication(nil)
if err != nil {
t.Errorf("Images.ListApplication returned error: %v", err)
}
expected := []Image{{ID: 1}, {ID: 2}}
if !reflect.DeepEqual(images, expected) {
t.Errorf("Images.ListApplication returned %+v, expected %+v", images, expected)
}
}
func TestImages_ListUser(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/v2/images", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
expected := "true"
actual := r.URL.Query().Get("private")
if actual != expected {
t.Errorf("'private' query = %v, expected %v", actual, expected)
}
fmt.Fprint(w, `{"images":[{"id":1},{"id":2}]}`)
})
images, _, err := client.Images.ListUser(nil)
if err != nil {
t.Errorf("Images.ListUser returned error: %v", err)
}
expected := []Image{{ID: 1}, {ID: 2}}
if !reflect.DeepEqual(images, expected) {
t.Errorf("Images.ListUser returned %+v, expected %+v", images, expected)
}
}
func TestImages_ListImagesMultiplePages(t *testing.T) {
setup()
defer teardown()
@ -74,18 +151,110 @@ func TestImages_RetrievePageByNumber(t *testing.T) {
checkCurrentPage(t, resp, 2)
}
func TestImages_GetImageByID(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/v2/images/12345", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `{"image":{"id":12345}}`)
})
images, _, err := client.Images.GetByID(12345)
if err != nil {
t.Errorf("Image.GetByID returned error: %v", err)
}
expected := &Image{ID: 12345}
if !reflect.DeepEqual(images, expected) {
t.Errorf("Images.GetByID returned %+v, expected %+v", images, expected)
}
}
func TestImages_GetImageBySlug(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/v2/images/ubuntu", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `{"image":{"id":12345}}`)
})
images, _, err := client.Images.GetBySlug("ubuntu")
if err != nil {
t.Errorf("Image.GetBySlug returned error: %v", err)
}
expected := &Image{ID: 12345}
if !reflect.DeepEqual(images, expected) {
t.Errorf("Images.Get returned %+v, expected %+v", images, expected)
}
}
func TestImages_Update(t *testing.T) {
setup()
defer teardown()
updateRequest := &ImageUpdateRequest{
Name: "name",
}
mux.HandleFunc("/v2/images/12345", func(w http.ResponseWriter, r *http.Request) {
expected := map[string]interface{}{
"name": "name",
}
var v map[string]interface{}
err := json.NewDecoder(r.Body).Decode(&v)
if err != nil {
t.Fatalf("decode json: %v", err)
}
if !reflect.DeepEqual(v, expected) {
t.Errorf("Request body = %#v, expected %#v", v, expected)
}
fmt.Fprintf(w, `{"image":{"id":1}}`)
})
image, _, err := client.Images.Update(12345, updateRequest)
if err != nil {
t.Errorf("Images.Update returned error: %v", err)
} else {
if id := image.ID; id != 1 {
t.Errorf("expected id '%d', received '%d'", 1, id)
}
}
}
func TestImages_Destroy(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/v2/images/12345", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
})
_, err := client.Images.Delete(12345)
if err != nil {
t.Errorf("Image.Delete returned error: %v", err)
}
}
func TestImage_String(t *testing.T) {
image := &Image{
ID: 1,
Name: "Image",
Type: "snapshot",
Distribution: "Ubuntu",
Slug: "image",
Public: true,
Regions: []string{"one", "two"},
MinDiskSize: 20,
}
stringified := image.String()
expected := `godo.Image{ID:1, Name:"Image", Distribution:"Ubuntu", Slug:"image", Public:true, Regions:["one" "two"]}`
expected := `godo.Image{ID:1, Name:"Image", Type:"snapshot", Distribution:"Ubuntu", Slug:"image", Public:true, Regions:["one" "two"], MinDiskSize:20}`
if expected != stringified {
t.Errorf("Image.String returned %+v, expected %+v", stringified, expected)
}