1package godo
2
3import "fmt"
4
5const imageBasePath = "v2/images"
6
7// ImagesService is an interface for interfacing with the images
8// endpoints of the DigitalOcean API
9// See: https://developers.digitalocean.com/documentation/v2#images
10type ImagesService interface {
11	List(*ListOptions) ([]Image, *Response, error)
12	ListDistribution(opt *ListOptions) ([]Image, *Response, error)
13	ListApplication(opt *ListOptions) ([]Image, *Response, error)
14	ListUser(opt *ListOptions) ([]Image, *Response, error)
15	GetByID(int) (*Image, *Response, error)
16	GetBySlug(string) (*Image, *Response, error)
17	Update(int, *ImageUpdateRequest) (*Image, *Response, error)
18	Delete(int) (*Response, error)
19}
20
21// ImagesServiceOp handles communication with the image related methods of the
22// DigitalOcean API.
23type ImagesServiceOp struct {
24	client *Client
25}
26
27var _ ImagesService = &ImagesServiceOp{}
28
29// Image represents a DigitalOcean Image
30type Image struct {
31	ID           int      `json:"id,float64,omitempty"`
32	Name         string   `json:"name,omitempty"`
33	Type         string   `json:"type,omitempty"`
34	Distribution string   `json:"distribution,omitempty"`
35	Slug         string   `json:"slug,omitempty"`
36	Public       bool     `json:"public,omitempty"`
37	Regions      []string `json:"regions,omitempty"`
38	MinDiskSize  int      `json:"min_disk_size,omitempty"`
39	Created      string   `json:"created_at,omitempty"`
40}
41
42// ImageUpdateRequest represents a request to update an image.
43type ImageUpdateRequest struct {
44	Name string `json:"name"`
45}
46
47type imageRoot struct {
48	Image Image
49}
50
51type imagesRoot struct {
52	Images []Image
53	Links  *Links `json:"links"`
54}
55
56type listImageOptions struct {
57	Private bool   `url:"private,omitempty"`
58	Type    string `url:"type,omitempty"`
59}
60
61func (i Image) String() string {
62	return Stringify(i)
63}
64
65// List lists all the images available.
66func (s *ImagesServiceOp) List(opt *ListOptions) ([]Image, *Response, error) {
67	return s.list(opt, nil)
68}
69
70// ListDistribution lists all the distribution images.
71func (s *ImagesServiceOp) ListDistribution(opt *ListOptions) ([]Image, *Response, error) {
72	listOpt := listImageOptions{Type: "distribution"}
73	return s.list(opt, &listOpt)
74}
75
76// ListApplication lists all the application images.
77func (s *ImagesServiceOp) ListApplication(opt *ListOptions) ([]Image, *Response, error) {
78	listOpt := listImageOptions{Type: "application"}
79	return s.list(opt, &listOpt)
80}
81
82// ListUser lists all the user images.
83func (s *ImagesServiceOp) ListUser(opt *ListOptions) ([]Image, *Response, error) {
84	listOpt := listImageOptions{Private: true}
85	return s.list(opt, &listOpt)
86}
87
88// GetByID retrieves an image by id.
89func (s *ImagesServiceOp) GetByID(imageID int) (*Image, *Response, error) {
90	if imageID < 1 {
91		return nil, nil, NewArgError("imageID", "cannot be less than 1")
92	}
93
94	return s.get(interface{}(imageID))
95}
96
97// GetBySlug retrieves an image by slug.
98func (s *ImagesServiceOp) GetBySlug(slug string) (*Image, *Response, error) {
99	if len(slug) < 1 {
100		return nil, nil, NewArgError("slug", "cannot be blank")
101	}
102
103	return s.get(interface{}(slug))
104}
105
106// Update an image name.
107func (s *ImagesServiceOp) Update(imageID int, updateRequest *ImageUpdateRequest) (*Image, *Response, error) {
108	if imageID < 1 {
109		return nil, nil, NewArgError("imageID", "cannot be less than 1")
110	}
111
112	if updateRequest == nil {
113		return nil, nil, NewArgError("updateRequest", "cannot be nil")
114	}
115
116	path := fmt.Sprintf("%s/%d", imageBasePath, imageID)
117	req, err := s.client.NewRequest("PUT", path, updateRequest)
118	if err != nil {
119		return nil, nil, err
120	}
121
122	root := new(imageRoot)
123	resp, err := s.client.Do(req, root)
124	if err != nil {
125		return nil, resp, err
126	}
127
128	return &root.Image, resp, err
129}
130
131// Delete an image.
132func (s *ImagesServiceOp) Delete(imageID int) (*Response, error) {
133	if imageID < 1 {
134		return nil, NewArgError("imageID", "cannot be less than 1")
135	}
136
137	path := fmt.Sprintf("%s/%d", imageBasePath, imageID)
138
139	req, err := s.client.NewRequest("DELETE", path, nil)
140	if err != nil {
141		return nil, err
142	}
143
144	resp, err := s.client.Do(req, nil)
145
146	return resp, err
147}
148
149// Helper method for getting an individual image
150func (s *ImagesServiceOp) get(ID interface{}) (*Image, *Response, error) {
151	path := fmt.Sprintf("%s/%v", imageBasePath, ID)
152
153	req, err := s.client.NewRequest("GET", path, nil)
154	if err != nil {
155		return nil, nil, err
156	}
157
158	root := new(imageRoot)
159	resp, err := s.client.Do(req, root)
160	if err != nil {
161		return nil, resp, err
162	}
163
164	return &root.Image, resp, err
165}
166
167// Helper method for listing images
168func (s *ImagesServiceOp) list(opt *ListOptions, listOpt *listImageOptions) ([]Image, *Response, error) {
169	path := imageBasePath
170	path, err := addOptions(path, opt)
171	if err != nil {
172		return nil, nil, err
173	}
174	path, err = addOptions(path, listOpt)
175	if err != nil {
176		return nil, nil, err
177	}
178
179	req, err := s.client.NewRequest("GET", path, nil)
180	if err != nil {
181		return nil, nil, err
182	}
183
184	root := new(imagesRoot)
185	resp, err := s.client.Do(req, root)
186	if err != nil {
187		return nil, resp, err
188	}
189	if l := root.Links; l != nil {
190		resp.Links = l
191	}
192
193	return root.Images, resp, err
194}
195