# HG changeset patch # User Mikael Berthe # Date 1492858631 -7200 # Node ID 9ce9b39c851c57285aef32babb5b96df1a92201c # Parent 048f390e94c59bba97807eb9c674ef6bc77e157d Add UpdateAccount() This patch adds support for the accounts/update_credentials API to update connected user information (display name, user note, avatar and header page image). Images can be provided in the API format (already base64-encoded) or with a file path. BTW the API implementation should be 100% complete now. diff -r 048f390e94c5 -r 9ce9b39c851c account.go --- a/account.go Wed Apr 19 19:53:26 2017 +0200 +++ b/account.go Sat Apr 22 12:57:11 2017 +0200 @@ -7,8 +7,15 @@ package madon import ( + "bytes" + "encoding/base64" + "encoding/json" "fmt" + "mime/multipart" + "net/http" + "os" "strconv" + "strings" "github.com/sendgrid/rest" ) @@ -304,3 +311,120 @@ _, err := mc.getSingleAccount(endPoint, accountID) return err } + +// UpdateAccount updates the connected user's account data +// The fields avatar & headerImage can contain base64-encoded images; if +// they do not (that is; if they don't contain ";base64,"), they are considered +// as file paths and their content will be encoded. +// All fields can be nil, in which case they are not updated. +// displayName and note can be set to "" to delete previous values; +// I'm not sure images can be deleted -- only replaced AFAICS. +func (mc *Client) UpdateAccount(displayName, note, avatar, headerImage *string) (*Account, error) { + const endPoint = "accounts/update_credentials" + params := make(apiCallParams) + + if displayName != nil { + params["display_name"] = *displayName + } + if note != nil { + params["note"] = *note + } + + var err error + avatar, err = fileToBase64(avatar, nil) + if err != nil { + return nil, err + } + headerImage, err = fileToBase64(headerImage, nil) + if err != nil { + return nil, err + } + + var formBuf bytes.Buffer + w := multipart.NewWriter(&formBuf) + + if avatar != nil { + w.WriteField("avatar", *avatar) + } + if headerImage != nil { + w.WriteField("header", *headerImage) + } + w.Close() + + // Prepare the request + req, err := mc.prepareRequest(endPoint, rest.Patch, params) + if err != nil { + return nil, fmt.Errorf("prepareRequest failed: %s", err.Error()) + } + req.Headers["Content-Type"] = w.FormDataContentType() + req.Body = formBuf.Bytes() + + // Make API call + r, err := restAPI(req) + if err != nil { + return nil, fmt.Errorf("account update failed: %s", err.Error()) + } + + // Check for error reply + var errorResult Error + if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil { + // The empty object is not an error + if errorResult.Text != "" { + return nil, fmt.Errorf("%s", errorResult.Text) + } + } + + // Not an error reply; let's unmarshal the data + var account Account + if err := json.Unmarshal([]byte(r.Body), &account); err != nil { + return nil, fmt.Errorf("cannot decode API response: %s", err.Error()) + } + return &account, nil +} + +// fileToBase64 is a helper function to convert a file's contents to +// base64-encoded data. Is the data string already contains base64 data, it +// is not modified. +// If contentType is nil, it is detected. +func fileToBase64(data, contentType *string) (*string, error) { + if data == nil { + return nil, nil + } + + if *data == "" { + return data, nil + } + + if strings.Contains(*data, ";base64,") { + return data, nil + } + + // We need to convert the file and file name to base64 + + file, err := os.Open(*data) + if err != nil { + return nil, err + } + defer file.Close() + + fStat, err := file.Stat() + if err != nil { + return nil, err + } + + buffer := make([]byte, fStat.Size()) + _, err = file.Read(buffer) + if err != nil { + return nil, err + } + + var cType string + if contentType == nil || *contentType == "" { + cType = http.DetectContentType(buffer[:512]) + } else { + cType = *contentType + } + contentData := base64.StdEncoding.EncodeToString(buffer) + newData := "data:" + cType + ";base64," + contentData + return &newData, nil +}