github.com/elliott5/community@v0.14.1-0.20160709191136-823126fb026a/documize/section/trello/trello.go (about) 1 // Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved. 2 // 3 // This software (Documize Community Edition) is licensed under 4 // GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html 5 // 6 // You can operate outside the AGPL restrictions by purchasing 7 // Documize Enterprise Edition and obtaining a commercial license 8 // by contacting <sales@documize.com>. 9 // 10 // https://documize.com 11 12 package trello 13 14 import ( 15 "bytes" 16 "encoding/json" 17 "fmt" 18 "html/template" 19 "io/ioutil" 20 "net/http" 21 22 "github.com/documize/community/documize/api/request" 23 "github.com/documize/community/documize/section/provider" 24 "github.com/documize/community/wordsmith/log" 25 ) 26 27 var meta provider.TypeMeta 28 29 func init() { 30 meta = provider.TypeMeta{} 31 meta.ID = "c455a552-202e-441c-ad79-397a8152920b" 32 meta.Title = "Trello" 33 meta.Description = "Embed cards from boards and lists" 34 meta.ContentType = "trello" 35 } 36 37 // Provider represents Trello 38 type Provider struct { 39 } 40 41 // Meta describes us 42 func (*Provider) Meta() provider.TypeMeta { 43 return meta 44 } 45 46 // Command stub. 47 func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) { 48 query := r.URL.Query() 49 method := query.Get("method") 50 51 defer r.Body.Close() 52 body, err := ioutil.ReadAll(r.Body) 53 54 if err != nil { 55 provider.WriteMessage(w, "trello", "Bad body") 56 return 57 } 58 59 var config = trelloConfig{} 60 err = json.Unmarshal(body, &config) 61 62 if err != nil { 63 provider.WriteError(w, "trello", err) 64 return 65 } 66 67 config.Clean() 68 config.AppKey = request.ConfigString(meta.ConfigHandle(), "appKey") 69 70 if len(config.AppKey) == 0 { 71 log.ErrorString("missing trello App Key") 72 provider.WriteMessage(w, "trello", "Missing appKey") 73 return 74 } 75 76 if len(config.Token) == 0 { 77 config.Token = ctx.GetSecrets("token") // get a token, if we have one 78 } 79 80 if method != "config" { 81 if len(config.Token) == 0 { 82 provider.WriteMessage(w, "trello", "Missing token") 83 return 84 } 85 } 86 87 switch method { 88 case "cards": 89 render, err := getCards(config) 90 91 if err != nil { 92 log.IfErr(err) 93 provider.WriteError(w, "trello", err) 94 log.IfErr(ctx.SaveSecrets("")) // failure means our secrets are invalid 95 return 96 } 97 98 provider.WriteJSON(w, render) 99 100 case "boards": 101 render, err := getBoards(config) 102 103 if err != nil { 104 log.IfErr(err) 105 provider.WriteError(w, "trello", err) 106 log.IfErr(ctx.SaveSecrets("")) // failure means our secrets are invalid 107 return 108 } 109 110 provider.WriteJSON(w, render) 111 112 case "lists": 113 render, err := getLists(config) 114 115 if err != nil { 116 log.IfErr(err) 117 provider.WriteError(w, "trello", err) 118 log.IfErr(ctx.SaveSecrets("")) // failure means our secrets are invalid 119 return 120 } 121 122 provider.WriteJSON(w, render) 123 124 case "config": 125 var ret struct { 126 AppKey string `json:"appKey"` 127 Token string `json:"token"` 128 } 129 ret.AppKey = config.AppKey 130 if config.Token != "" { 131 ret.Token = provider.SecretReplacement 132 } 133 provider.WriteJSON(w, ret) 134 return 135 136 default: 137 log.ErrorString("trello unknown method name: " + method) 138 provider.WriteMessage(w, "trello", "missing method name") 139 return 140 } 141 142 // the token has just worked, so save it as our secret 143 var s secrets 144 s.Token = config.Token 145 b, e := json.Marshal(s) 146 log.IfErr(e) 147 log.IfErr(ctx.SaveSecrets(string(b))) 148 } 149 150 // Render just sends back HMTL as-is. 151 func (*Provider) Render(ctx *provider.Context, config, data string) string { 152 raw := []trelloListCards{} 153 payload := trelloRender{} 154 var c = trelloConfig{} 155 156 json.Unmarshal([]byte(data), &raw) 157 json.Unmarshal([]byte(config), &c) 158 159 payload.Board = c.Board 160 payload.Data = raw 161 payload.ListCount = len(raw) 162 163 for _, list := range raw { 164 payload.CardCount += len(list.Cards) 165 } 166 167 t := template.New("trello") 168 t, _ = t.Parse(renderTemplate) 169 170 buffer := new(bytes.Buffer) 171 t.Execute(buffer, payload) 172 173 return buffer.String() 174 } 175 176 // Refresh just sends back data as-is. 177 func (*Provider) Refresh(ctx *provider.Context, config, data string) string { 178 var c = trelloConfig{} 179 json.Unmarshal([]byte(config), &c) 180 181 refreshed, err := getCards(c) 182 183 if err != nil { 184 return data 185 } 186 187 j, err := json.Marshal(refreshed) 188 189 if err != nil { 190 log.Error("unable to marshall trello cards", err) 191 return data 192 } 193 194 return string(j) 195 } 196 197 // Helpers 198 func getBoards(config trelloConfig) (boards []trelloBoard, err error) { 199 req, err := http.NewRequest("GET", fmt.Sprintf("https://api.trello.com/1/members/me/boards?fields=id,name,url,closed,prefs,idOrganization&key=%s&token=%s", config.AppKey, config.Token), nil) 200 client := &http.Client{} 201 res, err := client.Do(req) 202 203 if err != nil { 204 return nil, err 205 } 206 207 if res.StatusCode != http.StatusOK { 208 return nil, fmt.Errorf("error: HTTP status code %d", res.StatusCode) 209 } 210 211 b := []trelloBoard{} 212 213 defer res.Body.Close() 214 dec := json.NewDecoder(res.Body) 215 err = dec.Decode(&b) 216 217 // we only show open, team boards (not personal) 218 for _, b := range b { 219 if !b.Closed && len(b.OrganizationID) > 0 { 220 boards = append(boards, b) 221 } 222 } 223 224 if err != nil { 225 fmt.Println(err) 226 return nil, err 227 } 228 229 return boards, nil 230 } 231 232 func getLists(config trelloConfig) (lists []trelloList, err error) { 233 req, err := http.NewRequest("GET", fmt.Sprintf("https://api.trello.com/1/boards/%s/lists/open?key=%s&token=%s", config.Board.ID, config.AppKey, config.Token), nil) 234 client := &http.Client{} 235 res, err := client.Do(req) 236 237 if err != nil { 238 return nil, err 239 } 240 241 if res.StatusCode != http.StatusOK { 242 return nil, fmt.Errorf("error: HTTP status code %d", res.StatusCode) 243 } 244 245 defer res.Body.Close() 246 247 dec := json.NewDecoder(res.Body) 248 err = dec.Decode(&lists) 249 250 if err != nil { 251 fmt.Println(err) 252 return nil, err 253 } 254 255 return lists, nil 256 } 257 258 func getCards(config trelloConfig) (listCards []trelloListCards, err error) { 259 for _, list := range config.Lists { 260 261 // don't process lists that user excluded from rendering 262 if !list.Included { 263 continue 264 } 265 266 req, err := http.NewRequest("GET", fmt.Sprintf("https://api.trello.com/1/lists/%s/cards?key=%s&token=%s", list.ID, config.AppKey, config.Token), nil) 267 client := &http.Client{} 268 res, err := client.Do(req) 269 270 if err != nil { 271 return nil, err 272 } 273 274 if res.StatusCode != http.StatusOK { 275 return nil, fmt.Errorf("error: HTTP status code %d", res.StatusCode) 276 } 277 278 defer res.Body.Close() 279 var cards []trelloCard 280 281 dec := json.NewDecoder(res.Body) 282 err = dec.Decode(&cards) 283 284 if err != nil { 285 return nil, err 286 } 287 288 data := trelloListCards{} 289 data.Cards = cards 290 data.List = list 291 292 listCards = append(listCards, data) 293 } 294 295 return listCards, nil 296 }