/gal3rest/gal3rest.go
Go | 359 lines | 246 code | 40 blank | 73 comment | 33 complexity | bd0cdb9e1b9e9ae675f322625e3c9761 MD5 | raw file
1//Copyright (c) 2012 Tim Shannon 2// 3//Permission is hereby granted, free of charge, to any person obtaining a copy 4//of this software and associated documentation files (the "Software"), to deal 5//in the Software without restriction, including without limitation the rights 6//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7//copies of the Software, and to permit persons to whom the Software is 8//furnished to do so, subject to the following conditions: 9// 10//The above copyright notice and this permission notice shall be included in 11//all copies or substantial portions of the Software. 12// 13//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19//THE SOFTWARE. 20 21//gal3rest packages handles all access to the gallery3 rest api 22// and is meant to abstract accessing it 23package gal3rest 24 25import ( 26 "bytes" 27 "encoding/json" 28 "fmt" 29 "io/ioutil" 30 "log" 31 "mime" 32 "net/http" 33 "net/url" 34 "path" 35 "reflect" 36 "strconv" 37 "strings" 38 "crypto/tls" 39) 40 41//Client holds the url and API keys for the gallery being used 42// so you don't have to specify the url and api key for each request 43// all gallery access functions are attached to this type 44type Client struct { 45 Url string 46 APIKey string 47} 48 49//RestData contains the general format of data returned from REST 50// API calls to Gallery3 51type RestData struct { 52 Url string 53 Entity Entity 54 Members []string 55 Relationships map[string]interface{} // may Type this later 56} 57 58//RestCreate holds the structure for REST API requests to create a new 59// gallery 60type RestCreate struct { 61 Type string `json:"type"` 62 Name string `json:"name"` 63 Title string `json:"title"` 64} 65 66//Entity is a general json data structure that is attached to most items 67// in the gallery3 rest api 68type Entity struct { 69 Id int 70 Description string 71 Name string 72 Web_url string 73 Mime_type string 74 Title string 75 Type string 76 Album_cover string 77 Thumb_url string 78 Thumb_url_public string 79 Width int 80 Thumb_width int 81 Resize_width int 82 Resize_height int 83 View_count int 84 Sort_order int 85 Height int 86 Updated int 87 Captured int 88 View_1 string 89 Can_edit int 90 View_2 string 91 Thumb_size int 92 Level int 93 Created int 94 Sort_column string 95 Slug string 96 Rand_key int 97 Thumb_height int 98 Owner_id int 99} 100 101//String function for pretty printing the entity REST data 102func (entity *Entity) String() string { 103 var strValue string 104 ref := reflect.ValueOf(entity).Elem() 105 entityType := ref.Type() 106 for i := 0; i < ref.NumField(); i++ { 107 field := ref.Field(i) 108 strValue += fmt.Sprintf("%s: %s %v\n", 109 entityType.Field(i).Name, 110 field.Type(), field.Interface()) 111 } 112 return strValue 113} 114 115//Album is the somewhat abstracted type used to hold the relationship 116// between albums and photos, as well as the REST API data associated to them 117type Album struct { 118 Entity 119 Photos []*Photo 120 Albums []*Album 121} 122 123//entity types 124const ( 125 PHOTO = "photo" 126 ALBUM = "album" 127) 128 129//Photo is the abstraction of the REST entity 130type Photo struct { 131 Entity 132} 133 134//Response is the json response from creating an album or uploading a photo 135// it simply contains the REST URL to the item added 136type Response struct { 137 Url string 138} 139 140//Returns a new client for accessing the Galllery3 REST API 141func NewClient(url string, apiKey string) Client { 142 client := Client{Url: url, APIKey: apiKey} 143 client.checkClient() 144 145 return client 146} 147 148//GetRESTItem retrieves an arbitrary REST item from a Gallery3 149// Possible key value parameters: 150// scope: direct - only specific item 151// all - All descendants (recursive) doesn't seem to work 152// name: value - only return items containing value 153// random: true - returns a single random item 154// type: photo 155// movie 156// album 157func (gClient *Client) GetRESTItem(itemUrl string, 158 parameters map[string]string) (restData *RestData, status int, err error) { 159 restData = new(RestData) 160 tr := &http.Transport{ 161 TLSClientConfig: &tls.Config{InsecureSkipVerify : true}, 162 } 163 hClient := &http.Client{Transport: tr} 164// hClient := new(http.Client) 165 166 if parameters != nil { 167 urlValues := url.Values{} 168 for k, v := range parameters { 169 urlValues.Set(k, v) 170 } 171 itemUrl += "?" + urlValues.Encode() 172 } 173 174 req, _ := http.NewRequest("GET", itemUrl, nil) 175 req.Header.Set("X-Gallery-Request-Method", "GET") 176 req.Header.Set("X-Gallery-Request-Key", gClient.APIKey) 177 178 response, err := hClient.Do(req) 179 if err != nil { 180 return 181 // log.Panic("Error connecting to: "+itemUrl+" Error: ", err) 182 } 183 body, err := ioutil.ReadAll(response.Body) 184 response.Body.Close() 185 if err != nil { 186 return 187 //log.Panic("Error reading response: ", err) 188 } 189 190 json.Unmarshal(body, &restData) 191 status = response.StatusCode 192 return 193} 194 195//checkClient checks to make sure the URL and API is set and configured properly 196func (gClient *Client) checkClient() { 197 if gClient.Url == "" { 198 log.Panicln("No URL specified in the client." + 199 " Be sure to specify the REST url before making a request") 200 } else { 201 if gClient.Url[len(gClient.Url)-1:] != "/" { 202 gClient.Url += "/" 203 } 204 } 205 if gClient.APIKey == "" { 206 log.Panicln("No API key specified in the client. " + 207 "Be sure to specify the REST API key before making a request.") 208 } 209} 210 211//GetUrlFromId simply builds the REST url from the passed in ID 212func (gClient *Client) GetUrlFromId(id int) string { 213 gClient.checkClient() 214 215 return gClient.Url + "rest/item/" + strconv.Itoa(id) 216} 217 218func (gClient *Client) GetItemsUrl() string { 219 gClient.checkClient() 220 221 return gClient.Url + "rest/items?" 222} 223 224//CreateAlbum creates an album with the passed in name and title inside the album at the 225// passed in url 226func (gClient *Client) CreateAlbum(title string, name string, parentUrl string) (itemUrl string, status int, err error) { 227 gClient.checkClient() 228 tr := &http.Transport{ 229 TLSClientConfig: &tls.Config{InsecureSkipVerify : true}, 230 } 231 hClient := &http.Client{Transport: tr} 232// hClient := new(http.Client) 233 234 c := &RestCreate{Name: name, Title: title, Type: ALBUM} 235 b, err := json.Marshal(c) 236 if err != nil { 237 return 238 // log.Panicln("Error marshalling Rest create: ", jErr) 239 } 240 241 //base64.URLEncoding.EncodeToString 242 encodedValue := "entity=" + url.QueryEscape(string(b)) 243 244 buffer := bytes.NewBuffer([]byte(encodedValue)) 245 246 req, _ := http.NewRequest("POST", parentUrl, buffer) 247 req.Header.Set("X-Gallery-Request-Method", "POST") 248 req.Header.Set("X-Gallery-Request-Key", gClient.APIKey) 249 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 250 req.Header.Set("Content-Length", strconv.Itoa(len(encodedValue))) 251 252 response, err := hClient.Do(req) 253 if err != nil { 254 return 255 //log.Panic("Error connecting to: "+parentUrl+" Error: ", err) 256 } 257 258 rspValue, err := ioutil.ReadAll(response.Body) 259 if err != nil { 260 return 261 //log.Panic("Error reading response: ", err) 262 } 263 response.Body.Close() 264 itemUrl = getUrl(rspValue) 265 status = response.StatusCode 266 return 267} 268 269//Uploads an image to the passed in album ur 270// sections of this should probabaly be scrapped and replaced 271// with the go mime/multipart package 272// a lot of this is hardcoded which could cause issues in the future 273func (gClient *Client) UploadImage(title string, imagePath string, 274 parentUrl string) (url string, status int, err error) { 275 gClient.checkClient() 276 tr := &http.Transport{ 277 TLSClientConfig: &tls.Config{InsecureSkipVerify : true}, 278 } 279 hClient := &http.Client{Transport: tr} 280// hClient := new(http.Client) 281 282 _, name := path.Split(imagePath) 283 c := &RestCreate{Name: name, Title: title, Type: PHOTO} 284 entity, err := json.Marshal(c) 285 if err != nil { 286 return 287 //log.Panicln("Error marshalling Rest create: ", jErr) 288 } 289 290 file, err := ioutil.ReadFile(imagePath) 291 if err != nil { 292 return url, status, err 293 // log.Panic("Error reading the image file: ", fErr) 294 } 295 296 var dataParts = make([]string, 13) 297 boundry := "roPK9J3DoG4ZWP6etiDuJ97h-zeNAph" 298 299 //build multipart request 300 dataParts[0] = "--" + boundry 301 dataParts[1] = `Content-Disposition: form-data; name="entity"` 302 dataParts[2] = "Content-Type: text/plain; charset=UTF-8" 303 dataParts[3] = "Content-Transfer-Encoding: 8bit" 304 //space in 4 305 dataParts[5] = string(entity) 306 dataParts[6] = "--" + boundry 307 dataParts[7] = `Content-Disposition: form-data; name="file";` + 308 `filename="` + path.Base(imagePath) + `"` 309 dataParts[8] = "Content-Type: " + getContentType(imagePath) 310 dataParts[9] = "Content-Transfer-Encoding: binary" 311 //space in 10 312 dataParts[11] = string(file) 313 dataParts[12] = "--" + boundry 314 315 data := strings.Join(dataParts, "\n") 316 buffer := bytes.NewBuffer([]byte(data)) 317 req, err := http.NewRequest("POST", parentUrl, buffer) 318 if err != nil { 319 return 320 } 321 req.Header.Set("X-Gallery-Request-Method", "POST") 322 req.Header.Set("X-Gallery-Request-Key", gClient.APIKey) 323 req.Header.Set("Content-Type", "multipart/form-data; boundary="+boundry) 324 req.Header.Set("Content-Length", strconv.Itoa(len(data))) 325 326 //fmt.Println("request: ", req) 327 response, err := hClient.Do(req) 328 if err != nil { 329 return 330 // log.Panic("Error connecting to: "+parentUrl+" Error: ", err) 331 } 332 333 rspValue, err := ioutil.ReadAll(response.Body) 334 if err != nil { 335 return 336 } 337 response.Body.Close() 338 url = getUrl(rspValue) 339 status = response.StatusCode 340 err = nil 341 return 342} 343 344//Returns the proper mime type for the passed in file 345func getContentType(file string) string { 346 ext := path.Ext(file) 347 mType := mime.TypeByExtension(ext) 348 if mType == "" { 349 return "application/octet-stream" 350 } 351 return mType 352} 353 354//Pull URL out of rest response 355func getUrl(data []byte) string { 356 response := new(Response) 357 json.Unmarshal(data, &response) 358 return response.Url 359}