Jon Perritt | e7b86d1 | 2015-01-16 20:37:11 -0700 | [diff] [blame] | 1 | package services |
| 2 | |
| 3 | import ( |
| 4 | "github.com/racker/perigee" |
| 5 | "github.com/rackspace/gophercloud" |
| 6 | "github.com/rackspace/gophercloud/pagination" |
| 7 | ) |
| 8 | |
| 9 | // ListOptsBuilder allows extensions to add additional parameters to the |
| 10 | // List request. |
| 11 | type ListOptsBuilder interface { |
| 12 | ToCDNServiceListQuery() (string, error) |
| 13 | } |
| 14 | |
| 15 | // ListOpts allows the filtering and sorting of paginated collections through |
| 16 | // the API. Marker and Limit are used for pagination. |
| 17 | type ListOpts struct { |
| 18 | Marker string `q:"marker"` |
| 19 | Limit int `q:"limit"` |
| 20 | } |
| 21 | |
| 22 | // ToCDNServiceListQuery formats a ListOpts into a query string. |
| 23 | func (opts ListOpts) ToCDNServiceListQuery() (string, error) { |
| 24 | q, err := gophercloud.BuildQueryString(opts) |
| 25 | if err != nil { |
| 26 | return "", err |
| 27 | } |
| 28 | return q.String(), nil |
| 29 | } |
| 30 | |
| 31 | // List returns a Pager which allows you to iterate over a collection of |
| 32 | // CDN services. It accepts a ListOpts struct, which allows for pagination via |
| 33 | // marker and limit. |
| 34 | func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { |
| 35 | url := listURL(c) |
| 36 | if opts != nil { |
| 37 | query, err := opts.ToCDNServiceListQuery() |
| 38 | if err != nil { |
| 39 | return pagination.Pager{Err: err} |
| 40 | } |
| 41 | url += query |
| 42 | } |
| 43 | |
| 44 | createPage := func(r pagination.PageResult) pagination.Page { |
| 45 | p := ServicePage{pagination.MarkerPageBase{PageResult: r}} |
| 46 | p.MarkerPageBase.Owner = p |
| 47 | return p |
| 48 | } |
| 49 | |
| 50 | pager := pagination.NewPager(c, url, createPage) |
| 51 | return pager |
| 52 | } |
| 53 | |
| 54 | // CreateOptsBuilder is the interface options structs have to satisfy in order |
| 55 | // to be used in the main Create operation in this package. Since many |
| 56 | // extensions decorate or modify the common logic, it is useful for them to |
| 57 | // satisfy a basic interface in order for them to be used. |
| 58 | type CreateOptsBuilder interface { |
| 59 | ToCDNServiceCreateMap() (map[string]interface{}, error) |
| 60 | } |
| 61 | |
| 62 | // CreateOpts is the common options struct used in this package's Create |
| 63 | // operation. |
| 64 | type CreateOpts struct { |
| 65 | // REQUIRED. Specifies the name of the service. The minimum length for name is |
| 66 | // 3. The maximum length is 256. |
| 67 | Name string |
| 68 | // REQUIRED. Specifies a list of domains used by users to access their website. |
| 69 | Domains []Domain |
| 70 | // REQUIRED. Specifies a list of origin domains or IP addresses where the |
| 71 | // original assets are stored. |
| 72 | Origins []Origin |
| 73 | // REQUIRED. Specifies the CDN provider flavor ID to use. For a list of |
| 74 | // flavors, see the operation to list the available flavors. The minimum |
| 75 | // length for flavor_id is 1. The maximum length is 256. |
| 76 | FlavorID string |
| 77 | // OPTIONAL. Specifies the TTL rules for the assets under this service. Supports wildcards for fine-grained control. |
Jon Perritt | 0bd2373 | 2015-01-19 20:58:57 -0700 | [diff] [blame^] | 78 | Caching []CacheRule |
Jon Perritt | e7b86d1 | 2015-01-16 20:37:11 -0700 | [diff] [blame] | 79 | // OPTIONAL. Specifies the restrictions that define who can access assets (content from the CDN cache). |
| 80 | Restrictions []Restriction |
| 81 | } |
| 82 | |
| 83 | // ToCDNServiceCreateMap casts a CreateOpts struct to a map. |
| 84 | func (opts CreateOpts) ToCDNServiceCreateMap() (map[string]interface{}, error) { |
| 85 | s := make(map[string]interface{}) |
| 86 | |
| 87 | if opts.Name == "" { |
| 88 | return nil, no("Name") |
| 89 | } |
| 90 | s["name"] = opts.Name |
| 91 | |
| 92 | if opts.Domains == nil { |
| 93 | return nil, no("Domains") |
| 94 | } |
| 95 | for _, domain := range opts.Domains { |
| 96 | if domain.Domain == "" { |
| 97 | return nil, no("Domains[].Domain") |
| 98 | } |
| 99 | } |
| 100 | s["domains"] = opts.Domains |
| 101 | |
| 102 | if opts.Origins == nil { |
| 103 | return nil, no("Origins") |
| 104 | } |
| 105 | for _, origin := range opts.Origins { |
| 106 | if origin.Origin == "" { |
| 107 | return nil, no("Origins[].Origin") |
| 108 | } |
| 109 | if origin.Rules == nil { |
| 110 | return nil, no("Origins[].Rules") |
| 111 | } |
| 112 | for _, rule := range origin.Rules { |
| 113 | if rule.Name == "" { |
| 114 | return nil, no("Origins[].Rules[].Name") |
| 115 | } |
| 116 | if rule.RequestURL == "" { |
| 117 | return nil, no("Origins[].Rules[].RequestURL") |
| 118 | } |
| 119 | } |
| 120 | } |
| 121 | s["origins"] = opts.Origins |
| 122 | |
| 123 | if opts.FlavorID == "" { |
| 124 | return nil, no("FlavorID") |
| 125 | } |
| 126 | s["flavor_id"] = opts.FlavorID |
| 127 | |
| 128 | if opts.Caching != nil { |
| 129 | for _, cache := range opts.Caching { |
| 130 | if cache.Name == "" { |
| 131 | return nil, no("Caching[].Name") |
| 132 | } |
| 133 | if cache.Rules != nil { |
| 134 | for _, rule := range cache.Rules { |
| 135 | if rule.Name == "" { |
| 136 | return nil, no("Caching[].Rules[].Name") |
| 137 | } |
| 138 | if rule.RequestURL == "" { |
| 139 | return nil, no("Caching[].Rules[].RequestURL") |
| 140 | } |
| 141 | } |
| 142 | } |
| 143 | } |
| 144 | s["caching"] = opts.Caching |
| 145 | } |
| 146 | |
| 147 | if opts.Restrictions != nil { |
| 148 | for _, restriction := range opts.Restrictions { |
| 149 | if restriction.Name == "" { |
| 150 | return nil, no("Restrictions[].Name") |
| 151 | } |
| 152 | if restriction.Rules != nil { |
| 153 | for _, rule := range restriction.Rules { |
| 154 | if rule.Name == "" { |
| 155 | return nil, no("Restrictions[].Rules[].Name") |
| 156 | } |
| 157 | } |
| 158 | } |
| 159 | } |
| 160 | s["restrictions"] = opts.Restrictions |
| 161 | } |
| 162 | |
| 163 | return s, nil |
| 164 | } |
| 165 | |
| 166 | // Create accepts a CreateOpts struct and creates a new CDN service using the |
| 167 | // values provided. |
| 168 | func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult { |
| 169 | var res CreateResult |
| 170 | |
| 171 | reqBody, err := opts.ToCDNServiceCreateMap() |
| 172 | if err != nil { |
| 173 | res.Err = err |
| 174 | return res |
| 175 | } |
| 176 | |
| 177 | // Send request to API |
| 178 | _, res.Err = perigee.Request("POST", createURL(c), perigee.Options{ |
| 179 | MoreHeaders: c.AuthenticatedHeaders(), |
| 180 | ReqBody: &reqBody, |
| 181 | OkCodes: []int{202}, |
| 182 | }) |
| 183 | return res |
| 184 | } |
| 185 | |
| 186 | // Get retrieves a specific service based on its unique ID. |
| 187 | func Get(c *gophercloud.ServiceClient, id string) GetResult { |
| 188 | var res GetResult |
| 189 | _, res.Err = perigee.Request("GET", getURL(c, id), perigee.Options{ |
| 190 | MoreHeaders: c.AuthenticatedHeaders(), |
| 191 | Results: &res.Body, |
| 192 | OkCodes: []int{200}, |
| 193 | }) |
| 194 | return res |
| 195 | } |
| 196 | |
| 197 | // UpdateOptsBuilder is the interface options structs have to satisfy in order |
| 198 | // to be used in the main Update operation in this package. Since many |
| 199 | // extensions decorate or modify the common logic, it is useful for them to |
| 200 | // satisfy a basic interface in order for them to be used. |
| 201 | type UpdateOptsBuilder interface { |
Jon Perritt | 608d3a5 | 2015-01-19 10:38:30 -0700 | [diff] [blame] | 202 | ToCDNServiceUpdateMap() ([]map[string]interface{}, error) |
Jon Perritt | e7b86d1 | 2015-01-16 20:37:11 -0700 | [diff] [blame] | 203 | } |
| 204 | |
| 205 | // Op represents an update operation. |
| 206 | type Op string |
| 207 | |
| 208 | var ( |
| 209 | // Add is a constant used for performing a "add" operation when updating. |
| 210 | Add Op = "add" |
| 211 | // Remove is a constant used for performing a "remove" operation when updating. |
| 212 | Remove Op = "remove" |
| 213 | // Replace is a constant used for performing a "replace" operation when updating. |
| 214 | Replace Op = "replace" |
| 215 | ) |
| 216 | |
| 217 | // UpdateOpts represents the attributes used when updating an existing CDN service. |
| 218 | type UpdateOpts []UpdateOpt |
| 219 | |
| 220 | // UpdateOpt represents a single update to an existing service. Multiple updates |
| 221 | // to a service can be submitted at the same time. See UpdateOpts. |
| 222 | type UpdateOpt struct { |
| 223 | // Specifies the update operation to perform. |
| 224 | Op Op |
| 225 | // Specifies the JSON Pointer location within the service's JSON representation |
| 226 | // of the service parameter being added, replaced or removed. |
| 227 | Path string |
| 228 | // Specifies the actual value to be added or replaced. It is not required for |
| 229 | // the remove operation. |
| 230 | Value map[string]interface{} |
| 231 | } |
| 232 | |
| 233 | // ToCDNServiceUpdateMap casts an UpdateOpts struct to a map. |
Jon Perritt | 608d3a5 | 2015-01-19 10:38:30 -0700 | [diff] [blame] | 234 | func (opts UpdateOpts) ToCDNServiceUpdateMap() ([]map[string]interface{}, error) { |
| 235 | s := make([]map[string]interface{}, len(opts)) |
Jon Perritt | e7b86d1 | 2015-01-16 20:37:11 -0700 | [diff] [blame] | 236 | |
| 237 | for i, opt := range opts { |
| 238 | if opt.Op == "" { |
| 239 | return nil, no("Op") |
| 240 | } |
| 241 | if opt.Path == "" { |
| 242 | return nil, no("Path") |
| 243 | } |
| 244 | if opt.Op != Remove && opt.Value == nil { |
| 245 | return nil, no("Value") |
| 246 | } |
Jon Perritt | 608d3a5 | 2015-01-19 10:38:30 -0700 | [diff] [blame] | 247 | s[i] = interface{}(opt).(map[string]interface{}) |
Jon Perritt | e7b86d1 | 2015-01-16 20:37:11 -0700 | [diff] [blame] | 248 | } |
| 249 | |
| 250 | return s, nil |
| 251 | } |
| 252 | |
| 253 | // Update accepts a UpdateOpts struct and updates an existing CDN service using |
| 254 | // the values provided. |
| 255 | func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult { |
| 256 | var res UpdateResult |
| 257 | |
| 258 | reqBody, err := opts.ToCDNServiceUpdateMap() |
| 259 | if err != nil { |
| 260 | res.Err = err |
| 261 | return res |
| 262 | } |
| 263 | |
| 264 | _, res.Err = perigee.Request("PATCH", updateURL(c, id), perigee.Options{ |
| 265 | MoreHeaders: c.AuthenticatedHeaders(), |
| 266 | ReqBody: &reqBody, |
| 267 | OkCodes: []int{202}, |
| 268 | }) |
| 269 | return res |
| 270 | } |
| 271 | |
| 272 | // Delete accepts a unique ID and deletes the CDN service associated with it. |
| 273 | func Delete(c *gophercloud.ServiceClient, id string) DeleteResult { |
| 274 | var res DeleteResult |
| 275 | _, res.Err = perigee.Request("DELETE", deleteURL(c, id), perigee.Options{ |
| 276 | MoreHeaders: c.AuthenticatedHeaders(), |
| 277 | OkCodes: []int{202}, |
| 278 | }) |
| 279 | return res |
| 280 | } |