github.com/levb/mattermost-server@v5.3.1+incompatible/model/client4.go (about)

     1  // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package model
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"mime/multipart"
    12  	"net/http"
    13  	"net/url"
    14  	"strconv"
    15  	"strings"
    16  	"time"
    17  )
    18  
    19  const (
    20  	HEADER_REQUEST_ID         = "X-Request-ID"
    21  	HEADER_VERSION_ID         = "X-Version-ID"
    22  	HEADER_CLUSTER_ID         = "X-Cluster-ID"
    23  	HEADER_ETAG_SERVER        = "ETag"
    24  	HEADER_ETAG_CLIENT        = "If-None-Match"
    25  	HEADER_FORWARDED          = "X-Forwarded-For"
    26  	HEADER_REAL_IP            = "X-Real-IP"
    27  	HEADER_FORWARDED_PROTO    = "X-Forwarded-Proto"
    28  	HEADER_TOKEN              = "token"
    29  	HEADER_BEARER             = "BEARER"
    30  	HEADER_AUTH               = "Authorization"
    31  	HEADER_REQUESTED_WITH     = "X-Requested-With"
    32  	HEADER_REQUESTED_WITH_XML = "XMLHttpRequest"
    33  	STATUS                    = "status"
    34  	STATUS_OK                 = "OK"
    35  	STATUS_FAIL               = "FAIL"
    36  	STATUS_REMOVE             = "REMOVE"
    37  
    38  	CLIENT_DIR = "client"
    39  
    40  	API_URL_SUFFIX_V1 = "/api/v1"
    41  	API_URL_SUFFIX_V4 = "/api/v4"
    42  	API_URL_SUFFIX    = API_URL_SUFFIX_V4
    43  )
    44  
    45  type Response struct {
    46  	StatusCode    int
    47  	Error         *AppError
    48  	RequestId     string
    49  	Etag          string
    50  	ServerVersion string
    51  	Header        http.Header
    52  }
    53  
    54  type Client4 struct {
    55  	Url        string       // The location of the server, for example  "http://localhost:8065"
    56  	ApiUrl     string       // The api location of the server, for example "http://localhost:8065/api/v4"
    57  	HttpClient *http.Client // The http client
    58  	AuthToken  string
    59  	AuthType   string
    60  	HttpHeader map[string]string // Headers to be copied over for each request
    61  }
    62  
    63  func closeBody(r *http.Response) {
    64  	if r.Body != nil {
    65  		ioutil.ReadAll(r.Body)
    66  		r.Body.Close()
    67  	}
    68  }
    69  
    70  // Must is a convenience function used for testing.
    71  func (c *Client4) Must(result interface{}, resp *Response) interface{} {
    72  	if resp.Error != nil {
    73  
    74  		time.Sleep(time.Second)
    75  		panic(resp.Error)
    76  	}
    77  
    78  	return result
    79  }
    80  
    81  func NewAPIv4Client(url string) *Client4 {
    82  	return &Client4{url, url + API_URL_SUFFIX, &http.Client{}, "", "", map[string]string{}}
    83  }
    84  
    85  func BuildErrorResponse(r *http.Response, err *AppError) *Response {
    86  	var statusCode int
    87  	var header http.Header
    88  	if r != nil {
    89  		statusCode = r.StatusCode
    90  		header = r.Header
    91  	} else {
    92  		statusCode = 0
    93  		header = make(http.Header)
    94  	}
    95  
    96  	return &Response{
    97  		StatusCode: statusCode,
    98  		Error:      err,
    99  		Header:     header,
   100  	}
   101  }
   102  
   103  func BuildResponse(r *http.Response) *Response {
   104  	return &Response{
   105  		StatusCode:    r.StatusCode,
   106  		RequestId:     r.Header.Get(HEADER_REQUEST_ID),
   107  		Etag:          r.Header.Get(HEADER_ETAG_SERVER),
   108  		ServerVersion: r.Header.Get(HEADER_VERSION_ID),
   109  		Header:        r.Header,
   110  	}
   111  }
   112  
   113  func (c *Client4) MockSession(sessionToken string) {
   114  	c.AuthToken = sessionToken
   115  	c.AuthType = HEADER_BEARER
   116  }
   117  
   118  func (c *Client4) SetOAuthToken(token string) {
   119  	c.AuthToken = token
   120  	c.AuthType = HEADER_TOKEN
   121  }
   122  
   123  func (c *Client4) ClearOAuthToken() {
   124  	c.AuthToken = ""
   125  	c.AuthType = HEADER_BEARER
   126  }
   127  
   128  func (c *Client4) GetUsersRoute() string {
   129  	return fmt.Sprintf("/users")
   130  }
   131  
   132  func (c *Client4) GetUserRoute(userId string) string {
   133  	return fmt.Sprintf(c.GetUsersRoute()+"/%v", userId)
   134  }
   135  
   136  func (c *Client4) GetUserAccessTokensRoute() string {
   137  	return fmt.Sprintf(c.GetUsersRoute() + "/tokens")
   138  }
   139  
   140  func (c *Client4) GetUserAccessTokenRoute(tokenId string) string {
   141  	return fmt.Sprintf(c.GetUsersRoute()+"/tokens/%v", tokenId)
   142  }
   143  
   144  func (c *Client4) GetUserByUsernameRoute(userName string) string {
   145  	return fmt.Sprintf(c.GetUsersRoute()+"/username/%v", userName)
   146  }
   147  
   148  func (c *Client4) GetUserByEmailRoute(email string) string {
   149  	return fmt.Sprintf(c.GetUsersRoute()+"/email/%v", email)
   150  }
   151  
   152  func (c *Client4) GetTeamsRoute() string {
   153  	return fmt.Sprintf("/teams")
   154  }
   155  
   156  func (c *Client4) GetTeamRoute(teamId string) string {
   157  	return fmt.Sprintf(c.GetTeamsRoute()+"/%v", teamId)
   158  }
   159  
   160  func (c *Client4) GetTeamAutoCompleteCommandsRoute(teamId string) string {
   161  	return fmt.Sprintf(c.GetTeamsRoute()+"/%v/commands/autocomplete", teamId)
   162  }
   163  
   164  func (c *Client4) GetTeamByNameRoute(teamName string) string {
   165  	return fmt.Sprintf(c.GetTeamsRoute()+"/name/%v", teamName)
   166  }
   167  
   168  func (c *Client4) GetTeamMemberRoute(teamId, userId string) string {
   169  	return fmt.Sprintf(c.GetTeamRoute(teamId)+"/members/%v", userId)
   170  }
   171  
   172  func (c *Client4) GetTeamMembersRoute(teamId string) string {
   173  	return fmt.Sprintf(c.GetTeamRoute(teamId) + "/members")
   174  }
   175  
   176  func (c *Client4) GetTeamStatsRoute(teamId string) string {
   177  	return fmt.Sprintf(c.GetTeamRoute(teamId) + "/stats")
   178  }
   179  
   180  func (c *Client4) GetTeamImportRoute(teamId string) string {
   181  	return fmt.Sprintf(c.GetTeamRoute(teamId) + "/import")
   182  }
   183  
   184  func (c *Client4) GetChannelsRoute() string {
   185  	return fmt.Sprintf("/channels")
   186  }
   187  
   188  func (c *Client4) GetChannelsForTeamRoute(teamId string) string {
   189  	return fmt.Sprintf(c.GetTeamRoute(teamId) + "/channels")
   190  }
   191  
   192  func (c *Client4) GetChannelRoute(channelId string) string {
   193  	return fmt.Sprintf(c.GetChannelsRoute()+"/%v", channelId)
   194  }
   195  
   196  func (c *Client4) GetChannelByNameRoute(channelName, teamId string) string {
   197  	return fmt.Sprintf(c.GetTeamRoute(teamId)+"/channels/name/%v", channelName)
   198  }
   199  
   200  func (c *Client4) GetChannelByNameForTeamNameRoute(channelName, teamName string) string {
   201  	return fmt.Sprintf(c.GetTeamByNameRoute(teamName)+"/channels/name/%v", channelName)
   202  }
   203  
   204  func (c *Client4) GetChannelMembersRoute(channelId string) string {
   205  	return fmt.Sprintf(c.GetChannelRoute(channelId) + "/members")
   206  }
   207  
   208  func (c *Client4) GetChannelMemberRoute(channelId, userId string) string {
   209  	return fmt.Sprintf(c.GetChannelMembersRoute(channelId)+"/%v", userId)
   210  }
   211  
   212  func (c *Client4) GetPostsRoute() string {
   213  	return fmt.Sprintf("/posts")
   214  }
   215  
   216  func (c *Client4) GetPostsEphemeralRoute() string {
   217  	return fmt.Sprintf("/posts/ephemeral")
   218  }
   219  
   220  func (c *Client4) GetConfigRoute() string {
   221  	return fmt.Sprintf("/config")
   222  }
   223  
   224  func (c *Client4) GetLicenseRoute() string {
   225  	return fmt.Sprintf("/license")
   226  }
   227  
   228  func (c *Client4) GetPostRoute(postId string) string {
   229  	return fmt.Sprintf(c.GetPostsRoute()+"/%v", postId)
   230  }
   231  
   232  func (c *Client4) GetFilesRoute() string {
   233  	return fmt.Sprintf("/files")
   234  }
   235  
   236  func (c *Client4) GetFileRoute(fileId string) string {
   237  	return fmt.Sprintf(c.GetFilesRoute()+"/%v", fileId)
   238  }
   239  
   240  func (c *Client4) GetPluginsRoute() string {
   241  	return fmt.Sprintf("/plugins")
   242  }
   243  
   244  func (c *Client4) GetPluginRoute(pluginId string) string {
   245  	return fmt.Sprintf(c.GetPluginsRoute()+"/%v", pluginId)
   246  }
   247  
   248  func (c *Client4) GetSystemRoute() string {
   249  	return fmt.Sprintf("/system")
   250  }
   251  
   252  func (c *Client4) GetTestEmailRoute() string {
   253  	return fmt.Sprintf("/email/test")
   254  }
   255  
   256  func (c *Client4) GetTestS3Route() string {
   257  	return fmt.Sprintf("/file/s3_test")
   258  }
   259  
   260  func (c *Client4) GetDatabaseRoute() string {
   261  	return fmt.Sprintf("/database")
   262  }
   263  
   264  func (c *Client4) GetCacheRoute() string {
   265  	return fmt.Sprintf("/caches")
   266  }
   267  
   268  func (c *Client4) GetClusterRoute() string {
   269  	return fmt.Sprintf("/cluster")
   270  }
   271  
   272  func (c *Client4) GetIncomingWebhooksRoute() string {
   273  	return fmt.Sprintf("/hooks/incoming")
   274  }
   275  
   276  func (c *Client4) GetIncomingWebhookRoute(hookID string) string {
   277  	return fmt.Sprintf(c.GetIncomingWebhooksRoute()+"/%v", hookID)
   278  }
   279  
   280  func (c *Client4) GetComplianceReportsRoute() string {
   281  	return fmt.Sprintf("/compliance/reports")
   282  }
   283  
   284  func (c *Client4) GetComplianceReportRoute(reportId string) string {
   285  	return fmt.Sprintf("/compliance/reports/%v", reportId)
   286  }
   287  
   288  func (c *Client4) GetOutgoingWebhooksRoute() string {
   289  	return fmt.Sprintf("/hooks/outgoing")
   290  }
   291  
   292  func (c *Client4) GetOutgoingWebhookRoute(hookID string) string {
   293  	return fmt.Sprintf(c.GetOutgoingWebhooksRoute()+"/%v", hookID)
   294  }
   295  
   296  func (c *Client4) GetPreferencesRoute(userId string) string {
   297  	return fmt.Sprintf(c.GetUserRoute(userId) + "/preferences")
   298  }
   299  
   300  func (c *Client4) GetUserStatusRoute(userId string) string {
   301  	return fmt.Sprintf(c.GetUserRoute(userId) + "/status")
   302  }
   303  
   304  func (c *Client4) GetUserStatusesRoute() string {
   305  	return fmt.Sprintf(c.GetUsersRoute() + "/status")
   306  }
   307  
   308  func (c *Client4) GetSamlRoute() string {
   309  	return fmt.Sprintf("/saml")
   310  }
   311  
   312  func (c *Client4) GetLdapRoute() string {
   313  	return fmt.Sprintf("/ldap")
   314  }
   315  
   316  func (c *Client4) GetBrandRoute() string {
   317  	return fmt.Sprintf("/brand")
   318  }
   319  
   320  func (c *Client4) GetDataRetentionRoute() string {
   321  	return fmt.Sprintf("/data_retention")
   322  }
   323  
   324  func (c *Client4) GetElasticsearchRoute() string {
   325  	return fmt.Sprintf("/elasticsearch")
   326  }
   327  
   328  func (c *Client4) GetCommandsRoute() string {
   329  	return fmt.Sprintf("/commands")
   330  }
   331  
   332  func (c *Client4) GetCommandRoute(commandId string) string {
   333  	return fmt.Sprintf(c.GetCommandsRoute()+"/%v", commandId)
   334  }
   335  
   336  func (c *Client4) GetEmojisRoute() string {
   337  	return fmt.Sprintf("/emoji")
   338  }
   339  
   340  func (c *Client4) GetEmojiRoute(emojiId string) string {
   341  	return fmt.Sprintf(c.GetEmojisRoute()+"/%v", emojiId)
   342  }
   343  
   344  func (c *Client4) GetEmojiByNameRoute(name string) string {
   345  	return fmt.Sprintf(c.GetEmojisRoute()+"/name/%v", name)
   346  }
   347  
   348  func (c *Client4) GetReactionsRoute() string {
   349  	return fmt.Sprintf("/reactions")
   350  }
   351  
   352  func (c *Client4) GetOAuthAppsRoute() string {
   353  	return fmt.Sprintf("/oauth/apps")
   354  }
   355  
   356  func (c *Client4) GetOAuthAppRoute(appId string) string {
   357  	return fmt.Sprintf("/oauth/apps/%v", appId)
   358  }
   359  
   360  func (c *Client4) GetOpenGraphRoute() string {
   361  	return fmt.Sprintf("/opengraph")
   362  }
   363  
   364  func (c *Client4) GetJobsRoute() string {
   365  	return fmt.Sprintf("/jobs")
   366  }
   367  
   368  func (c *Client4) GetRolesRoute() string {
   369  	return fmt.Sprintf("/roles")
   370  }
   371  
   372  func (c *Client4) GetSchemesRoute() string {
   373  	return fmt.Sprintf("/schemes")
   374  }
   375  
   376  func (c *Client4) GetSchemeRoute(id string) string {
   377  	return c.GetSchemesRoute() + fmt.Sprintf("/%v", id)
   378  }
   379  
   380  func (c *Client4) GetAnalyticsRoute() string {
   381  	return fmt.Sprintf("/analytics")
   382  }
   383  
   384  func (c *Client4) GetTimezonesRoute() string {
   385  	return fmt.Sprintf(c.GetSystemRoute() + "/timezones")
   386  }
   387  
   388  func (c *Client4) GetChannelSchemeRoute(channelId string) string {
   389  	return fmt.Sprintf(c.GetChannelsRoute()+"/%v/scheme", channelId)
   390  }
   391  
   392  func (c *Client4) GetTeamSchemeRoute(teamId string) string {
   393  	return fmt.Sprintf(c.GetTeamsRoute()+"/%v/scheme", teamId)
   394  }
   395  
   396  func (c *Client4) GetTotalUsersStatsRoute() string {
   397  	return fmt.Sprintf(c.GetUsersRoute() + "/stats")
   398  }
   399  
   400  func (c *Client4) GetRedirectLocationRoute() string {
   401  	return fmt.Sprintf("/redirect_location")
   402  }
   403  
   404  func (c *Client4) DoApiGet(url string, etag string) (*http.Response, *AppError) {
   405  	return c.DoApiRequest(http.MethodGet, c.ApiUrl+url, "", etag)
   406  }
   407  
   408  func (c *Client4) DoApiPost(url string, data string) (*http.Response, *AppError) {
   409  	return c.DoApiRequest(http.MethodPost, c.ApiUrl+url, data, "")
   410  }
   411  
   412  func (c *Client4) DoApiPut(url string, data string) (*http.Response, *AppError) {
   413  	return c.DoApiRequest(http.MethodPut, c.ApiUrl+url, data, "")
   414  }
   415  
   416  func (c *Client4) DoApiDelete(url string) (*http.Response, *AppError) {
   417  	return c.DoApiRequest(http.MethodDelete, c.ApiUrl+url, "", "")
   418  }
   419  
   420  func (c *Client4) DoApiRequest(method, url, data, etag string) (*http.Response, *AppError) {
   421  	rq, _ := http.NewRequest(method, url, strings.NewReader(data))
   422  
   423  	if len(etag) > 0 {
   424  		rq.Header.Set(HEADER_ETAG_CLIENT, etag)
   425  	}
   426  
   427  	if len(c.AuthToken) > 0 {
   428  		rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
   429  	}
   430  
   431  	if c.HttpHeader != nil && len(c.HttpHeader) > 0 {
   432  
   433  		for k, v := range c.HttpHeader {
   434  			rq.Header.Set(k, v)
   435  		}
   436  	}
   437  
   438  	if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil {
   439  		return nil, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)
   440  	} else if rp.StatusCode == 304 {
   441  		return rp, nil
   442  	} else if rp.StatusCode >= 300 {
   443  		defer closeBody(rp)
   444  		return rp, AppErrorFromJson(rp.Body)
   445  	} else {
   446  		return rp, nil
   447  	}
   448  }
   449  
   450  func (c *Client4) DoUploadFile(url string, data []byte, contentType string) (*FileUploadResponse, *Response) {
   451  	rq, _ := http.NewRequest("POST", c.ApiUrl+url, bytes.NewReader(data))
   452  	rq.Header.Set("Content-Type", contentType)
   453  
   454  	if len(c.AuthToken) > 0 {
   455  		rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
   456  	}
   457  
   458  	if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil {
   459  		return nil, BuildErrorResponse(rp, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0))
   460  	} else {
   461  		defer closeBody(rp)
   462  
   463  		if rp.StatusCode >= 300 {
   464  			return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body))
   465  		} else {
   466  			return FileUploadResponseFromJson(rp.Body), BuildResponse(rp)
   467  		}
   468  	}
   469  }
   470  
   471  func (c *Client4) DoEmojiUploadFile(url string, data []byte, contentType string) (*Emoji, *Response) {
   472  	rq, _ := http.NewRequest("POST", c.ApiUrl+url, bytes.NewReader(data))
   473  	rq.Header.Set("Content-Type", contentType)
   474  
   475  	if len(c.AuthToken) > 0 {
   476  		rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
   477  	}
   478  
   479  	if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil {
   480  		return nil, BuildErrorResponse(rp, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0))
   481  	} else {
   482  		defer closeBody(rp)
   483  
   484  		if rp.StatusCode >= 300 {
   485  			return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body))
   486  		} else {
   487  			return EmojiFromJson(rp.Body), BuildResponse(rp)
   488  		}
   489  	}
   490  }
   491  
   492  func (c *Client4) DoUploadImportTeam(url string, data []byte, contentType string) (map[string]string, *Response) {
   493  	rq, _ := http.NewRequest("POST", c.ApiUrl+url, bytes.NewReader(data))
   494  	rq.Header.Set("Content-Type", contentType)
   495  
   496  	if len(c.AuthToken) > 0 {
   497  		rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
   498  	}
   499  
   500  	if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil {
   501  		return nil, BuildErrorResponse(rp, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0))
   502  	} else {
   503  		defer closeBody(rp)
   504  
   505  		if rp.StatusCode >= 300 {
   506  			return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body))
   507  		} else {
   508  			return MapFromJson(rp.Body), BuildResponse(rp)
   509  		}
   510  	}
   511  }
   512  
   513  // CheckStatusOK is a convenience function for checking the standard OK response
   514  // from the web service.
   515  func CheckStatusOK(r *http.Response) bool {
   516  	m := MapFromJson(r.Body)
   517  	defer closeBody(r)
   518  
   519  	if m != nil && m[STATUS] == STATUS_OK {
   520  		return true
   521  	}
   522  
   523  	return false
   524  }
   525  
   526  // Authentication Section
   527  
   528  // LoginById authenticates a user by user id and password.
   529  func (c *Client4) LoginById(id string, password string) (*User, *Response) {
   530  	m := make(map[string]string)
   531  	m["id"] = id
   532  	m["password"] = password
   533  	return c.login(m)
   534  }
   535  
   536  // Login authenticates a user by login id, which can be username, email or some sort
   537  // of SSO identifier based on server configuration, and a password.
   538  func (c *Client4) Login(loginId string, password string) (*User, *Response) {
   539  	m := make(map[string]string)
   540  	m["login_id"] = loginId
   541  	m["password"] = password
   542  	return c.login(m)
   543  }
   544  
   545  // LoginByLdap authenticates a user by LDAP id and password.
   546  func (c *Client4) LoginByLdap(loginId string, password string) (*User, *Response) {
   547  	m := make(map[string]string)
   548  	m["login_id"] = loginId
   549  	m["password"] = password
   550  	m["ldap_only"] = "true"
   551  	return c.login(m)
   552  }
   553  
   554  // LoginWithDevice authenticates a user by login id (username, email or some sort
   555  // of SSO identifier based on configuration), password and attaches a device id to
   556  // the session.
   557  func (c *Client4) LoginWithDevice(loginId string, password string, deviceId string) (*User, *Response) {
   558  	m := make(map[string]string)
   559  	m["login_id"] = loginId
   560  	m["password"] = password
   561  	m["device_id"] = deviceId
   562  	return c.login(m)
   563  }
   564  
   565  func (c *Client4) login(m map[string]string) (*User, *Response) {
   566  	if r, err := c.DoApiPost("/users/login", MapToJson(m)); err != nil {
   567  		return nil, BuildErrorResponse(r, err)
   568  	} else {
   569  		c.AuthToken = r.Header.Get(HEADER_TOKEN)
   570  		c.AuthType = HEADER_BEARER
   571  		defer closeBody(r)
   572  		return UserFromJson(r.Body), BuildResponse(r)
   573  	}
   574  }
   575  
   576  // Logout terminates the current user's session.
   577  func (c *Client4) Logout() (bool, *Response) {
   578  	if r, err := c.DoApiPost("/users/logout", ""); err != nil {
   579  		return false, BuildErrorResponse(r, err)
   580  	} else {
   581  		c.AuthToken = ""
   582  		c.AuthType = HEADER_BEARER
   583  
   584  		defer closeBody(r)
   585  		return CheckStatusOK(r), BuildResponse(r)
   586  	}
   587  }
   588  
   589  // SwitchAccountType changes a user's login type from one type to another.
   590  func (c *Client4) SwitchAccountType(switchRequest *SwitchRequest) (string, *Response) {
   591  	if r, err := c.DoApiPost(c.GetUsersRoute()+"/login/switch", switchRequest.ToJson()); err != nil {
   592  		return "", BuildErrorResponse(r, err)
   593  	} else {
   594  		defer closeBody(r)
   595  		return MapFromJson(r.Body)["follow_link"], BuildResponse(r)
   596  	}
   597  }
   598  
   599  // User Section
   600  
   601  // CreateUser creates a user in the system based on the provided user struct.
   602  func (c *Client4) CreateUser(user *User) (*User, *Response) {
   603  	if r, err := c.DoApiPost(c.GetUsersRoute(), user.ToJson()); err != nil {
   604  		return nil, BuildErrorResponse(r, err)
   605  	} else {
   606  		defer closeBody(r)
   607  		return UserFromJson(r.Body), BuildResponse(r)
   608  	}
   609  }
   610  
   611  // CreateUserWithToken creates a user in the system based on the provided tokenId.
   612  func (c *Client4) CreateUserWithToken(user *User, tokenId string) (*User, *Response) {
   613  	var query string
   614  	if tokenId != "" {
   615  		query = fmt.Sprintf("?t=%v", tokenId)
   616  	} else {
   617  		err := NewAppError("MissingHashOrData", "api.user.create_user.missing_token.app_error", nil, "", http.StatusBadRequest)
   618  		return nil, &Response{StatusCode: err.StatusCode, Error: err}
   619  	}
   620  	if r, err := c.DoApiPost(c.GetUsersRoute()+query, user.ToJson()); err != nil {
   621  		return nil, BuildErrorResponse(r, err)
   622  	} else {
   623  		defer closeBody(r)
   624  		return UserFromJson(r.Body), BuildResponse(r)
   625  	}
   626  }
   627  
   628  // CreateUserWithInviteId creates a user in the system based on the provided invited id.
   629  func (c *Client4) CreateUserWithInviteId(user *User, inviteId string) (*User, *Response) {
   630  	var query string
   631  	if inviteId != "" {
   632  		query = fmt.Sprintf("?iid=%v", url.QueryEscape(inviteId))
   633  	} else {
   634  		err := NewAppError("MissingInviteId", "api.user.create_user.missing_invite_id.app_error", nil, "", http.StatusBadRequest)
   635  		return nil, &Response{StatusCode: err.StatusCode, Error: err}
   636  	}
   637  	if r, err := c.DoApiPost(c.GetUsersRoute()+query, user.ToJson()); err != nil {
   638  		return nil, BuildErrorResponse(r, err)
   639  	} else {
   640  		defer closeBody(r)
   641  		return UserFromJson(r.Body), BuildResponse(r)
   642  	}
   643  }
   644  
   645  // GetMe returns the logged in user.
   646  func (c *Client4) GetMe(etag string) (*User, *Response) {
   647  	if r, err := c.DoApiGet(c.GetUserRoute(ME), etag); err != nil {
   648  		return nil, BuildErrorResponse(r, err)
   649  	} else {
   650  		defer closeBody(r)
   651  		return UserFromJson(r.Body), BuildResponse(r)
   652  	}
   653  }
   654  
   655  // GetUser returns a user based on the provided user id string.
   656  func (c *Client4) GetUser(userId, etag string) (*User, *Response) {
   657  	if r, err := c.DoApiGet(c.GetUserRoute(userId), etag); err != nil {
   658  		return nil, BuildErrorResponse(r, err)
   659  	} else {
   660  		defer closeBody(r)
   661  		return UserFromJson(r.Body), BuildResponse(r)
   662  	}
   663  }
   664  
   665  // GetUserByUsername returns a user based on the provided user name string.
   666  func (c *Client4) GetUserByUsername(userName, etag string) (*User, *Response) {
   667  	if r, err := c.DoApiGet(c.GetUserByUsernameRoute(userName), etag); err != nil {
   668  		return nil, BuildErrorResponse(r, err)
   669  	} else {
   670  		defer closeBody(r)
   671  		return UserFromJson(r.Body), BuildResponse(r)
   672  	}
   673  }
   674  
   675  // GetUserByEmail returns a user based on the provided user email string.
   676  func (c *Client4) GetUserByEmail(email, etag string) (*User, *Response) {
   677  	if r, err := c.DoApiGet(c.GetUserByEmailRoute(email), etag); err != nil {
   678  		return nil, BuildErrorResponse(r, err)
   679  	} else {
   680  		defer closeBody(r)
   681  		return UserFromJson(r.Body), BuildResponse(r)
   682  	}
   683  }
   684  
   685  // AutocompleteUsersInTeam returns the users on a team based on search term.
   686  func (c *Client4) AutocompleteUsersInTeam(teamId string, username string, etag string) (*UserAutocomplete, *Response) {
   687  	query := fmt.Sprintf("?in_team=%v&name=%v", teamId, username)
   688  	if r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag); err != nil {
   689  		return nil, BuildErrorResponse(r, err)
   690  	} else {
   691  		defer closeBody(r)
   692  		return UserAutocompleteFromJson(r.Body), BuildResponse(r)
   693  	}
   694  }
   695  
   696  // AutocompleteUsersInChannel returns the users in a channel based on search term.
   697  func (c *Client4) AutocompleteUsersInChannel(teamId string, channelId string, username string, etag string) (*UserAutocomplete, *Response) {
   698  	query := fmt.Sprintf("?in_team=%v&in_channel=%v&name=%v", teamId, channelId, username)
   699  	if r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag); err != nil {
   700  		return nil, BuildErrorResponse(r, err)
   701  	} else {
   702  		defer closeBody(r)
   703  		return UserAutocompleteFromJson(r.Body), BuildResponse(r)
   704  	}
   705  }
   706  
   707  // AutocompleteUsers returns the users in the system based on search term.
   708  func (c *Client4) AutocompleteUsers(username string, etag string) (*UserAutocomplete, *Response) {
   709  	query := fmt.Sprintf("?name=%v", username)
   710  	if r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag); err != nil {
   711  		return nil, BuildErrorResponse(r, err)
   712  	} else {
   713  		defer closeBody(r)
   714  		return UserAutocompleteFromJson(r.Body), BuildResponse(r)
   715  	}
   716  }
   717  
   718  // GetProfileImage gets user's profile image. Must be logged in or be a system administrator.
   719  func (c *Client4) GetProfileImage(userId, etag string) ([]byte, *Response) {
   720  	if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/image", etag); err != nil {
   721  		return nil, BuildErrorResponse(r, err)
   722  	} else {
   723  		defer closeBody(r)
   724  
   725  		if data, err := ioutil.ReadAll(r.Body); err != nil {
   726  			return nil, BuildErrorResponse(r, NewAppError("GetProfileImage", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode))
   727  		} else {
   728  			return data, BuildResponse(r)
   729  		}
   730  	}
   731  }
   732  
   733  // GetUsers returns a page of users on the system. Page counting starts at 0.
   734  func (c *Client4) GetUsers(page int, perPage int, etag string) ([]*User, *Response) {
   735  	query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
   736  	if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil {
   737  		return nil, BuildErrorResponse(r, err)
   738  	} else {
   739  		defer closeBody(r)
   740  		return UserListFromJson(r.Body), BuildResponse(r)
   741  	}
   742  }
   743  
   744  // GetUsersInTeam returns a page of users on a team. Page counting starts at 0.
   745  func (c *Client4) GetUsersInTeam(teamId string, page int, perPage int, etag string) ([]*User, *Response) {
   746  	query := fmt.Sprintf("?in_team=%v&page=%v&per_page=%v", teamId, page, perPage)
   747  	if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil {
   748  		return nil, BuildErrorResponse(r, err)
   749  	} else {
   750  		defer closeBody(r)
   751  		return UserListFromJson(r.Body), BuildResponse(r)
   752  	}
   753  }
   754  
   755  // GetNewUsersInTeam returns a page of users on a team. Page counting starts at 0.
   756  func (c *Client4) GetNewUsersInTeam(teamId string, page int, perPage int, etag string) ([]*User, *Response) {
   757  	query := fmt.Sprintf("?sort=create_at&in_team=%v&page=%v&per_page=%v", teamId, page, perPage)
   758  	if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil {
   759  		return nil, BuildErrorResponse(r, err)
   760  	} else {
   761  		defer closeBody(r)
   762  		return UserListFromJson(r.Body), BuildResponse(r)
   763  	}
   764  }
   765  
   766  // GetRecentlyActiveUsersInTeam returns a page of users on a team. Page counting starts at 0.
   767  func (c *Client4) GetRecentlyActiveUsersInTeam(teamId string, page int, perPage int, etag string) ([]*User, *Response) {
   768  	query := fmt.Sprintf("?sort=last_activity_at&in_team=%v&page=%v&per_page=%v", teamId, page, perPage)
   769  	if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil {
   770  		return nil, BuildErrorResponse(r, err)
   771  	} else {
   772  		defer closeBody(r)
   773  		return UserListFromJson(r.Body), BuildResponse(r)
   774  	}
   775  }
   776  
   777  // GetUsersNotInTeam returns a page of users who are not in a team. Page counting starts at 0.
   778  func (c *Client4) GetUsersNotInTeam(teamId string, page int, perPage int, etag string) ([]*User, *Response) {
   779  	query := fmt.Sprintf("?not_in_team=%v&page=%v&per_page=%v", teamId, page, perPage)
   780  	if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil {
   781  		return nil, BuildErrorResponse(r, err)
   782  	} else {
   783  		defer closeBody(r)
   784  		return UserListFromJson(r.Body), BuildResponse(r)
   785  	}
   786  }
   787  
   788  // GetUsersInChannel returns a page of users in a channel. Page counting starts at 0.
   789  func (c *Client4) GetUsersInChannel(channelId string, page int, perPage int, etag string) ([]*User, *Response) {
   790  	query := fmt.Sprintf("?in_channel=%v&page=%v&per_page=%v", channelId, page, perPage)
   791  	if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil {
   792  		return nil, BuildErrorResponse(r, err)
   793  	} else {
   794  		defer closeBody(r)
   795  		return UserListFromJson(r.Body), BuildResponse(r)
   796  	}
   797  }
   798  
   799  // GetUsersInChannelStatus returns a page of users in a channel. Page counting starts at 0. Sorted by Status
   800  func (c *Client4) GetUsersInChannelByStatus(channelId string, page int, perPage int, etag string) ([]*User, *Response) {
   801  	query := fmt.Sprintf("?in_channel=%v&page=%v&per_page=%v&sort=status", channelId, page, perPage)
   802  	if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil {
   803  		return nil, BuildErrorResponse(r, err)
   804  	} else {
   805  		defer closeBody(r)
   806  		return UserListFromJson(r.Body), BuildResponse(r)
   807  	}
   808  }
   809  
   810  // GetUsersNotInChannel returns a page of users not in a channel. Page counting starts at 0.
   811  func (c *Client4) GetUsersNotInChannel(teamId, channelId string, page int, perPage int, etag string) ([]*User, *Response) {
   812  	query := fmt.Sprintf("?in_team=%v&not_in_channel=%v&page=%v&per_page=%v", teamId, channelId, page, perPage)
   813  	if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil {
   814  		return nil, BuildErrorResponse(r, err)
   815  	} else {
   816  		defer closeBody(r)
   817  		return UserListFromJson(r.Body), BuildResponse(r)
   818  	}
   819  }
   820  
   821  // GetUsersWithoutTeam returns a page of users on the system that aren't on any teams. Page counting starts at 0.
   822  func (c *Client4) GetUsersWithoutTeam(page int, perPage int, etag string) ([]*User, *Response) {
   823  	query := fmt.Sprintf("?without_team=1&page=%v&per_page=%v", page, perPage)
   824  	if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil {
   825  		return nil, BuildErrorResponse(r, err)
   826  	} else {
   827  		defer closeBody(r)
   828  		return UserListFromJson(r.Body), BuildResponse(r)
   829  	}
   830  }
   831  
   832  // GetUsersByIds returns a list of users based on the provided user ids.
   833  func (c *Client4) GetUsersByIds(userIds []string) ([]*User, *Response) {
   834  	if r, err := c.DoApiPost(c.GetUsersRoute()+"/ids", ArrayToJson(userIds)); err != nil {
   835  		return nil, BuildErrorResponse(r, err)
   836  	} else {
   837  		defer closeBody(r)
   838  		return UserListFromJson(r.Body), BuildResponse(r)
   839  	}
   840  }
   841  
   842  // GetUsersByUsernames returns a list of users based on the provided usernames.
   843  func (c *Client4) GetUsersByUsernames(usernames []string) ([]*User, *Response) {
   844  	if r, err := c.DoApiPost(c.GetUsersRoute()+"/usernames", ArrayToJson(usernames)); err != nil {
   845  		return nil, BuildErrorResponse(r, err)
   846  	} else {
   847  		defer closeBody(r)
   848  		return UserListFromJson(r.Body), BuildResponse(r)
   849  	}
   850  }
   851  
   852  // SearchUsers returns a list of users based on some search criteria.
   853  func (c *Client4) SearchUsers(search *UserSearch) ([]*User, *Response) {
   854  	if r, err := c.DoApiPost(c.GetUsersRoute()+"/search", search.ToJson()); err != nil {
   855  		return nil, BuildErrorResponse(r, err)
   856  	} else {
   857  		defer closeBody(r)
   858  		return UserListFromJson(r.Body), BuildResponse(r)
   859  	}
   860  }
   861  
   862  // UpdateUser updates a user in the system based on the provided user struct.
   863  func (c *Client4) UpdateUser(user *User) (*User, *Response) {
   864  	if r, err := c.DoApiPut(c.GetUserRoute(user.Id), user.ToJson()); err != nil {
   865  		return nil, BuildErrorResponse(r, err)
   866  	} else {
   867  		defer closeBody(r)
   868  		return UserFromJson(r.Body), BuildResponse(r)
   869  	}
   870  }
   871  
   872  // PatchUser partially updates a user in the system. Any missing fields are not updated.
   873  func (c *Client4) PatchUser(userId string, patch *UserPatch) (*User, *Response) {
   874  	if r, err := c.DoApiPut(c.GetUserRoute(userId)+"/patch", patch.ToJson()); err != nil {
   875  		return nil, BuildErrorResponse(r, err)
   876  	} else {
   877  		defer closeBody(r)
   878  		return UserFromJson(r.Body), BuildResponse(r)
   879  	}
   880  }
   881  
   882  // UpdateUserAuth updates a user AuthData (uthData, authService and password) in the system.
   883  func (c *Client4) UpdateUserAuth(userId string, userAuth *UserAuth) (*UserAuth, *Response) {
   884  	if r, err := c.DoApiPut(c.GetUserRoute(userId)+"/auth", userAuth.ToJson()); err != nil {
   885  		return nil, BuildErrorResponse(r, err)
   886  	} else {
   887  		defer closeBody(r)
   888  		return UserAuthFromJson(r.Body), BuildResponse(r)
   889  	}
   890  }
   891  
   892  // UpdateUserMfa activates multi-factor authentication for a user if activate
   893  // is true and a valid code is provided. If activate is false, then code is not
   894  // required and multi-factor authentication is disabled for the user.
   895  func (c *Client4) UpdateUserMfa(userId, code string, activate bool) (bool, *Response) {
   896  	requestBody := make(map[string]interface{})
   897  	requestBody["activate"] = activate
   898  	requestBody["code"] = code
   899  
   900  	if r, err := c.DoApiPut(c.GetUserRoute(userId)+"/mfa", StringInterfaceToJson(requestBody)); err != nil {
   901  		return false, BuildErrorResponse(r, err)
   902  	} else {
   903  		defer closeBody(r)
   904  		return CheckStatusOK(r), BuildResponse(r)
   905  	}
   906  }
   907  
   908  // CheckUserMfa checks whether a user has MFA active on their account or not based on the
   909  // provided login id.
   910  func (c *Client4) CheckUserMfa(loginId string) (bool, *Response) {
   911  	requestBody := make(map[string]interface{})
   912  	requestBody["login_id"] = loginId
   913  
   914  	if r, err := c.DoApiPost(c.GetUsersRoute()+"/mfa", StringInterfaceToJson(requestBody)); err != nil {
   915  		return false, BuildErrorResponse(r, err)
   916  	} else {
   917  		defer closeBody(r)
   918  		data := StringInterfaceFromJson(r.Body)
   919  		if mfaRequired, ok := data["mfa_required"].(bool); !ok {
   920  			return false, BuildResponse(r)
   921  		} else {
   922  			return mfaRequired, BuildResponse(r)
   923  		}
   924  	}
   925  }
   926  
   927  // GenerateMfaSecret will generate a new MFA secret for a user and return it as a string and
   928  // as a base64 encoded image QR code.
   929  func (c *Client4) GenerateMfaSecret(userId string) (*MfaSecret, *Response) {
   930  	if r, err := c.DoApiPost(c.GetUserRoute(userId)+"/mfa/generate", ""); err != nil {
   931  		return nil, BuildErrorResponse(r, err)
   932  	} else {
   933  		defer closeBody(r)
   934  		return MfaSecretFromJson(r.Body), BuildResponse(r)
   935  	}
   936  }
   937  
   938  // UpdateUserPassword updates a user's password. Must be logged in as the user or be a system administrator.
   939  func (c *Client4) UpdateUserPassword(userId, currentPassword, newPassword string) (bool, *Response) {
   940  	requestBody := map[string]string{"current_password": currentPassword, "new_password": newPassword}
   941  	if r, err := c.DoApiPut(c.GetUserRoute(userId)+"/password", MapToJson(requestBody)); err != nil {
   942  		return false, BuildErrorResponse(r, err)
   943  	} else {
   944  		defer closeBody(r)
   945  		return CheckStatusOK(r), BuildResponse(r)
   946  	}
   947  }
   948  
   949  // UpdateUserRoles updates a user's roles in the system. A user can have "system_user" and "system_admin" roles.
   950  func (c *Client4) UpdateUserRoles(userId, roles string) (bool, *Response) {
   951  	requestBody := map[string]string{"roles": roles}
   952  	if r, err := c.DoApiPut(c.GetUserRoute(userId)+"/roles", MapToJson(requestBody)); err != nil {
   953  		return false, BuildErrorResponse(r, err)
   954  	} else {
   955  		defer closeBody(r)
   956  		return CheckStatusOK(r), BuildResponse(r)
   957  	}
   958  }
   959  
   960  // UpdateUserActive updates status of a user whether active or not.
   961  func (c *Client4) UpdateUserActive(userId string, active bool) (bool, *Response) {
   962  	requestBody := make(map[string]interface{})
   963  	requestBody["active"] = active
   964  
   965  	if r, err := c.DoApiPut(c.GetUserRoute(userId)+"/active", StringInterfaceToJson(requestBody)); err != nil {
   966  		return false, BuildErrorResponse(r, err)
   967  	} else {
   968  		defer closeBody(r)
   969  		return CheckStatusOK(r), BuildResponse(r)
   970  	}
   971  }
   972  
   973  // DeleteUser deactivates a user in the system based on the provided user id string.
   974  func (c *Client4) DeleteUser(userId string) (bool, *Response) {
   975  	if r, err := c.DoApiDelete(c.GetUserRoute(userId)); err != nil {
   976  		return false, BuildErrorResponse(r, err)
   977  	} else {
   978  		defer closeBody(r)
   979  		return CheckStatusOK(r), BuildResponse(r)
   980  	}
   981  }
   982  
   983  // SendPasswordResetEmail will send a link for password resetting to a user with the
   984  // provided email.
   985  func (c *Client4) SendPasswordResetEmail(email string) (bool, *Response) {
   986  	requestBody := map[string]string{"email": email}
   987  	if r, err := c.DoApiPost(c.GetUsersRoute()+"/password/reset/send", MapToJson(requestBody)); err != nil {
   988  		return false, BuildErrorResponse(r, err)
   989  	} else {
   990  		defer closeBody(r)
   991  		return CheckStatusOK(r), BuildResponse(r)
   992  	}
   993  }
   994  
   995  // ResetPassword uses a recovery code to update reset a user's password.
   996  func (c *Client4) ResetPassword(token, newPassword string) (bool, *Response) {
   997  	requestBody := map[string]string{"token": token, "new_password": newPassword}
   998  	if r, err := c.DoApiPost(c.GetUsersRoute()+"/password/reset", MapToJson(requestBody)); err != nil {
   999  		return false, BuildErrorResponse(r, err)
  1000  	} else {
  1001  		defer closeBody(r)
  1002  		return CheckStatusOK(r), BuildResponse(r)
  1003  	}
  1004  }
  1005  
  1006  // GetSessions returns a list of sessions based on the provided user id string.
  1007  func (c *Client4) GetSessions(userId, etag string) ([]*Session, *Response) {
  1008  	if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/sessions", etag); err != nil {
  1009  		return nil, BuildErrorResponse(r, err)
  1010  	} else {
  1011  		defer closeBody(r)
  1012  		return SessionsFromJson(r.Body), BuildResponse(r)
  1013  	}
  1014  }
  1015  
  1016  // RevokeSession revokes a user session based on the provided user id and session id strings.
  1017  func (c *Client4) RevokeSession(userId, sessionId string) (bool, *Response) {
  1018  	requestBody := map[string]string{"session_id": sessionId}
  1019  	if r, err := c.DoApiPost(c.GetUserRoute(userId)+"/sessions/revoke", MapToJson(requestBody)); err != nil {
  1020  		return false, BuildErrorResponse(r, err)
  1021  	} else {
  1022  		defer closeBody(r)
  1023  		return CheckStatusOK(r), BuildResponse(r)
  1024  	}
  1025  }
  1026  
  1027  // RevokeAllSessions revokes all sessions for the provided user id string.
  1028  func (c *Client4) RevokeAllSessions(userId string) (bool, *Response) {
  1029  	if r, err := c.DoApiPost(c.GetUserRoute(userId)+"/sessions/revoke/all", ""); err != nil {
  1030  		return false, BuildErrorResponse(r, err)
  1031  	} else {
  1032  		defer closeBody(r)
  1033  		return CheckStatusOK(r), BuildResponse(r)
  1034  	}
  1035  }
  1036  
  1037  // AttachDeviceId attaches a mobile device ID to the current session.
  1038  func (c *Client4) AttachDeviceId(deviceId string) (bool, *Response) {
  1039  	requestBody := map[string]string{"device_id": deviceId}
  1040  	if r, err := c.DoApiPut(c.GetUsersRoute()+"/sessions/device", MapToJson(requestBody)); err != nil {
  1041  		return false, BuildErrorResponse(r, err)
  1042  	} else {
  1043  		defer closeBody(r)
  1044  		return CheckStatusOK(r), BuildResponse(r)
  1045  	}
  1046  }
  1047  
  1048  // GetTeamsUnreadForUser will return an array with TeamUnread objects that contain the amount
  1049  // of unread messages and mentions the current user has for the teams it belongs to.
  1050  // An optional team ID can be set to exclude that team from the results. Must be authenticated.
  1051  func (c *Client4) GetTeamsUnreadForUser(userId, teamIdToExclude string) ([]*TeamUnread, *Response) {
  1052  	optional := ""
  1053  	if teamIdToExclude != "" {
  1054  		optional += fmt.Sprintf("?exclude_team=%s", url.QueryEscape(teamIdToExclude))
  1055  	}
  1056  
  1057  	if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/teams/unread"+optional, ""); err != nil {
  1058  		return nil, BuildErrorResponse(r, err)
  1059  	} else {
  1060  		defer closeBody(r)
  1061  		return TeamsUnreadFromJson(r.Body), BuildResponse(r)
  1062  	}
  1063  }
  1064  
  1065  // GetUserAudits returns a list of audit based on the provided user id string.
  1066  func (c *Client4) GetUserAudits(userId string, page int, perPage int, etag string) (Audits, *Response) {
  1067  	query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
  1068  	if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/audits"+query, etag); err != nil {
  1069  		return nil, BuildErrorResponse(r, err)
  1070  	} else {
  1071  		defer closeBody(r)
  1072  		return AuditsFromJson(r.Body), BuildResponse(r)
  1073  	}
  1074  }
  1075  
  1076  // VerifyUserEmail will verify a user's email using the supplied token.
  1077  func (c *Client4) VerifyUserEmail(token string) (bool, *Response) {
  1078  	requestBody := map[string]string{"token": token}
  1079  	if r, err := c.DoApiPost(c.GetUsersRoute()+"/email/verify", MapToJson(requestBody)); err != nil {
  1080  		return false, BuildErrorResponse(r, err)
  1081  	} else {
  1082  		defer closeBody(r)
  1083  		return CheckStatusOK(r), BuildResponse(r)
  1084  	}
  1085  }
  1086  
  1087  // SendVerificationEmail will send an email to the user with the provided email address, if
  1088  // that user exists. The email will contain a link that can be used to verify the user's
  1089  // email address.
  1090  func (c *Client4) SendVerificationEmail(email string) (bool, *Response) {
  1091  	requestBody := map[string]string{"email": email}
  1092  	if r, err := c.DoApiPost(c.GetUsersRoute()+"/email/verify/send", MapToJson(requestBody)); err != nil {
  1093  		return false, BuildErrorResponse(r, err)
  1094  	} else {
  1095  		defer closeBody(r)
  1096  		return CheckStatusOK(r), BuildResponse(r)
  1097  	}
  1098  }
  1099  
  1100  // SetProfileImage sets profile image of the user
  1101  func (c *Client4) SetProfileImage(userId string, data []byte) (bool, *Response) {
  1102  	body := &bytes.Buffer{}
  1103  	writer := multipart.NewWriter(body)
  1104  
  1105  	if part, err := writer.CreateFormFile("image", "profile.png"); err != nil {
  1106  		return false, &Response{Error: NewAppError("SetProfileImage", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)}
  1107  	} else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil {
  1108  		return false, &Response{Error: NewAppError("SetProfileImage", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)}
  1109  	}
  1110  
  1111  	if err := writer.Close(); err != nil {
  1112  		return false, &Response{Error: NewAppError("SetProfileImage", "model.client.set_profile_user.writer.app_error", nil, err.Error(), http.StatusBadRequest)}
  1113  	}
  1114  
  1115  	rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetUserRoute(userId)+"/image", bytes.NewReader(body.Bytes()))
  1116  	rq.Header.Set("Content-Type", writer.FormDataContentType())
  1117  
  1118  	if len(c.AuthToken) > 0 {
  1119  		rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
  1120  	}
  1121  
  1122  	if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil {
  1123  		// set to http.StatusForbidden(403)
  1124  		return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetUserRoute(userId)+"/image", "model.client.connecting.app_error", nil, err.Error(), 403)}
  1125  	} else {
  1126  		defer closeBody(rp)
  1127  
  1128  		if rp.StatusCode >= 300 {
  1129  			return false, BuildErrorResponse(rp, AppErrorFromJson(rp.Body))
  1130  		} else {
  1131  			return CheckStatusOK(rp), BuildResponse(rp)
  1132  		}
  1133  	}
  1134  }
  1135  
  1136  // CreateUserAccessToken will generate a user access token that can be used in place
  1137  // of a session token to access the REST API. Must have the 'create_user_access_token'
  1138  // permission and if generating for another user, must have the 'edit_other_users'
  1139  // permission. A non-blank description is required.
  1140  func (c *Client4) CreateUserAccessToken(userId, description string) (*UserAccessToken, *Response) {
  1141  	requestBody := map[string]string{"description": description}
  1142  	if r, err := c.DoApiPost(c.GetUserRoute(userId)+"/tokens", MapToJson(requestBody)); err != nil {
  1143  		return nil, BuildErrorResponse(r, err)
  1144  	} else {
  1145  		defer closeBody(r)
  1146  		return UserAccessTokenFromJson(r.Body), BuildResponse(r)
  1147  	}
  1148  }
  1149  
  1150  // GetUserAccessTokens will get a page of access tokens' id, description, is_active
  1151  // and the user_id in the system. The actual token will not be returned. Must have
  1152  // the 'manage_system' permission.
  1153  func (c *Client4) GetUserAccessTokens(page int, perPage int) ([]*UserAccessToken, *Response) {
  1154  	query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
  1155  	if r, err := c.DoApiGet(c.GetUserAccessTokensRoute()+query, ""); err != nil {
  1156  		return nil, BuildErrorResponse(r, err)
  1157  	} else {
  1158  		defer closeBody(r)
  1159  		return UserAccessTokenListFromJson(r.Body), BuildResponse(r)
  1160  	}
  1161  }
  1162  
  1163  // GetUserAccessToken will get a user access tokens' id, description, is_active
  1164  // and the user_id of the user it is for. The actual token will not be returned.
  1165  // Must have the 'read_user_access_token' permission and if getting for another
  1166  // user, must have the 'edit_other_users' permission.
  1167  func (c *Client4) GetUserAccessToken(tokenId string) (*UserAccessToken, *Response) {
  1168  	if r, err := c.DoApiGet(c.GetUserAccessTokenRoute(tokenId), ""); err != nil {
  1169  		return nil, BuildErrorResponse(r, err)
  1170  	} else {
  1171  		defer closeBody(r)
  1172  		return UserAccessTokenFromJson(r.Body), BuildResponse(r)
  1173  	}
  1174  }
  1175  
  1176  // GetUserAccessTokensForUser will get a paged list of user access tokens showing id,
  1177  // description and user_id for each. The actual tokens will not be returned. Must have
  1178  // the 'read_user_access_token' permission and if getting for another user, must have the
  1179  // 'edit_other_users' permission.
  1180  func (c *Client4) GetUserAccessTokensForUser(userId string, page, perPage int) ([]*UserAccessToken, *Response) {
  1181  	query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
  1182  	if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/tokens"+query, ""); err != nil {
  1183  		return nil, BuildErrorResponse(r, err)
  1184  	} else {
  1185  		defer closeBody(r)
  1186  		return UserAccessTokenListFromJson(r.Body), BuildResponse(r)
  1187  	}
  1188  }
  1189  
  1190  // RevokeUserAccessToken will revoke a user access token by id. Must have the
  1191  // 'revoke_user_access_token' permission and if revoking for another user, must have the
  1192  // 'edit_other_users' permission.
  1193  func (c *Client4) RevokeUserAccessToken(tokenId string) (bool, *Response) {
  1194  	requestBody := map[string]string{"token_id": tokenId}
  1195  	if r, err := c.DoApiPost(c.GetUsersRoute()+"/tokens/revoke", MapToJson(requestBody)); err != nil {
  1196  		return false, BuildErrorResponse(r, err)
  1197  	} else {
  1198  		defer closeBody(r)
  1199  		return CheckStatusOK(r), BuildResponse(r)
  1200  	}
  1201  }
  1202  
  1203  // SearchUserAccessTokens returns user access tokens matching the provided search term.
  1204  func (c *Client4) SearchUserAccessTokens(search *UserAccessTokenSearch) ([]*UserAccessToken, *Response) {
  1205  	if r, err := c.DoApiPost(c.GetUsersRoute()+"/tokens/search", search.ToJson()); err != nil {
  1206  		return nil, BuildErrorResponse(r, err)
  1207  	} else {
  1208  		defer closeBody(r)
  1209  		return UserAccessTokenListFromJson(r.Body), BuildResponse(r)
  1210  	}
  1211  }
  1212  
  1213  // DisableUserAccessToken will disable a user access token by id. Must have the
  1214  // 'revoke_user_access_token' permission and if disabling for another user, must have the
  1215  // 'edit_other_users' permission.
  1216  func (c *Client4) DisableUserAccessToken(tokenId string) (bool, *Response) {
  1217  	requestBody := map[string]string{"token_id": tokenId}
  1218  	if r, err := c.DoApiPost(c.GetUsersRoute()+"/tokens/disable", MapToJson(requestBody)); err != nil {
  1219  		return false, BuildErrorResponse(r, err)
  1220  	} else {
  1221  		defer closeBody(r)
  1222  		return CheckStatusOK(r), BuildResponse(r)
  1223  	}
  1224  }
  1225  
  1226  // EnableUserAccessToken will enable a user access token by id. Must have the
  1227  // 'create_user_access_token' permission and if enabling for another user, must have the
  1228  // 'edit_other_users' permission.
  1229  func (c *Client4) EnableUserAccessToken(tokenId string) (bool, *Response) {
  1230  	requestBody := map[string]string{"token_id": tokenId}
  1231  	if r, err := c.DoApiPost(c.GetUsersRoute()+"/tokens/enable", MapToJson(requestBody)); err != nil {
  1232  		return false, BuildErrorResponse(r, err)
  1233  	} else {
  1234  		defer closeBody(r)
  1235  		return CheckStatusOK(r), BuildResponse(r)
  1236  	}
  1237  }
  1238  
  1239  // Team Section
  1240  
  1241  // CreateTeam creates a team in the system based on the provided team struct.
  1242  func (c *Client4) CreateTeam(team *Team) (*Team, *Response) {
  1243  	if r, err := c.DoApiPost(c.GetTeamsRoute(), team.ToJson()); err != nil {
  1244  		return nil, BuildErrorResponse(r, err)
  1245  	} else {
  1246  		defer closeBody(r)
  1247  		return TeamFromJson(r.Body), BuildResponse(r)
  1248  	}
  1249  }
  1250  
  1251  // GetTeam returns a team based on the provided team id string.
  1252  func (c *Client4) GetTeam(teamId, etag string) (*Team, *Response) {
  1253  	if r, err := c.DoApiGet(c.GetTeamRoute(teamId), etag); err != nil {
  1254  		return nil, BuildErrorResponse(r, err)
  1255  	} else {
  1256  		defer closeBody(r)
  1257  		return TeamFromJson(r.Body), BuildResponse(r)
  1258  	}
  1259  }
  1260  
  1261  // GetAllTeams returns all teams based on permissions.
  1262  func (c *Client4) GetAllTeams(etag string, page int, perPage int) ([]*Team, *Response) {
  1263  	query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
  1264  	if r, err := c.DoApiGet(c.GetTeamsRoute()+query, etag); err != nil {
  1265  		return nil, BuildErrorResponse(r, err)
  1266  	} else {
  1267  		defer closeBody(r)
  1268  		return TeamListFromJson(r.Body), BuildResponse(r)
  1269  	}
  1270  }
  1271  
  1272  // GetTeamByName returns a team based on the provided team name string.
  1273  func (c *Client4) GetTeamByName(name, etag string) (*Team, *Response) {
  1274  	if r, err := c.DoApiGet(c.GetTeamByNameRoute(name), etag); err != nil {
  1275  		return nil, BuildErrorResponse(r, err)
  1276  	} else {
  1277  		defer closeBody(r)
  1278  		return TeamFromJson(r.Body), BuildResponse(r)
  1279  	}
  1280  }
  1281  
  1282  // SearchTeams returns teams matching the provided search term.
  1283  func (c *Client4) SearchTeams(search *TeamSearch) ([]*Team, *Response) {
  1284  	if r, err := c.DoApiPost(c.GetTeamsRoute()+"/search", search.ToJson()); err != nil {
  1285  		return nil, BuildErrorResponse(r, err)
  1286  	} else {
  1287  		defer closeBody(r)
  1288  		return TeamListFromJson(r.Body), BuildResponse(r)
  1289  	}
  1290  }
  1291  
  1292  // TeamExists returns true or false if the team exist or not.
  1293  func (c *Client4) TeamExists(name, etag string) (bool, *Response) {
  1294  	if r, err := c.DoApiGet(c.GetTeamByNameRoute(name)+"/exists", etag); err != nil {
  1295  		return false, BuildErrorResponse(r, err)
  1296  	} else {
  1297  		defer closeBody(r)
  1298  		return MapBoolFromJson(r.Body)["exists"], BuildResponse(r)
  1299  	}
  1300  }
  1301  
  1302  // GetTeamsForUser returns a list of teams a user is on. Must be logged in as the user
  1303  // or be a system administrator.
  1304  func (c *Client4) GetTeamsForUser(userId, etag string) ([]*Team, *Response) {
  1305  	if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/teams", etag); err != nil {
  1306  		return nil, BuildErrorResponse(r, err)
  1307  	} else {
  1308  		defer closeBody(r)
  1309  		return TeamListFromJson(r.Body), BuildResponse(r)
  1310  	}
  1311  }
  1312  
  1313  // GetTeamMember returns a team member based on the provided team and user id strings.
  1314  func (c *Client4) GetTeamMember(teamId, userId, etag string) (*TeamMember, *Response) {
  1315  	if r, err := c.DoApiGet(c.GetTeamMemberRoute(teamId, userId), etag); err != nil {
  1316  		return nil, BuildErrorResponse(r, err)
  1317  	} else {
  1318  		defer closeBody(r)
  1319  		return TeamMemberFromJson(r.Body), BuildResponse(r)
  1320  	}
  1321  }
  1322  
  1323  // UpdateTeamMemberRoles will update the roles on a team for a user.
  1324  func (c *Client4) UpdateTeamMemberRoles(teamId, userId, newRoles string) (bool, *Response) {
  1325  	requestBody := map[string]string{"roles": newRoles}
  1326  	if r, err := c.DoApiPut(c.GetTeamMemberRoute(teamId, userId)+"/roles", MapToJson(requestBody)); err != nil {
  1327  		return false, BuildErrorResponse(r, err)
  1328  	} else {
  1329  		defer closeBody(r)
  1330  		return CheckStatusOK(r), BuildResponse(r)
  1331  	}
  1332  }
  1333  
  1334  // UpdateTeamMemberSchemeRoles will update the scheme-derived roles on a team for a user.
  1335  func (c *Client4) UpdateTeamMemberSchemeRoles(teamId string, userId string, schemeRoles *SchemeRoles) (bool, *Response) {
  1336  	if r, err := c.DoApiPut(c.GetTeamMemberRoute(teamId, userId)+"/schemeRoles", schemeRoles.ToJson()); err != nil {
  1337  		return false, BuildErrorResponse(r, err)
  1338  	} else {
  1339  		defer closeBody(r)
  1340  		return CheckStatusOK(r), BuildResponse(r)
  1341  	}
  1342  }
  1343  
  1344  // UpdateTeam will update a team.
  1345  func (c *Client4) UpdateTeam(team *Team) (*Team, *Response) {
  1346  	if r, err := c.DoApiPut(c.GetTeamRoute(team.Id), team.ToJson()); err != nil {
  1347  		return nil, BuildErrorResponse(r, err)
  1348  	} else {
  1349  		defer closeBody(r)
  1350  		return TeamFromJson(r.Body), BuildResponse(r)
  1351  	}
  1352  }
  1353  
  1354  // PatchTeam partially updates a team. Any missing fields are not updated.
  1355  func (c *Client4) PatchTeam(teamId string, patch *TeamPatch) (*Team, *Response) {
  1356  	if r, err := c.DoApiPut(c.GetTeamRoute(teamId)+"/patch", patch.ToJson()); err != nil {
  1357  		return nil, BuildErrorResponse(r, err)
  1358  	} else {
  1359  		defer closeBody(r)
  1360  		return TeamFromJson(r.Body), BuildResponse(r)
  1361  	}
  1362  }
  1363  
  1364  // SoftDeleteTeam deletes the team softly (archive only, not permanent delete).
  1365  func (c *Client4) SoftDeleteTeam(teamId string) (bool, *Response) {
  1366  	if r, err := c.DoApiDelete(c.GetTeamRoute(teamId)); err != nil {
  1367  		return false, BuildErrorResponse(r, err)
  1368  	} else {
  1369  		defer closeBody(r)
  1370  		return CheckStatusOK(r), BuildResponse(r)
  1371  	}
  1372  }
  1373  
  1374  // PermanentDeleteTeam deletes the team, should only be used when needed for
  1375  // compliance and the like
  1376  func (c *Client4) PermanentDeleteTeam(teamId string) (bool, *Response) {
  1377  	if r, err := c.DoApiDelete(c.GetTeamRoute(teamId) + "?permanent=true"); err != nil {
  1378  		return false, BuildErrorResponse(r, err)
  1379  	} else {
  1380  		defer closeBody(r)
  1381  		return CheckStatusOK(r), BuildResponse(r)
  1382  	}
  1383  }
  1384  
  1385  // GetTeamMembers returns team members based on the provided team id string.
  1386  func (c *Client4) GetTeamMembers(teamId string, page int, perPage int, etag string) ([]*TeamMember, *Response) {
  1387  	query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
  1388  	if r, err := c.DoApiGet(c.GetTeamMembersRoute(teamId)+query, etag); err != nil {
  1389  		return nil, BuildErrorResponse(r, err)
  1390  	} else {
  1391  		defer closeBody(r)
  1392  		return TeamMembersFromJson(r.Body), BuildResponse(r)
  1393  	}
  1394  }
  1395  
  1396  // GetTeamMembersForUser returns the team members for a user.
  1397  func (c *Client4) GetTeamMembersForUser(userId string, etag string) ([]*TeamMember, *Response) {
  1398  	if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/teams/members", etag); err != nil {
  1399  		return nil, BuildErrorResponse(r, err)
  1400  	} else {
  1401  		defer closeBody(r)
  1402  		return TeamMembersFromJson(r.Body), BuildResponse(r)
  1403  	}
  1404  }
  1405  
  1406  // GetTeamMembersByIds will return an array of team members based on the
  1407  // team id and a list of user ids provided. Must be authenticated.
  1408  func (c *Client4) GetTeamMembersByIds(teamId string, userIds []string) ([]*TeamMember, *Response) {
  1409  	if r, err := c.DoApiPost(fmt.Sprintf("/teams/%v/members/ids", teamId), ArrayToJson(userIds)); err != nil {
  1410  		return nil, BuildErrorResponse(r, err)
  1411  	} else {
  1412  		defer closeBody(r)
  1413  		return TeamMembersFromJson(r.Body), BuildResponse(r)
  1414  	}
  1415  }
  1416  
  1417  // AddTeamMember adds user to a team and return a team member.
  1418  func (c *Client4) AddTeamMember(teamId, userId string) (*TeamMember, *Response) {
  1419  	member := &TeamMember{TeamId: teamId, UserId: userId}
  1420  
  1421  	if r, err := c.DoApiPost(c.GetTeamMembersRoute(teamId), member.ToJson()); err != nil {
  1422  		return nil, BuildErrorResponse(r, err)
  1423  	} else {
  1424  		defer closeBody(r)
  1425  		return TeamMemberFromJson(r.Body), BuildResponse(r)
  1426  	}
  1427  }
  1428  
  1429  // AddTeamMemberFromInvite adds a user to a team and return a team member using an invite id
  1430  // or an invite token/data pair.
  1431  func (c *Client4) AddTeamMemberFromInvite(token, inviteId string) (*TeamMember, *Response) {
  1432  	var query string
  1433  
  1434  	if inviteId != "" {
  1435  		query += fmt.Sprintf("?invite_id=%v", inviteId)
  1436  	}
  1437  
  1438  	if token != "" {
  1439  		query += fmt.Sprintf("?token=%v", token)
  1440  	}
  1441  
  1442  	if r, err := c.DoApiPost(c.GetTeamsRoute()+"/members/invite"+query, ""); err != nil {
  1443  		return nil, BuildErrorResponse(r, err)
  1444  	} else {
  1445  		defer closeBody(r)
  1446  		return TeamMemberFromJson(r.Body), BuildResponse(r)
  1447  	}
  1448  }
  1449  
  1450  // AddTeamMembers adds a number of users to a team and returns the team members.
  1451  func (c *Client4) AddTeamMembers(teamId string, userIds []string) ([]*TeamMember, *Response) {
  1452  	var members []*TeamMember
  1453  	for _, userId := range userIds {
  1454  		member := &TeamMember{TeamId: teamId, UserId: userId}
  1455  		members = append(members, member)
  1456  	}
  1457  
  1458  	if r, err := c.DoApiPost(c.GetTeamMembersRoute(teamId)+"/batch", TeamMembersToJson(members)); err != nil {
  1459  		return nil, BuildErrorResponse(r, err)
  1460  	} else {
  1461  		defer closeBody(r)
  1462  		return TeamMembersFromJson(r.Body), BuildResponse(r)
  1463  	}
  1464  }
  1465  
  1466  // RemoveTeamMember will remove a user from a team.
  1467  func (c *Client4) RemoveTeamMember(teamId, userId string) (bool, *Response) {
  1468  	if r, err := c.DoApiDelete(c.GetTeamMemberRoute(teamId, userId)); err != nil {
  1469  		return false, BuildErrorResponse(r, err)
  1470  	} else {
  1471  		defer closeBody(r)
  1472  		return CheckStatusOK(r), BuildResponse(r)
  1473  	}
  1474  }
  1475  
  1476  // GetTeamStats returns a team stats based on the team id string.
  1477  // Must be authenticated.
  1478  func (c *Client4) GetTeamStats(teamId, etag string) (*TeamStats, *Response) {
  1479  	if r, err := c.DoApiGet(c.GetTeamStatsRoute(teamId), etag); err != nil {
  1480  		return nil, BuildErrorResponse(r, err)
  1481  	} else {
  1482  		defer closeBody(r)
  1483  		return TeamStatsFromJson(r.Body), BuildResponse(r)
  1484  	}
  1485  }
  1486  
  1487  // GetTotalUsersStats returns a total system user stats.
  1488  // Must be authenticated.
  1489  func (c *Client4) GetTotalUsersStats(etag string) (*UsersStats, *Response) {
  1490  	if r, err := c.DoApiGet(c.GetTotalUsersStatsRoute(), etag); err != nil {
  1491  		return nil, BuildErrorResponse(r, err)
  1492  	} else {
  1493  		defer closeBody(r)
  1494  		return UsersStatsFromJson(r.Body), BuildResponse(r)
  1495  	}
  1496  }
  1497  
  1498  // GetTeamUnread will return a TeamUnread object that contains the amount of
  1499  // unread messages and mentions the user has for the specified team.
  1500  // Must be authenticated.
  1501  func (c *Client4) GetTeamUnread(teamId, userId string) (*TeamUnread, *Response) {
  1502  	if r, err := c.DoApiGet(c.GetUserRoute(userId)+c.GetTeamRoute(teamId)+"/unread", ""); err != nil {
  1503  		return nil, BuildErrorResponse(r, err)
  1504  	} else {
  1505  		defer closeBody(r)
  1506  		return TeamUnreadFromJson(r.Body), BuildResponse(r)
  1507  	}
  1508  }
  1509  
  1510  // ImportTeam will import an exported team from other app into a existing team.
  1511  func (c *Client4) ImportTeam(data []byte, filesize int, importFrom, filename, teamId string) (map[string]string, *Response) {
  1512  	body := &bytes.Buffer{}
  1513  	writer := multipart.NewWriter(body)
  1514  
  1515  	if part, err := writer.CreateFormFile("file", filename); err != nil {
  1516  		return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.file.app_error", nil, err.Error(), http.StatusBadRequest)}
  1517  	} else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil {
  1518  		return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.file.app_error", nil, err.Error(), http.StatusBadRequest)}
  1519  	}
  1520  
  1521  	if part, err := writer.CreateFormField("filesize"); err != nil {
  1522  		return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.file_size.app_error", nil, err.Error(), http.StatusBadRequest)}
  1523  	} else if _, err = io.Copy(part, strings.NewReader(strconv.Itoa(filesize))); err != nil {
  1524  		return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.file_size.app_error", nil, err.Error(), http.StatusBadRequest)}
  1525  	}
  1526  
  1527  	if part, err := writer.CreateFormField("importFrom"); err != nil {
  1528  		return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.import_from.app_error", nil, err.Error(), http.StatusBadRequest)}
  1529  	} else if _, err = io.Copy(part, strings.NewReader(importFrom)); err != nil {
  1530  		return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.import_from.app_error", nil, err.Error(), http.StatusBadRequest)}
  1531  	}
  1532  
  1533  	if err := writer.Close(); err != nil {
  1534  		return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.writer.app_error", nil, err.Error(), http.StatusBadRequest)}
  1535  	}
  1536  
  1537  	return c.DoUploadImportTeam(c.GetTeamImportRoute(teamId), body.Bytes(), writer.FormDataContentType())
  1538  }
  1539  
  1540  // InviteUsersToTeam invite users by email to the team.
  1541  func (c *Client4) InviteUsersToTeam(teamId string, userEmails []string) (bool, *Response) {
  1542  	if r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/invite/email", ArrayToJson(userEmails)); err != nil {
  1543  		return false, BuildErrorResponse(r, err)
  1544  	} else {
  1545  		defer closeBody(r)
  1546  		return CheckStatusOK(r), BuildResponse(r)
  1547  	}
  1548  }
  1549  
  1550  // GetTeamInviteInfo returns a team object from an invite id containing sanitized information.
  1551  func (c *Client4) GetTeamInviteInfo(inviteId string) (*Team, *Response) {
  1552  	if r, err := c.DoApiGet(c.GetTeamsRoute()+"/invite/"+inviteId, ""); err != nil {
  1553  		return nil, BuildErrorResponse(r, err)
  1554  	} else {
  1555  		defer closeBody(r)
  1556  		return TeamFromJson(r.Body), BuildResponse(r)
  1557  	}
  1558  }
  1559  
  1560  // SetTeamIcon sets team icon of the team
  1561  func (c *Client4) SetTeamIcon(teamId string, data []byte) (bool, *Response) {
  1562  
  1563  	body := &bytes.Buffer{}
  1564  	writer := multipart.NewWriter(body)
  1565  
  1566  	if part, err := writer.CreateFormFile("image", "teamIcon.png"); err != nil {
  1567  		return false, &Response{Error: NewAppError("SetTeamIcon", "model.client.set_team_icon.no_file.app_error", nil, err.Error(), http.StatusBadRequest)}
  1568  	} else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil {
  1569  		return false, &Response{Error: NewAppError("SetTeamIcon", "model.client.set_team_icon.no_file.app_error", nil, err.Error(), http.StatusBadRequest)}
  1570  	}
  1571  
  1572  	if err := writer.Close(); err != nil {
  1573  		return false, &Response{Error: NewAppError("SetTeamIcon", "model.client.set_team_icon.writer.app_error", nil, err.Error(), http.StatusBadRequest)}
  1574  	}
  1575  
  1576  	rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetTeamRoute(teamId)+"/image", bytes.NewReader(body.Bytes()))
  1577  	rq.Header.Set("Content-Type", writer.FormDataContentType())
  1578  
  1579  	if len(c.AuthToken) > 0 {
  1580  		rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
  1581  	}
  1582  
  1583  	if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil {
  1584  		// set to http.StatusForbidden(403)
  1585  		return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetTeamRoute(teamId)+"/image", "model.client.connecting.app_error", nil, err.Error(), 403)}
  1586  	} else {
  1587  		defer closeBody(rp)
  1588  
  1589  		if rp.StatusCode >= 300 {
  1590  			return false, BuildErrorResponse(rp, AppErrorFromJson(rp.Body))
  1591  		} else {
  1592  			return CheckStatusOK(rp), BuildResponse(rp)
  1593  		}
  1594  	}
  1595  }
  1596  
  1597  // GetTeamIcon gets the team icon of the team
  1598  func (c *Client4) GetTeamIcon(teamId, etag string) ([]byte, *Response) {
  1599  	if r, err := c.DoApiGet(c.GetTeamRoute(teamId)+"/image", etag); err != nil {
  1600  		return nil, BuildErrorResponse(r, err)
  1601  	} else {
  1602  		defer closeBody(r)
  1603  
  1604  		if data, err := ioutil.ReadAll(r.Body); err != nil {
  1605  			return nil, BuildErrorResponse(r, NewAppError("GetTeamIcon", "model.client.get_team_icon.app_error", nil, err.Error(), r.StatusCode))
  1606  		} else {
  1607  			return data, BuildResponse(r)
  1608  		}
  1609  	}
  1610  }
  1611  
  1612  // RemoveTeamIcon updates LastTeamIconUpdate to 0 which indicates team icon is removed.
  1613  func (c *Client4) RemoveTeamIcon(teamId string) (bool, *Response) {
  1614  	if r, err := c.DoApiDelete(c.GetTeamRoute(teamId) + "/image"); err != nil {
  1615  		return false, BuildErrorResponse(r, err)
  1616  	} else {
  1617  		defer closeBody(r)
  1618  		return CheckStatusOK(r), BuildResponse(r)
  1619  	}
  1620  }
  1621  
  1622  // Channel Section
  1623  
  1624  // CreateChannel creates a channel based on the provided channel struct.
  1625  func (c *Client4) CreateChannel(channel *Channel) (*Channel, *Response) {
  1626  	if r, err := c.DoApiPost(c.GetChannelsRoute(), channel.ToJson()); err != nil {
  1627  		return nil, BuildErrorResponse(r, err)
  1628  	} else {
  1629  		defer closeBody(r)
  1630  		return ChannelFromJson(r.Body), BuildResponse(r)
  1631  	}
  1632  }
  1633  
  1634  // UpdateChannel update a channel based on the provided channel struct.
  1635  func (c *Client4) UpdateChannel(channel *Channel) (*Channel, *Response) {
  1636  	if r, err := c.DoApiPut(c.GetChannelRoute(channel.Id), channel.ToJson()); err != nil {
  1637  		return nil, BuildErrorResponse(r, err)
  1638  	} else {
  1639  		defer closeBody(r)
  1640  		return ChannelFromJson(r.Body), BuildResponse(r)
  1641  	}
  1642  }
  1643  
  1644  // PatchChannel partially updates a channel. Any missing fields are not updated.
  1645  func (c *Client4) PatchChannel(channelId string, patch *ChannelPatch) (*Channel, *Response) {
  1646  	if r, err := c.DoApiPut(c.GetChannelRoute(channelId)+"/patch", patch.ToJson()); err != nil {
  1647  		return nil, BuildErrorResponse(r, err)
  1648  	} else {
  1649  		defer closeBody(r)
  1650  		return ChannelFromJson(r.Body), BuildResponse(r)
  1651  	}
  1652  }
  1653  
  1654  // ConvertChannelToPrivate converts public to private channel.
  1655  func (c *Client4) ConvertChannelToPrivate(channelId string) (*Channel, *Response) {
  1656  	if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/convert", ""); err != nil {
  1657  		return nil, BuildErrorResponse(r, err)
  1658  	} else {
  1659  		defer closeBody(r)
  1660  		return ChannelFromJson(r.Body), BuildResponse(r)
  1661  	}
  1662  }
  1663  
  1664  // RestoreChannel restores a previously deleted channel. Any missing fields are not updated.
  1665  func (c *Client4) RestoreChannel(channelId string) (*Channel, *Response) {
  1666  	if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/restore", ""); err != nil {
  1667  		return nil, BuildErrorResponse(r, err)
  1668  	} else {
  1669  		defer closeBody(r)
  1670  		return ChannelFromJson(r.Body), BuildResponse(r)
  1671  	}
  1672  }
  1673  
  1674  // CreateDirectChannel creates a direct message channel based on the two user
  1675  // ids provided.
  1676  func (c *Client4) CreateDirectChannel(userId1, userId2 string) (*Channel, *Response) {
  1677  	requestBody := []string{userId1, userId2}
  1678  	if r, err := c.DoApiPost(c.GetChannelsRoute()+"/direct", ArrayToJson(requestBody)); err != nil {
  1679  		return nil, BuildErrorResponse(r, err)
  1680  	} else {
  1681  		defer closeBody(r)
  1682  		return ChannelFromJson(r.Body), BuildResponse(r)
  1683  	}
  1684  }
  1685  
  1686  // CreateGroupChannel creates a group message channel based on userIds provided
  1687  func (c *Client4) CreateGroupChannel(userIds []string) (*Channel, *Response) {
  1688  	if r, err := c.DoApiPost(c.GetChannelsRoute()+"/group", ArrayToJson(userIds)); err != nil {
  1689  		return nil, BuildErrorResponse(r, err)
  1690  	} else {
  1691  		defer closeBody(r)
  1692  		return ChannelFromJson(r.Body), BuildResponse(r)
  1693  	}
  1694  }
  1695  
  1696  // GetChannel returns a channel based on the provided channel id string.
  1697  func (c *Client4) GetChannel(channelId, etag string) (*Channel, *Response) {
  1698  	if r, err := c.DoApiGet(c.GetChannelRoute(channelId), etag); err != nil {
  1699  		return nil, BuildErrorResponse(r, err)
  1700  	} else {
  1701  		defer closeBody(r)
  1702  		return ChannelFromJson(r.Body), BuildResponse(r)
  1703  	}
  1704  }
  1705  
  1706  // GetChannelStats returns statistics for a channel.
  1707  func (c *Client4) GetChannelStats(channelId string, etag string) (*ChannelStats, *Response) {
  1708  	if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/stats", etag); err != nil {
  1709  		return nil, BuildErrorResponse(r, err)
  1710  	} else {
  1711  		defer closeBody(r)
  1712  		return ChannelStatsFromJson(r.Body), BuildResponse(r)
  1713  	}
  1714  }
  1715  
  1716  // GetPinnedPosts gets a list of pinned posts.
  1717  func (c *Client4) GetPinnedPosts(channelId string, etag string) (*PostList, *Response) {
  1718  	if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/pinned", etag); err != nil {
  1719  		return nil, BuildErrorResponse(r, err)
  1720  	} else {
  1721  		defer closeBody(r)
  1722  		return PostListFromJson(r.Body), BuildResponse(r)
  1723  	}
  1724  }
  1725  
  1726  // GetPublicChannelsForTeam returns a list of public channels based on the provided team id string.
  1727  func (c *Client4) GetPublicChannelsForTeam(teamId string, page int, perPage int, etag string) ([]*Channel, *Response) {
  1728  	query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
  1729  	if r, err := c.DoApiGet(c.GetChannelsForTeamRoute(teamId)+query, etag); err != nil {
  1730  		return nil, BuildErrorResponse(r, err)
  1731  	} else {
  1732  		defer closeBody(r)
  1733  		return ChannelSliceFromJson(r.Body), BuildResponse(r)
  1734  	}
  1735  }
  1736  
  1737  // GetDeletedChannelsForTeam returns a list of public channels based on the provided team id string.
  1738  func (c *Client4) GetDeletedChannelsForTeam(teamId string, page int, perPage int, etag string) ([]*Channel, *Response) {
  1739  	query := fmt.Sprintf("/deleted?page=%v&per_page=%v", page, perPage)
  1740  	if r, err := c.DoApiGet(c.GetChannelsForTeamRoute(teamId)+query, etag); err != nil {
  1741  		return nil, BuildErrorResponse(r, err)
  1742  	} else {
  1743  		defer closeBody(r)
  1744  		return ChannelSliceFromJson(r.Body), BuildResponse(r)
  1745  	}
  1746  }
  1747  
  1748  // GetPublicChannelsByIdsForTeam returns a list of public channels based on provided team id string
  1749  func (c *Client4) GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) ([]*Channel, *Response) {
  1750  	if r, err := c.DoApiPost(c.GetChannelsForTeamRoute(teamId)+"/ids", ArrayToJson(channelIds)); err != nil {
  1751  		return nil, BuildErrorResponse(r, err)
  1752  	} else {
  1753  		defer closeBody(r)
  1754  		return ChannelSliceFromJson(r.Body), BuildResponse(r)
  1755  	}
  1756  }
  1757  
  1758  // GetChannelsForTeamForUser returns a list channels of on a team for a user.
  1759  func (c *Client4) GetChannelsForTeamForUser(teamId, userId, etag string) ([]*Channel, *Response) {
  1760  	if r, err := c.DoApiGet(c.GetUserRoute(userId)+c.GetTeamRoute(teamId)+"/channels", etag); err != nil {
  1761  		return nil, BuildErrorResponse(r, err)
  1762  	} else {
  1763  		defer closeBody(r)
  1764  		return ChannelSliceFromJson(r.Body), BuildResponse(r)
  1765  	}
  1766  }
  1767  
  1768  // SearchChannels returns the channels on a team matching the provided search term.
  1769  func (c *Client4) SearchChannels(teamId string, search *ChannelSearch) ([]*Channel, *Response) {
  1770  	if r, err := c.DoApiPost(c.GetChannelsForTeamRoute(teamId)+"/search", search.ToJson()); err != nil {
  1771  		return nil, BuildErrorResponse(r, err)
  1772  	} else {
  1773  		defer closeBody(r)
  1774  		return ChannelSliceFromJson(r.Body), BuildResponse(r)
  1775  	}
  1776  }
  1777  
  1778  // DeleteChannel deletes channel based on the provided channel id string.
  1779  func (c *Client4) DeleteChannel(channelId string) (bool, *Response) {
  1780  	if r, err := c.DoApiDelete(c.GetChannelRoute(channelId)); err != nil {
  1781  		return false, BuildErrorResponse(r, err)
  1782  	} else {
  1783  		defer closeBody(r)
  1784  		return CheckStatusOK(r), BuildResponse(r)
  1785  	}
  1786  }
  1787  
  1788  // GetChannelByName returns a channel based on the provided channel name and team id strings.
  1789  func (c *Client4) GetChannelByName(channelName, teamId string, etag string) (*Channel, *Response) {
  1790  	if r, err := c.DoApiGet(c.GetChannelByNameRoute(channelName, teamId), etag); err != nil {
  1791  		return nil, BuildErrorResponse(r, err)
  1792  	} else {
  1793  		defer closeBody(r)
  1794  		return ChannelFromJson(r.Body), BuildResponse(r)
  1795  	}
  1796  }
  1797  
  1798  func (c *Client4) GetChannelByNameIncludeDeleted(channelName, teamId string, etag string) (*Channel, *Response) {
  1799  	if r, err := c.DoApiGet(c.GetChannelByNameRoute(channelName, teamId)+"?include_deleted=true", etag); err != nil {
  1800  		return nil, BuildErrorResponse(r, err)
  1801  	} else {
  1802  		defer closeBody(r)
  1803  		return ChannelFromJson(r.Body), BuildResponse(r)
  1804  	}
  1805  }
  1806  
  1807  // GetChannelByNameForTeamName returns a channel based on the provided channel name and team name strings.
  1808  func (c *Client4) GetChannelByNameForTeamName(channelName, teamName string, etag string) (*Channel, *Response) {
  1809  	if r, err := c.DoApiGet(c.GetChannelByNameForTeamNameRoute(channelName, teamName), etag); err != nil {
  1810  		return nil, BuildErrorResponse(r, err)
  1811  	} else {
  1812  		defer closeBody(r)
  1813  		return ChannelFromJson(r.Body), BuildResponse(r)
  1814  	}
  1815  }
  1816  
  1817  func (c *Client4) GetChannelByNameForTeamNameIncludeDeleted(channelName, teamName string, etag string) (*Channel, *Response) {
  1818  	if r, err := c.DoApiGet(c.GetChannelByNameForTeamNameRoute(channelName, teamName)+"?include_deleted=true", etag); err != nil {
  1819  		return nil, BuildErrorResponse(r, err)
  1820  	} else {
  1821  		defer closeBody(r)
  1822  		return ChannelFromJson(r.Body), BuildResponse(r)
  1823  	}
  1824  }
  1825  
  1826  // GetChannelMembers gets a page of channel members.
  1827  func (c *Client4) GetChannelMembers(channelId string, page, perPage int, etag string) (*ChannelMembers, *Response) {
  1828  	query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
  1829  	if r, err := c.DoApiGet(c.GetChannelMembersRoute(channelId)+query, etag); err != nil {
  1830  		return nil, BuildErrorResponse(r, err)
  1831  	} else {
  1832  		defer closeBody(r)
  1833  		return ChannelMembersFromJson(r.Body), BuildResponse(r)
  1834  	}
  1835  }
  1836  
  1837  // GetChannelMembersByIds gets the channel members in a channel for a list of user ids.
  1838  func (c *Client4) GetChannelMembersByIds(channelId string, userIds []string) (*ChannelMembers, *Response) {
  1839  	if r, err := c.DoApiPost(c.GetChannelMembersRoute(channelId)+"/ids", ArrayToJson(userIds)); err != nil {
  1840  		return nil, BuildErrorResponse(r, err)
  1841  	} else {
  1842  		defer closeBody(r)
  1843  		return ChannelMembersFromJson(r.Body), BuildResponse(r)
  1844  
  1845  	}
  1846  }
  1847  
  1848  // GetChannelMember gets a channel member.
  1849  func (c *Client4) GetChannelMember(channelId, userId, etag string) (*ChannelMember, *Response) {
  1850  	if r, err := c.DoApiGet(c.GetChannelMemberRoute(channelId, userId), etag); err != nil {
  1851  		return nil, BuildErrorResponse(r, err)
  1852  	} else {
  1853  		defer closeBody(r)
  1854  		return ChannelMemberFromJson(r.Body), BuildResponse(r)
  1855  	}
  1856  }
  1857  
  1858  // GetChannelMembersForUser gets all the channel members for a user on a team.
  1859  func (c *Client4) GetChannelMembersForUser(userId, teamId, etag string) (*ChannelMembers, *Response) {
  1860  	if r, err := c.DoApiGet(fmt.Sprintf(c.GetUserRoute(userId)+"/teams/%v/channels/members", teamId), etag); err != nil {
  1861  		return nil, BuildErrorResponse(r, err)
  1862  	} else {
  1863  		defer closeBody(r)
  1864  		return ChannelMembersFromJson(r.Body), BuildResponse(r)
  1865  	}
  1866  }
  1867  
  1868  // ViewChannel performs a view action for a user. Synonymous with switching channels or marking channels as read by a user.
  1869  func (c *Client4) ViewChannel(userId string, view *ChannelView) (*ChannelViewResponse, *Response) {
  1870  	url := fmt.Sprintf(c.GetChannelsRoute()+"/members/%v/view", userId)
  1871  	if r, err := c.DoApiPost(url, view.ToJson()); err != nil {
  1872  		return nil, BuildErrorResponse(r, err)
  1873  	} else {
  1874  		defer closeBody(r)
  1875  		return ChannelViewResponseFromJson(r.Body), BuildResponse(r)
  1876  	}
  1877  }
  1878  
  1879  // GetChannelUnread will return a ChannelUnread object that contains the number of
  1880  // unread messages and mentions for a user.
  1881  func (c *Client4) GetChannelUnread(channelId, userId string) (*ChannelUnread, *Response) {
  1882  	if r, err := c.DoApiGet(c.GetUserRoute(userId)+c.GetChannelRoute(channelId)+"/unread", ""); err != nil {
  1883  		return nil, BuildErrorResponse(r, err)
  1884  	} else {
  1885  		defer closeBody(r)
  1886  		return ChannelUnreadFromJson(r.Body), BuildResponse(r)
  1887  	}
  1888  }
  1889  
  1890  // UpdateChannelRoles will update the roles on a channel for a user.
  1891  func (c *Client4) UpdateChannelRoles(channelId, userId, roles string) (bool, *Response) {
  1892  	requestBody := map[string]string{"roles": roles}
  1893  	if r, err := c.DoApiPut(c.GetChannelMemberRoute(channelId, userId)+"/roles", MapToJson(requestBody)); err != nil {
  1894  		return false, BuildErrorResponse(r, err)
  1895  	} else {
  1896  		defer closeBody(r)
  1897  		return CheckStatusOK(r), BuildResponse(r)
  1898  	}
  1899  }
  1900  
  1901  // UpdateChannelMemberSchemeRoles will update the scheme-derived roles on a channel for a user.
  1902  func (c *Client4) UpdateChannelMemberSchemeRoles(channelId string, userId string, schemeRoles *SchemeRoles) (bool, *Response) {
  1903  	if r, err := c.DoApiPut(c.GetChannelMemberRoute(channelId, userId)+"/schemeRoles", schemeRoles.ToJson()); err != nil {
  1904  		return false, BuildErrorResponse(r, err)
  1905  	} else {
  1906  		defer closeBody(r)
  1907  		return CheckStatusOK(r), BuildResponse(r)
  1908  	}
  1909  }
  1910  
  1911  // UpdateChannelNotifyProps will update the notification properties on a channel for a user.
  1912  func (c *Client4) UpdateChannelNotifyProps(channelId, userId string, props map[string]string) (bool, *Response) {
  1913  	if r, err := c.DoApiPut(c.GetChannelMemberRoute(channelId, userId)+"/notify_props", MapToJson(props)); err != nil {
  1914  		return false, BuildErrorResponse(r, err)
  1915  	} else {
  1916  		defer closeBody(r)
  1917  		return CheckStatusOK(r), BuildResponse(r)
  1918  	}
  1919  }
  1920  
  1921  // AddChannelMember adds user to channel and return a channel member.
  1922  func (c *Client4) AddChannelMember(channelId, userId string) (*ChannelMember, *Response) {
  1923  	requestBody := map[string]string{"user_id": userId}
  1924  	if r, err := c.DoApiPost(c.GetChannelMembersRoute(channelId)+"", MapToJson(requestBody)); err != nil {
  1925  		return nil, BuildErrorResponse(r, err)
  1926  	} else {
  1927  		defer closeBody(r)
  1928  		return ChannelMemberFromJson(r.Body), BuildResponse(r)
  1929  	}
  1930  }
  1931  
  1932  // AddChannelMemberWithRootId adds user to channel and return a channel member. Post add to channel message has the postRootId.
  1933  func (c *Client4) AddChannelMemberWithRootId(channelId, userId, postRootId string) (*ChannelMember, *Response) {
  1934  	requestBody := map[string]string{"user_id": userId, "post_root_id": postRootId}
  1935  	if r, err := c.DoApiPost(c.GetChannelMembersRoute(channelId)+"", MapToJson(requestBody)); err != nil {
  1936  		return nil, BuildErrorResponse(r, err)
  1937  	} else {
  1938  		defer closeBody(r)
  1939  		return ChannelMemberFromJson(r.Body), BuildResponse(r)
  1940  	}
  1941  }
  1942  
  1943  // RemoveUserFromChannel will delete the channel member object for a user, effectively removing the user from a channel.
  1944  func (c *Client4) RemoveUserFromChannel(channelId, userId string) (bool, *Response) {
  1945  	if r, err := c.DoApiDelete(c.GetChannelMemberRoute(channelId, userId)); err != nil {
  1946  		return false, BuildErrorResponse(r, err)
  1947  	} else {
  1948  		defer closeBody(r)
  1949  		return CheckStatusOK(r), BuildResponse(r)
  1950  	}
  1951  }
  1952  
  1953  // AutocompleteChannelsForTeam will return an ordered list of channels autocomplete suggestions
  1954  func (c *Client4) AutocompleteChannelsForTeam(teamId, name string) (*ChannelList, *Response) {
  1955  	query := fmt.Sprintf("?name=%v", name)
  1956  	if r, err := c.DoApiGet(c.GetChannelsForTeamRoute(teamId)+"/autocomplete"+query, ""); err != nil {
  1957  		return nil, BuildErrorResponse(r, err)
  1958  	} else {
  1959  		defer closeBody(r)
  1960  		return ChannelListFromJson(r.Body), BuildResponse(r)
  1961  	}
  1962  }
  1963  
  1964  // Post Section
  1965  
  1966  // CreatePost creates a post based on the provided post struct.
  1967  func (c *Client4) CreatePost(post *Post) (*Post, *Response) {
  1968  	if r, err := c.DoApiPost(c.GetPostsRoute(), post.ToUnsanitizedJson()); err != nil {
  1969  		return nil, BuildErrorResponse(r, err)
  1970  	} else {
  1971  		defer closeBody(r)
  1972  		return PostFromJson(r.Body), BuildResponse(r)
  1973  	}
  1974  }
  1975  
  1976  // CreatePostEphemeral creates a ephemeral post based on the provided post struct which is send to the given user id
  1977  func (c *Client4) CreatePostEphemeral(post *PostEphemeral) (*Post, *Response) {
  1978  	if r, err := c.DoApiPost(c.GetPostsEphemeralRoute(), post.ToUnsanitizedJson()); err != nil {
  1979  		return nil, BuildErrorResponse(r, err)
  1980  	} else {
  1981  		defer closeBody(r)
  1982  		return PostFromJson(r.Body), BuildResponse(r)
  1983  	}
  1984  }
  1985  
  1986  // UpdatePost updates a post based on the provided post struct.
  1987  func (c *Client4) UpdatePost(postId string, post *Post) (*Post, *Response) {
  1988  	if r, err := c.DoApiPut(c.GetPostRoute(postId), post.ToUnsanitizedJson()); err != nil {
  1989  		return nil, BuildErrorResponse(r, err)
  1990  	} else {
  1991  		defer closeBody(r)
  1992  		return PostFromJson(r.Body), BuildResponse(r)
  1993  	}
  1994  }
  1995  
  1996  // PatchPost partially updates a post. Any missing fields are not updated.
  1997  func (c *Client4) PatchPost(postId string, patch *PostPatch) (*Post, *Response) {
  1998  	if r, err := c.DoApiPut(c.GetPostRoute(postId)+"/patch", patch.ToJson()); err != nil {
  1999  		return nil, BuildErrorResponse(r, err)
  2000  	} else {
  2001  		defer closeBody(r)
  2002  		return PostFromJson(r.Body), BuildResponse(r)
  2003  	}
  2004  }
  2005  
  2006  // PinPost pin a post based on provided post id string.
  2007  func (c *Client4) PinPost(postId string) (bool, *Response) {
  2008  	if r, err := c.DoApiPost(c.GetPostRoute(postId)+"/pin", ""); err != nil {
  2009  		return false, BuildErrorResponse(r, err)
  2010  	} else {
  2011  		defer closeBody(r)
  2012  		return CheckStatusOK(r), BuildResponse(r)
  2013  	}
  2014  }
  2015  
  2016  // UnpinPost unpin a post based on provided post id string.
  2017  func (c *Client4) UnpinPost(postId string) (bool, *Response) {
  2018  	if r, err := c.DoApiPost(c.GetPostRoute(postId)+"/unpin", ""); err != nil {
  2019  		return false, BuildErrorResponse(r, err)
  2020  	} else {
  2021  		defer closeBody(r)
  2022  		return CheckStatusOK(r), BuildResponse(r)
  2023  	}
  2024  }
  2025  
  2026  // GetPost gets a single post.
  2027  func (c *Client4) GetPost(postId string, etag string) (*Post, *Response) {
  2028  	if r, err := c.DoApiGet(c.GetPostRoute(postId), etag); err != nil {
  2029  		return nil, BuildErrorResponse(r, err)
  2030  	} else {
  2031  		defer closeBody(r)
  2032  		return PostFromJson(r.Body), BuildResponse(r)
  2033  	}
  2034  }
  2035  
  2036  // DeletePost deletes a post from the provided post id string.
  2037  func (c *Client4) DeletePost(postId string) (bool, *Response) {
  2038  	if r, err := c.DoApiDelete(c.GetPostRoute(postId)); err != nil {
  2039  		return false, BuildErrorResponse(r, err)
  2040  	} else {
  2041  		defer closeBody(r)
  2042  		return CheckStatusOK(r), BuildResponse(r)
  2043  	}
  2044  }
  2045  
  2046  // GetPostThread gets a post with all the other posts in the same thread.
  2047  func (c *Client4) GetPostThread(postId string, etag string) (*PostList, *Response) {
  2048  	if r, err := c.DoApiGet(c.GetPostRoute(postId)+"/thread", etag); err != nil {
  2049  		return nil, BuildErrorResponse(r, err)
  2050  	} else {
  2051  		defer closeBody(r)
  2052  		return PostListFromJson(r.Body), BuildResponse(r)
  2053  	}
  2054  }
  2055  
  2056  // GetPostsForChannel gets a page of posts with an array for ordering for a channel.
  2057  func (c *Client4) GetPostsForChannel(channelId string, page, perPage int, etag string) (*PostList, *Response) {
  2058  	query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
  2059  	if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag); err != nil {
  2060  		return nil, BuildErrorResponse(r, err)
  2061  	} else {
  2062  		defer closeBody(r)
  2063  		return PostListFromJson(r.Body), BuildResponse(r)
  2064  	}
  2065  }
  2066  
  2067  // GetFlaggedPostsForUser returns flagged posts of a user based on user id string.
  2068  func (c *Client4) GetFlaggedPostsForUser(userId string, page int, perPage int) (*PostList, *Response) {
  2069  	query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
  2070  	if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/posts/flagged"+query, ""); err != nil {
  2071  		return nil, BuildErrorResponse(r, err)
  2072  	} else {
  2073  		defer closeBody(r)
  2074  		return PostListFromJson(r.Body), BuildResponse(r)
  2075  	}
  2076  }
  2077  
  2078  // GetFlaggedPostsForUserInTeam returns flagged posts in team of a user based on user id string.
  2079  func (c *Client4) GetFlaggedPostsForUserInTeam(userId string, teamId string, page int, perPage int) (*PostList, *Response) {
  2080  	if len(teamId) == 0 || len(teamId) != 26 {
  2081  		return nil, &Response{StatusCode: http.StatusBadRequest, Error: NewAppError("GetFlaggedPostsForUserInTeam", "model.client.get_flagged_posts_in_team.missing_parameter.app_error", nil, "", http.StatusBadRequest)}
  2082  	}
  2083  
  2084  	query := fmt.Sprintf("?team_id=%v&page=%v&per_page=%v", teamId, page, perPage)
  2085  	if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/posts/flagged"+query, ""); err != nil {
  2086  		return nil, BuildErrorResponse(r, err)
  2087  	} else {
  2088  		defer closeBody(r)
  2089  		return PostListFromJson(r.Body), BuildResponse(r)
  2090  	}
  2091  }
  2092  
  2093  // GetFlaggedPostsForUserInChannel returns flagged posts in channel of a user based on user id string.
  2094  func (c *Client4) GetFlaggedPostsForUserInChannel(userId string, channelId string, page int, perPage int) (*PostList, *Response) {
  2095  	if len(channelId) == 0 || len(channelId) != 26 {
  2096  		return nil, &Response{StatusCode: http.StatusBadRequest, Error: NewAppError("GetFlaggedPostsForUserInChannel", "model.client.get_flagged_posts_in_channel.missing_parameter.app_error", nil, "", http.StatusBadRequest)}
  2097  	}
  2098  
  2099  	query := fmt.Sprintf("?channel_id=%v&page=%v&per_page=%v", channelId, page, perPage)
  2100  	if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/posts/flagged"+query, ""); err != nil {
  2101  		return nil, BuildErrorResponse(r, err)
  2102  	} else {
  2103  		defer closeBody(r)
  2104  		return PostListFromJson(r.Body), BuildResponse(r)
  2105  	}
  2106  }
  2107  
  2108  // GetPostsSince gets posts created after a specified time as Unix time in milliseconds.
  2109  func (c *Client4) GetPostsSince(channelId string, time int64) (*PostList, *Response) {
  2110  	query := fmt.Sprintf("?since=%v", time)
  2111  	if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, ""); err != nil {
  2112  		return nil, BuildErrorResponse(r, err)
  2113  	} else {
  2114  		defer closeBody(r)
  2115  		return PostListFromJson(r.Body), BuildResponse(r)
  2116  	}
  2117  }
  2118  
  2119  // GetPostsAfter gets a page of posts that were posted after the post provided.
  2120  func (c *Client4) GetPostsAfter(channelId, postId string, page, perPage int, etag string) (*PostList, *Response) {
  2121  	query := fmt.Sprintf("?page=%v&per_page=%v&after=%v", page, perPage, postId)
  2122  	if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag); err != nil {
  2123  		return nil, BuildErrorResponse(r, err)
  2124  	} else {
  2125  		defer closeBody(r)
  2126  		return PostListFromJson(r.Body), BuildResponse(r)
  2127  	}
  2128  }
  2129  
  2130  // GetPostsBefore gets a page of posts that were posted before the post provided.
  2131  func (c *Client4) GetPostsBefore(channelId, postId string, page, perPage int, etag string) (*PostList, *Response) {
  2132  	query := fmt.Sprintf("?page=%v&per_page=%v&before=%v", page, perPage, postId)
  2133  	if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag); err != nil {
  2134  		return nil, BuildErrorResponse(r, err)
  2135  	} else {
  2136  		defer closeBody(r)
  2137  		return PostListFromJson(r.Body), BuildResponse(r)
  2138  	}
  2139  }
  2140  
  2141  // SearchPosts returns any posts with matching terms string.
  2142  func (c *Client4) SearchPosts(teamId string, terms string, isOrSearch bool) (*PostList, *Response) {
  2143  	params := SearchParameter{
  2144  		Terms:      &terms,
  2145  		IsOrSearch: &isOrSearch,
  2146  	}
  2147  	return c.SearchPostsWithParams(teamId, &params)
  2148  }
  2149  
  2150  // SearchPosts returns any posts with matching terms string.
  2151  func (c *Client4) SearchPostsWithParams(teamId string, params *SearchParameter) (*PostList, *Response) {
  2152  	if r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/posts/search", params.SearchParameterToJson()); err != nil {
  2153  		return nil, BuildErrorResponse(r, err)
  2154  	} else {
  2155  		defer closeBody(r)
  2156  		return PostListFromJson(r.Body), BuildResponse(r)
  2157  	}
  2158  }
  2159  
  2160  // SearchPosts returns any posts with matching terms string including deleted channels.
  2161  func (c *Client4) SearchPostsIncludeDeletedChannels(teamId string, terms string, isOrSearch bool) (*PostList, *Response) {
  2162  	requestBody := map[string]interface{}{"terms": terms, "is_or_search": isOrSearch}
  2163  	if r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/posts/search?include_deleted_channels=true", StringInterfaceToJson(requestBody)); err != nil {
  2164  		return nil, BuildErrorResponse(r, err)
  2165  	} else {
  2166  		defer closeBody(r)
  2167  		return PostListFromJson(r.Body), BuildResponse(r)
  2168  	}
  2169  }
  2170  
  2171  // SearchPosts returns any posts with matching terms string, including .
  2172  func (c *Client4) SearchPostsWithMatches(teamId string, terms string, isOrSearch bool) (*PostSearchResults, *Response) {
  2173  	requestBody := map[string]interface{}{"terms": terms, "is_or_search": isOrSearch}
  2174  	if r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/posts/search", StringInterfaceToJson(requestBody)); err != nil {
  2175  		return nil, BuildErrorResponse(r, err)
  2176  	} else {
  2177  		defer closeBody(r)
  2178  		return PostSearchResultsFromJson(r.Body), BuildResponse(r)
  2179  	}
  2180  }
  2181  
  2182  // DoPostAction performs a post action.
  2183  func (c *Client4) DoPostAction(postId, actionId string) (bool, *Response) {
  2184  	if r, err := c.DoApiPost(c.GetPostRoute(postId)+"/actions/"+actionId, ""); err != nil {
  2185  		return false, BuildErrorResponse(r, err)
  2186  	} else {
  2187  		defer closeBody(r)
  2188  		return CheckStatusOK(r), BuildResponse(r)
  2189  	}
  2190  }
  2191  
  2192  // File Section
  2193  
  2194  // UploadFile will upload a file to a channel using a multipart request, to be later attached to a post.
  2195  // This method is functionally equivalent to Client4.UploadFileAsRequestBody.
  2196  func (c *Client4) UploadFile(data []byte, channelId string, filename string) (*FileUploadResponse, *Response) {
  2197  	body := &bytes.Buffer{}
  2198  	writer := multipart.NewWriter(body)
  2199  
  2200  	if part, err := writer.CreateFormFile("files", filename); err != nil {
  2201  		return nil, &Response{Error: NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.file.app_error", nil, err.Error(), http.StatusBadRequest)}
  2202  	} else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil {
  2203  		return nil, &Response{Error: NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.file.app_error", nil, err.Error(), http.StatusBadRequest)}
  2204  	}
  2205  
  2206  	if part, err := writer.CreateFormField("channel_id"); err != nil {
  2207  		return nil, &Response{Error: NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.channel_id.app_error", nil, err.Error(), http.StatusBadRequest)}
  2208  	} else if _, err = io.Copy(part, strings.NewReader(channelId)); err != nil {
  2209  		return nil, &Response{Error: NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.channel_id.app_error", nil, err.Error(), http.StatusBadRequest)}
  2210  	}
  2211  
  2212  	if err := writer.Close(); err != nil {
  2213  		return nil, &Response{Error: NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.writer.app_error", nil, err.Error(), http.StatusBadRequest)}
  2214  	}
  2215  
  2216  	return c.DoUploadFile(c.GetFilesRoute(), body.Bytes(), writer.FormDataContentType())
  2217  }
  2218  
  2219  // UploadFileAsRequestBody will upload a file to a channel as the body of a request, to be later attached
  2220  // to a post. This method is functionally equivalent to Client4.UploadFile.
  2221  func (c *Client4) UploadFileAsRequestBody(data []byte, channelId string, filename string) (*FileUploadResponse, *Response) {
  2222  	return c.DoUploadFile(c.GetFilesRoute()+fmt.Sprintf("?channel_id=%v&filename=%v", url.QueryEscape(channelId), url.QueryEscape(filename)), data, http.DetectContentType(data))
  2223  }
  2224  
  2225  // GetFile gets the bytes for a file by id.
  2226  func (c *Client4) GetFile(fileId string) ([]byte, *Response) {
  2227  	if r, err := c.DoApiGet(c.GetFileRoute(fileId), ""); err != nil {
  2228  		return nil, BuildErrorResponse(r, err)
  2229  	} else {
  2230  		defer closeBody(r)
  2231  
  2232  		if data, err := ioutil.ReadAll(r.Body); err != nil {
  2233  			return nil, BuildErrorResponse(r, NewAppError("GetFile", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode))
  2234  		} else {
  2235  			return data, BuildResponse(r)
  2236  		}
  2237  	}
  2238  }
  2239  
  2240  // DownloadFile gets the bytes for a file by id, optionally adding headers to force the browser to download it
  2241  func (c *Client4) DownloadFile(fileId string, download bool) ([]byte, *Response) {
  2242  	if r, err := c.DoApiGet(c.GetFileRoute(fileId)+fmt.Sprintf("?download=%v", download), ""); err != nil {
  2243  		return nil, BuildErrorResponse(r, err)
  2244  	} else {
  2245  		defer closeBody(r)
  2246  
  2247  		if data, err := ioutil.ReadAll(r.Body); err != nil {
  2248  			return nil, BuildErrorResponse(r, NewAppError("DownloadFile", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode))
  2249  		} else {
  2250  			return data, BuildResponse(r)
  2251  		}
  2252  	}
  2253  }
  2254  
  2255  // GetFileThumbnail gets the bytes for a file by id.
  2256  func (c *Client4) GetFileThumbnail(fileId string) ([]byte, *Response) {
  2257  	if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/thumbnail", ""); err != nil {
  2258  		return nil, BuildErrorResponse(r, err)
  2259  	} else {
  2260  		defer closeBody(r)
  2261  
  2262  		if data, err := ioutil.ReadAll(r.Body); err != nil {
  2263  			return nil, BuildErrorResponse(r, NewAppError("GetFileThumbnail", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode))
  2264  		} else {
  2265  			return data, BuildResponse(r)
  2266  		}
  2267  	}
  2268  }
  2269  
  2270  // DownloadFileThumbnail gets the bytes for a file by id, optionally adding headers to force the browser to download it.
  2271  func (c *Client4) DownloadFileThumbnail(fileId string, download bool) ([]byte, *Response) {
  2272  	if r, err := c.DoApiGet(c.GetFileRoute(fileId)+fmt.Sprintf("/thumbnail?download=%v", download), ""); err != nil {
  2273  		return nil, BuildErrorResponse(r, err)
  2274  	} else {
  2275  		defer closeBody(r)
  2276  
  2277  		if data, err := ioutil.ReadAll(r.Body); err != nil {
  2278  			return nil, BuildErrorResponse(r, NewAppError("DownloadFileThumbnail", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode))
  2279  		} else {
  2280  			return data, BuildResponse(r)
  2281  		}
  2282  	}
  2283  }
  2284  
  2285  // GetFileLink gets the public link of a file by id.
  2286  func (c *Client4) GetFileLink(fileId string) (string, *Response) {
  2287  	if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/link", ""); err != nil {
  2288  		return "", BuildErrorResponse(r, err)
  2289  	} else {
  2290  		defer closeBody(r)
  2291  
  2292  		return MapFromJson(r.Body)["link"], BuildResponse(r)
  2293  	}
  2294  }
  2295  
  2296  // GetFilePreview gets the bytes for a file by id.
  2297  func (c *Client4) GetFilePreview(fileId string) ([]byte, *Response) {
  2298  	if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/preview", ""); err != nil {
  2299  		return nil, BuildErrorResponse(r, err)
  2300  	} else {
  2301  		defer closeBody(r)
  2302  
  2303  		if data, err := ioutil.ReadAll(r.Body); err != nil {
  2304  			return nil, BuildErrorResponse(r, NewAppError("GetFilePreview", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode))
  2305  		} else {
  2306  			return data, BuildResponse(r)
  2307  		}
  2308  	}
  2309  }
  2310  
  2311  // DownloadFilePreview gets the bytes for a file by id.
  2312  func (c *Client4) DownloadFilePreview(fileId string, download bool) ([]byte, *Response) {
  2313  	if r, err := c.DoApiGet(c.GetFileRoute(fileId)+fmt.Sprintf("/preview?download=%v", download), ""); err != nil {
  2314  		return nil, BuildErrorResponse(r, err)
  2315  	} else {
  2316  		defer closeBody(r)
  2317  
  2318  		if data, err := ioutil.ReadAll(r.Body); err != nil {
  2319  			return nil, BuildErrorResponse(r, NewAppError("DownloadFilePreview", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode))
  2320  		} else {
  2321  			return data, BuildResponse(r)
  2322  		}
  2323  	}
  2324  }
  2325  
  2326  // GetFileInfo gets all the file info objects.
  2327  func (c *Client4) GetFileInfo(fileId string) (*FileInfo, *Response) {
  2328  	if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/info", ""); err != nil {
  2329  		return nil, BuildErrorResponse(r, err)
  2330  	} else {
  2331  		defer closeBody(r)
  2332  		return FileInfoFromJson(r.Body), BuildResponse(r)
  2333  	}
  2334  }
  2335  
  2336  // GetFileInfosForPost gets all the file info objects attached to a post.
  2337  func (c *Client4) GetFileInfosForPost(postId string, etag string) ([]*FileInfo, *Response) {
  2338  	if r, err := c.DoApiGet(c.GetPostRoute(postId)+"/files/info", etag); err != nil {
  2339  		return nil, BuildErrorResponse(r, err)
  2340  	} else {
  2341  		defer closeBody(r)
  2342  		return FileInfosFromJson(r.Body), BuildResponse(r)
  2343  	}
  2344  }
  2345  
  2346  // General/System Section
  2347  
  2348  // GetPing will return ok if the running goRoutines are below the threshold and unhealthy for above.
  2349  func (c *Client4) GetPing() (string, *Response) {
  2350  	if r, err := c.DoApiGet(c.GetSystemRoute()+"/ping", ""); r != nil && r.StatusCode == 500 {
  2351  		defer r.Body.Close()
  2352  		return "unhealthy", BuildErrorResponse(r, err)
  2353  	} else if err != nil {
  2354  		return "", BuildErrorResponse(r, err)
  2355  	} else {
  2356  		defer closeBody(r)
  2357  		return MapFromJson(r.Body)["status"], BuildResponse(r)
  2358  	}
  2359  }
  2360  
  2361  // TestEmail will attempt to connect to the configured SMTP server.
  2362  func (c *Client4) TestEmail(config *Config) (bool, *Response) {
  2363  	if r, err := c.DoApiPost(c.GetTestEmailRoute(), config.ToJson()); err != nil {
  2364  		return false, BuildErrorResponse(r, err)
  2365  	} else {
  2366  		defer closeBody(r)
  2367  		return CheckStatusOK(r), BuildResponse(r)
  2368  	}
  2369  }
  2370  
  2371  // TestS3Connection will attempt to connect to the AWS S3.
  2372  func (c *Client4) TestS3Connection(config *Config) (bool, *Response) {
  2373  	if r, err := c.DoApiPost(c.GetTestS3Route(), config.ToJson()); err != nil {
  2374  		return false, BuildErrorResponse(r, err)
  2375  	} else {
  2376  		defer closeBody(r)
  2377  		return CheckStatusOK(r), BuildResponse(r)
  2378  	}
  2379  }
  2380  
  2381  // GetConfig will retrieve the server config with some sanitized items.
  2382  func (c *Client4) GetConfig() (*Config, *Response) {
  2383  	if r, err := c.DoApiGet(c.GetConfigRoute(), ""); err != nil {
  2384  		return nil, BuildErrorResponse(r, err)
  2385  	} else {
  2386  		defer closeBody(r)
  2387  		return ConfigFromJson(r.Body), BuildResponse(r)
  2388  	}
  2389  }
  2390  
  2391  // ReloadConfig will reload the server configuration.
  2392  func (c *Client4) ReloadConfig() (bool, *Response) {
  2393  	if r, err := c.DoApiPost(c.GetConfigRoute()+"/reload", ""); err != nil {
  2394  		return false, BuildErrorResponse(r, err)
  2395  	} else {
  2396  		defer closeBody(r)
  2397  		return CheckStatusOK(r), BuildResponse(r)
  2398  	}
  2399  }
  2400  
  2401  // GetOldClientConfig will retrieve the parts of the server configuration needed by the
  2402  // client, formatted in the old format.
  2403  func (c *Client4) GetOldClientConfig(etag string) (map[string]string, *Response) {
  2404  	if r, err := c.DoApiGet(c.GetConfigRoute()+"/client?format=old", etag); err != nil {
  2405  		return nil, BuildErrorResponse(r, err)
  2406  	} else {
  2407  		defer closeBody(r)
  2408  		return MapFromJson(r.Body), BuildResponse(r)
  2409  	}
  2410  }
  2411  
  2412  // GetEnvironmentConfig will retrieve a map mirroring the server configuration where fields
  2413  // are set to true if the corresponding config setting is set through an environment variable.
  2414  // Settings that haven't been set through environment variables will be missing from the map.
  2415  func (c *Client4) GetEnvironmentConfig() (map[string]interface{}, *Response) {
  2416  	if r, err := c.DoApiGet(c.GetConfigRoute()+"/environment", ""); err != nil {
  2417  		return nil, BuildErrorResponse(r, err)
  2418  	} else {
  2419  		defer closeBody(r)
  2420  		return StringInterfaceFromJson(r.Body), BuildResponse(r)
  2421  	}
  2422  }
  2423  
  2424  // GetOldClientLicense will retrieve the parts of the server license needed by the
  2425  // client, formatted in the old format.
  2426  func (c *Client4) GetOldClientLicense(etag string) (map[string]string, *Response) {
  2427  	if r, err := c.DoApiGet(c.GetLicenseRoute()+"/client?format=old", etag); err != nil {
  2428  		return nil, BuildErrorResponse(r, err)
  2429  	} else {
  2430  		defer closeBody(r)
  2431  		return MapFromJson(r.Body), BuildResponse(r)
  2432  	}
  2433  }
  2434  
  2435  // DatabaseRecycle will recycle the connections. Discard current connection and get new one.
  2436  func (c *Client4) DatabaseRecycle() (bool, *Response) {
  2437  	if r, err := c.DoApiPost(c.GetDatabaseRoute()+"/recycle", ""); err != nil {
  2438  		return false, BuildErrorResponse(r, err)
  2439  	} else {
  2440  		defer closeBody(r)
  2441  		return CheckStatusOK(r), BuildResponse(r)
  2442  	}
  2443  }
  2444  
  2445  // InvalidateCaches will purge the cache and can affect the performance while is cleaning.
  2446  func (c *Client4) InvalidateCaches() (bool, *Response) {
  2447  	if r, err := c.DoApiPost(c.GetCacheRoute()+"/invalidate", ""); err != nil {
  2448  		return false, BuildErrorResponse(r, err)
  2449  	} else {
  2450  		defer closeBody(r)
  2451  		return CheckStatusOK(r), BuildResponse(r)
  2452  	}
  2453  }
  2454  
  2455  // UpdateConfig will update the server configuration.
  2456  func (c *Client4) UpdateConfig(config *Config) (*Config, *Response) {
  2457  	if r, err := c.DoApiPut(c.GetConfigRoute(), config.ToJson()); err != nil {
  2458  		return nil, BuildErrorResponse(r, err)
  2459  	} else {
  2460  		defer closeBody(r)
  2461  		return ConfigFromJson(r.Body), BuildResponse(r)
  2462  	}
  2463  }
  2464  
  2465  // UploadLicenseFile will add a license file to the system.
  2466  func (c *Client4) UploadLicenseFile(data []byte) (bool, *Response) {
  2467  	body := &bytes.Buffer{}
  2468  	writer := multipart.NewWriter(body)
  2469  
  2470  	if part, err := writer.CreateFormFile("license", "test-license.mattermost-license"); err != nil {
  2471  		return false, &Response{Error: NewAppError("UploadLicenseFile", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)}
  2472  	} else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil {
  2473  		return false, &Response{Error: NewAppError("UploadLicenseFile", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)}
  2474  	}
  2475  
  2476  	if err := writer.Close(); err != nil {
  2477  		return false, &Response{Error: NewAppError("UploadLicenseFile", "model.client.set_profile_user.writer.app_error", nil, err.Error(), http.StatusBadRequest)}
  2478  	}
  2479  
  2480  	rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetLicenseRoute(), bytes.NewReader(body.Bytes()))
  2481  	rq.Header.Set("Content-Type", writer.FormDataContentType())
  2482  
  2483  	if len(c.AuthToken) > 0 {
  2484  		rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
  2485  	}
  2486  
  2487  	if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil {
  2488  		return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetLicenseRoute(), "model.client.connecting.app_error", nil, err.Error(), http.StatusForbidden)}
  2489  	} else {
  2490  		defer closeBody(rp)
  2491  
  2492  		if rp.StatusCode >= 300 {
  2493  			return false, BuildErrorResponse(rp, AppErrorFromJson(rp.Body))
  2494  		} else {
  2495  			return CheckStatusOK(rp), BuildResponse(rp)
  2496  		}
  2497  	}
  2498  }
  2499  
  2500  // RemoveLicenseFile will remove the server license it exists. Note that this will
  2501  // disable all enterprise features.
  2502  func (c *Client4) RemoveLicenseFile() (bool, *Response) {
  2503  	if r, err := c.DoApiDelete(c.GetLicenseRoute()); err != nil {
  2504  		return false, BuildErrorResponse(r, err)
  2505  	} else {
  2506  		defer closeBody(r)
  2507  		return CheckStatusOK(r), BuildResponse(r)
  2508  	}
  2509  }
  2510  
  2511  // GetAnalyticsOld will retrieve analytics using the old format. New format is not
  2512  // available but the "/analytics" endpoint is reserved for it. The "name" argument is optional
  2513  // and defaults to "standard". The "teamId" argument is optional and will limit results
  2514  // to a specific team.
  2515  func (c *Client4) GetAnalyticsOld(name, teamId string) (AnalyticsRows, *Response) {
  2516  	query := fmt.Sprintf("?name=%v&team_id=%v", name, teamId)
  2517  	if r, err := c.DoApiGet(c.GetAnalyticsRoute()+"/old"+query, ""); err != nil {
  2518  		return nil, BuildErrorResponse(r, err)
  2519  	} else {
  2520  		defer closeBody(r)
  2521  		return AnalyticsRowsFromJson(r.Body), BuildResponse(r)
  2522  	}
  2523  }
  2524  
  2525  // Webhooks Section
  2526  
  2527  // CreateIncomingWebhook creates an incoming webhook for a channel.
  2528  func (c *Client4) CreateIncomingWebhook(hook *IncomingWebhook) (*IncomingWebhook, *Response) {
  2529  	if r, err := c.DoApiPost(c.GetIncomingWebhooksRoute(), hook.ToJson()); err != nil {
  2530  		return nil, BuildErrorResponse(r, err)
  2531  	} else {
  2532  		defer closeBody(r)
  2533  		return IncomingWebhookFromJson(r.Body), BuildResponse(r)
  2534  	}
  2535  }
  2536  
  2537  // UpdateIncomingWebhook updates an incoming webhook for a channel.
  2538  func (c *Client4) UpdateIncomingWebhook(hook *IncomingWebhook) (*IncomingWebhook, *Response) {
  2539  	if r, err := c.DoApiPut(c.GetIncomingWebhookRoute(hook.Id), hook.ToJson()); err != nil {
  2540  		return nil, BuildErrorResponse(r, err)
  2541  	} else {
  2542  		defer closeBody(r)
  2543  		return IncomingWebhookFromJson(r.Body), BuildResponse(r)
  2544  	}
  2545  }
  2546  
  2547  // GetIncomingWebhooks returns a page of incoming webhooks on the system. Page counting starts at 0.
  2548  func (c *Client4) GetIncomingWebhooks(page int, perPage int, etag string) ([]*IncomingWebhook, *Response) {
  2549  	query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
  2550  	if r, err := c.DoApiGet(c.GetIncomingWebhooksRoute()+query, etag); err != nil {
  2551  		return nil, BuildErrorResponse(r, err)
  2552  	} else {
  2553  		defer closeBody(r)
  2554  		return IncomingWebhookListFromJson(r.Body), BuildResponse(r)
  2555  	}
  2556  }
  2557  
  2558  // GetIncomingWebhooksForTeam returns a page of incoming webhooks for a team. Page counting starts at 0.
  2559  func (c *Client4) GetIncomingWebhooksForTeam(teamId string, page int, perPage int, etag string) ([]*IncomingWebhook, *Response) {
  2560  	query := fmt.Sprintf("?page=%v&per_page=%v&team_id=%v", page, perPage, teamId)
  2561  	if r, err := c.DoApiGet(c.GetIncomingWebhooksRoute()+query, etag); err != nil {
  2562  		return nil, BuildErrorResponse(r, err)
  2563  	} else {
  2564  		defer closeBody(r)
  2565  		return IncomingWebhookListFromJson(r.Body), BuildResponse(r)
  2566  	}
  2567  }
  2568  
  2569  // GetIncomingWebhook returns an Incoming webhook given the hook ID
  2570  func (c *Client4) GetIncomingWebhook(hookID string, etag string) (*IncomingWebhook, *Response) {
  2571  	if r, err := c.DoApiGet(c.GetIncomingWebhookRoute(hookID), etag); err != nil {
  2572  		return nil, BuildErrorResponse(r, err)
  2573  	} else {
  2574  		defer closeBody(r)
  2575  		return IncomingWebhookFromJson(r.Body), BuildResponse(r)
  2576  	}
  2577  }
  2578  
  2579  // DeleteIncomingWebhook deletes and Incoming Webhook given the hook ID
  2580  func (c *Client4) DeleteIncomingWebhook(hookID string) (bool, *Response) {
  2581  	if r, err := c.DoApiDelete(c.GetIncomingWebhookRoute(hookID)); err != nil {
  2582  		return false, BuildErrorResponse(r, err)
  2583  	} else {
  2584  		defer closeBody(r)
  2585  		return CheckStatusOK(r), BuildResponse(r)
  2586  	}
  2587  }
  2588  
  2589  // CreateOutgoingWebhook creates an outgoing webhook for a team or channel.
  2590  func (c *Client4) CreateOutgoingWebhook(hook *OutgoingWebhook) (*OutgoingWebhook, *Response) {
  2591  	if r, err := c.DoApiPost(c.GetOutgoingWebhooksRoute(), hook.ToJson()); err != nil {
  2592  		return nil, BuildErrorResponse(r, err)
  2593  	} else {
  2594  		defer closeBody(r)
  2595  		return OutgoingWebhookFromJson(r.Body), BuildResponse(r)
  2596  	}
  2597  }
  2598  
  2599  // UpdateOutgoingWebhook creates an outgoing webhook for a team or channel.
  2600  func (c *Client4) UpdateOutgoingWebhook(hook *OutgoingWebhook) (*OutgoingWebhook, *Response) {
  2601  	if r, err := c.DoApiPut(c.GetOutgoingWebhookRoute(hook.Id), hook.ToJson()); err != nil {
  2602  		return nil, BuildErrorResponse(r, err)
  2603  	} else {
  2604  		defer closeBody(r)
  2605  		return OutgoingWebhookFromJson(r.Body), BuildResponse(r)
  2606  	}
  2607  }
  2608  
  2609  // GetOutgoingWebhooks returns a page of outgoing webhooks on the system. Page counting starts at 0.
  2610  func (c *Client4) GetOutgoingWebhooks(page int, perPage int, etag string) ([]*OutgoingWebhook, *Response) {
  2611  	query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
  2612  	if r, err := c.DoApiGet(c.GetOutgoingWebhooksRoute()+query, etag); err != nil {
  2613  		return nil, BuildErrorResponse(r, err)
  2614  	} else {
  2615  		defer closeBody(r)
  2616  		return OutgoingWebhookListFromJson(r.Body), BuildResponse(r)
  2617  	}
  2618  }
  2619  
  2620  // GetOutgoingWebhook outgoing webhooks on the system requested by Hook Id.
  2621  func (c *Client4) GetOutgoingWebhook(hookId string) (*OutgoingWebhook, *Response) {
  2622  	if r, err := c.DoApiGet(c.GetOutgoingWebhookRoute(hookId), ""); err != nil {
  2623  		return nil, BuildErrorResponse(r, err)
  2624  	} else {
  2625  		defer closeBody(r)
  2626  		return OutgoingWebhookFromJson(r.Body), BuildResponse(r)
  2627  	}
  2628  }
  2629  
  2630  // GetOutgoingWebhooksForChannel returns a page of outgoing webhooks for a channel. Page counting starts at 0.
  2631  func (c *Client4) GetOutgoingWebhooksForChannel(channelId string, page int, perPage int, etag string) ([]*OutgoingWebhook, *Response) {
  2632  	query := fmt.Sprintf("?page=%v&per_page=%v&channel_id=%v", page, perPage, channelId)
  2633  	if r, err := c.DoApiGet(c.GetOutgoingWebhooksRoute()+query, etag); err != nil {
  2634  		return nil, BuildErrorResponse(r, err)
  2635  	} else {
  2636  		defer closeBody(r)
  2637  		return OutgoingWebhookListFromJson(r.Body), BuildResponse(r)
  2638  	}
  2639  }
  2640  
  2641  // GetOutgoingWebhooksForTeam returns a page of outgoing webhooks for a team. Page counting starts at 0.
  2642  func (c *Client4) GetOutgoingWebhooksForTeam(teamId string, page int, perPage int, etag string) ([]*OutgoingWebhook, *Response) {
  2643  	query := fmt.Sprintf("?page=%v&per_page=%v&team_id=%v", page, perPage, teamId)
  2644  	if r, err := c.DoApiGet(c.GetOutgoingWebhooksRoute()+query, etag); err != nil {
  2645  		return nil, BuildErrorResponse(r, err)
  2646  	} else {
  2647  		defer closeBody(r)
  2648  		return OutgoingWebhookListFromJson(r.Body), BuildResponse(r)
  2649  	}
  2650  }
  2651  
  2652  // RegenOutgoingHookToken regenerate the outgoing webhook token.
  2653  func (c *Client4) RegenOutgoingHookToken(hookId string) (*OutgoingWebhook, *Response) {
  2654  	if r, err := c.DoApiPost(c.GetOutgoingWebhookRoute(hookId)+"/regen_token", ""); err != nil {
  2655  		return nil, BuildErrorResponse(r, err)
  2656  	} else {
  2657  		defer closeBody(r)
  2658  		return OutgoingWebhookFromJson(r.Body), BuildResponse(r)
  2659  	}
  2660  }
  2661  
  2662  // DeleteOutgoingWebhook delete the outgoing webhook on the system requested by Hook Id.
  2663  func (c *Client4) DeleteOutgoingWebhook(hookId string) (bool, *Response) {
  2664  	if r, err := c.DoApiDelete(c.GetOutgoingWebhookRoute(hookId)); err != nil {
  2665  		return false, BuildErrorResponse(r, err)
  2666  	} else {
  2667  		defer closeBody(r)
  2668  		return CheckStatusOK(r), BuildResponse(r)
  2669  	}
  2670  }
  2671  
  2672  // Preferences Section
  2673  
  2674  // GetPreferences returns the user's preferences.
  2675  func (c *Client4) GetPreferences(userId string) (Preferences, *Response) {
  2676  	if r, err := c.DoApiGet(c.GetPreferencesRoute(userId), ""); err != nil {
  2677  		return nil, BuildErrorResponse(r, err)
  2678  	} else {
  2679  		preferences, _ := PreferencesFromJson(r.Body)
  2680  		defer closeBody(r)
  2681  		return preferences, BuildResponse(r)
  2682  	}
  2683  }
  2684  
  2685  // UpdatePreferences saves the user's preferences.
  2686  func (c *Client4) UpdatePreferences(userId string, preferences *Preferences) (bool, *Response) {
  2687  	if r, err := c.DoApiPut(c.GetPreferencesRoute(userId), preferences.ToJson()); err != nil {
  2688  		return false, BuildErrorResponse(r, err)
  2689  	} else {
  2690  		defer closeBody(r)
  2691  		return true, BuildResponse(r)
  2692  	}
  2693  }
  2694  
  2695  // DeletePreferences deletes the user's preferences.
  2696  func (c *Client4) DeletePreferences(userId string, preferences *Preferences) (bool, *Response) {
  2697  	if r, err := c.DoApiPost(c.GetPreferencesRoute(userId)+"/delete", preferences.ToJson()); err != nil {
  2698  		return false, BuildErrorResponse(r, err)
  2699  	} else {
  2700  		defer closeBody(r)
  2701  		return true, BuildResponse(r)
  2702  	}
  2703  }
  2704  
  2705  // GetPreferencesByCategory returns the user's preferences from the provided category string.
  2706  func (c *Client4) GetPreferencesByCategory(userId string, category string) (Preferences, *Response) {
  2707  	url := fmt.Sprintf(c.GetPreferencesRoute(userId)+"/%s", category)
  2708  	if r, err := c.DoApiGet(url, ""); err != nil {
  2709  		return nil, BuildErrorResponse(r, err)
  2710  	} else {
  2711  		preferences, _ := PreferencesFromJson(r.Body)
  2712  		defer closeBody(r)
  2713  		return preferences, BuildResponse(r)
  2714  	}
  2715  }
  2716  
  2717  // GetPreferenceByCategoryAndName returns the user's preferences from the provided category and preference name string.
  2718  func (c *Client4) GetPreferenceByCategoryAndName(userId string, category string, preferenceName string) (*Preference, *Response) {
  2719  	url := fmt.Sprintf(c.GetPreferencesRoute(userId)+"/%s/name/%v", category, preferenceName)
  2720  	if r, err := c.DoApiGet(url, ""); err != nil {
  2721  		return nil, BuildErrorResponse(r, err)
  2722  	} else {
  2723  		defer closeBody(r)
  2724  		return PreferenceFromJson(r.Body), BuildResponse(r)
  2725  	}
  2726  }
  2727  
  2728  // SAML Section
  2729  
  2730  // GetSamlMetadata returns metadata for the SAML configuration.
  2731  func (c *Client4) GetSamlMetadata() (string, *Response) {
  2732  	if r, err := c.DoApiGet(c.GetSamlRoute()+"/metadata", ""); err != nil {
  2733  		return "", BuildErrorResponse(r, err)
  2734  	} else {
  2735  		defer closeBody(r)
  2736  		buf := new(bytes.Buffer)
  2737  		buf.ReadFrom(r.Body)
  2738  		return buf.String(), BuildResponse(r)
  2739  	}
  2740  }
  2741  
  2742  func samlFileToMultipart(data []byte, filename string) ([]byte, *multipart.Writer, error) {
  2743  	body := &bytes.Buffer{}
  2744  	writer := multipart.NewWriter(body)
  2745  
  2746  	if part, err := writer.CreateFormFile("certificate", filename); err != nil {
  2747  		return nil, nil, err
  2748  	} else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil {
  2749  		return nil, nil, err
  2750  	}
  2751  
  2752  	if err := writer.Close(); err != nil {
  2753  		return nil, nil, err
  2754  	}
  2755  
  2756  	return body.Bytes(), writer, nil
  2757  }
  2758  
  2759  // UploadSamlIdpCertificate will upload an IDP certificate for SAML and set the config to use it.
  2760  func (c *Client4) UploadSamlIdpCertificate(data []byte, filename string) (bool, *Response) {
  2761  	body, writer, err := samlFileToMultipart(data, filename)
  2762  	if err != nil {
  2763  		return false, &Response{Error: NewAppError("UploadSamlIdpCertificate", "model.client.upload_saml_cert.app_error", nil, err.Error(), http.StatusBadRequest)}
  2764  	}
  2765  
  2766  	_, resp := c.DoUploadFile(c.GetSamlRoute()+"/certificate/idp", body, writer.FormDataContentType())
  2767  	return resp.Error == nil, resp
  2768  }
  2769  
  2770  // UploadSamlPublicCertificate will upload a public certificate for SAML and set the config to use it.
  2771  func (c *Client4) UploadSamlPublicCertificate(data []byte, filename string) (bool, *Response) {
  2772  	body, writer, err := samlFileToMultipart(data, filename)
  2773  	if err != nil {
  2774  		return false, &Response{Error: NewAppError("UploadSamlPublicCertificate", "model.client.upload_saml_cert.app_error", nil, err.Error(), http.StatusBadRequest)}
  2775  	}
  2776  
  2777  	_, resp := c.DoUploadFile(c.GetSamlRoute()+"/certificate/public", body, writer.FormDataContentType())
  2778  	return resp.Error == nil, resp
  2779  }
  2780  
  2781  // UploadSamlPrivateCertificate will upload a private key for SAML and set the config to use it.
  2782  func (c *Client4) UploadSamlPrivateCertificate(data []byte, filename string) (bool, *Response) {
  2783  	body, writer, err := samlFileToMultipart(data, filename)
  2784  	if err != nil {
  2785  		return false, &Response{Error: NewAppError("UploadSamlPrivateCertificate", "model.client.upload_saml_cert.app_error", nil, err.Error(), http.StatusBadRequest)}
  2786  	}
  2787  
  2788  	_, resp := c.DoUploadFile(c.GetSamlRoute()+"/certificate/private", body, writer.FormDataContentType())
  2789  	return resp.Error == nil, resp
  2790  }
  2791  
  2792  // DeleteSamlIdpCertificate deletes the SAML IDP certificate from the server and updates the config to not use it and disable SAML.
  2793  func (c *Client4) DeleteSamlIdpCertificate() (bool, *Response) {
  2794  	if r, err := c.DoApiDelete(c.GetSamlRoute() + "/certificate/idp"); err != nil {
  2795  		return false, BuildErrorResponse(r, err)
  2796  	} else {
  2797  		defer closeBody(r)
  2798  		return CheckStatusOK(r), BuildResponse(r)
  2799  	}
  2800  }
  2801  
  2802  // DeleteSamlPublicCertificate deletes the SAML IDP certificate from the server and updates the config to not use it and disable SAML.
  2803  func (c *Client4) DeleteSamlPublicCertificate() (bool, *Response) {
  2804  	if r, err := c.DoApiDelete(c.GetSamlRoute() + "/certificate/public"); err != nil {
  2805  		return false, BuildErrorResponse(r, err)
  2806  	} else {
  2807  		defer closeBody(r)
  2808  		return CheckStatusOK(r), BuildResponse(r)
  2809  	}
  2810  }
  2811  
  2812  // DeleteSamlPrivateCertificate deletes the SAML IDP certificate from the server and updates the config to not use it and disable SAML.
  2813  func (c *Client4) DeleteSamlPrivateCertificate() (bool, *Response) {
  2814  	if r, err := c.DoApiDelete(c.GetSamlRoute() + "/certificate/private"); err != nil {
  2815  		return false, BuildErrorResponse(r, err)
  2816  	} else {
  2817  		defer closeBody(r)
  2818  		return CheckStatusOK(r), BuildResponse(r)
  2819  	}
  2820  }
  2821  
  2822  // GetSamlCertificateStatus returns metadata for the SAML configuration.
  2823  func (c *Client4) GetSamlCertificateStatus() (*SamlCertificateStatus, *Response) {
  2824  	if r, err := c.DoApiGet(c.GetSamlRoute()+"/certificate/status", ""); err != nil {
  2825  		return nil, BuildErrorResponse(r, err)
  2826  	} else {
  2827  		defer closeBody(r)
  2828  		return SamlCertificateStatusFromJson(r.Body), BuildResponse(r)
  2829  	}
  2830  }
  2831  
  2832  // Compliance Section
  2833  
  2834  // CreateComplianceReport creates an incoming webhook for a channel.
  2835  func (c *Client4) CreateComplianceReport(report *Compliance) (*Compliance, *Response) {
  2836  	if r, err := c.DoApiPost(c.GetComplianceReportsRoute(), report.ToJson()); err != nil {
  2837  		return nil, BuildErrorResponse(r, err)
  2838  	} else {
  2839  		defer closeBody(r)
  2840  		return ComplianceFromJson(r.Body), BuildResponse(r)
  2841  	}
  2842  }
  2843  
  2844  // GetComplianceReports returns list of compliance reports.
  2845  func (c *Client4) GetComplianceReports(page, perPage int) (Compliances, *Response) {
  2846  	query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
  2847  	if r, err := c.DoApiGet(c.GetComplianceReportsRoute()+query, ""); err != nil {
  2848  		return nil, BuildErrorResponse(r, err)
  2849  	} else {
  2850  		defer closeBody(r)
  2851  		return CompliancesFromJson(r.Body), BuildResponse(r)
  2852  	}
  2853  }
  2854  
  2855  // GetComplianceReport returns a compliance report.
  2856  func (c *Client4) GetComplianceReport(reportId string) (*Compliance, *Response) {
  2857  	if r, err := c.DoApiGet(c.GetComplianceReportRoute(reportId), ""); err != nil {
  2858  		return nil, BuildErrorResponse(r, err)
  2859  	} else {
  2860  		defer closeBody(r)
  2861  		return ComplianceFromJson(r.Body), BuildResponse(r)
  2862  	}
  2863  }
  2864  
  2865  // DownloadComplianceReport returns a full compliance report as a file.
  2866  func (c *Client4) DownloadComplianceReport(reportId string) ([]byte, *Response) {
  2867  	var rq *http.Request
  2868  	rq, _ = http.NewRequest("GET", c.ApiUrl+c.GetComplianceReportRoute(reportId), nil)
  2869  
  2870  	if len(c.AuthToken) > 0 {
  2871  		rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
  2872  	}
  2873  
  2874  	if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil {
  2875  		return nil, &Response{Error: NewAppError("DownloadComplianceReport", "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest)}
  2876  	} else {
  2877  		defer closeBody(rp)
  2878  
  2879  		if rp.StatusCode >= 300 {
  2880  			return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body))
  2881  		} else if data, err := ioutil.ReadAll(rp.Body); err != nil {
  2882  			return nil, BuildErrorResponse(rp, NewAppError("DownloadComplianceReport", "model.client.read_file.app_error", nil, err.Error(), rp.StatusCode))
  2883  		} else {
  2884  			return data, BuildResponse(rp)
  2885  		}
  2886  	}
  2887  }
  2888  
  2889  // Cluster Section
  2890  
  2891  // GetClusterStatus returns the status of all the configured cluster nodes.
  2892  func (c *Client4) GetClusterStatus() ([]*ClusterInfo, *Response) {
  2893  	if r, err := c.DoApiGet(c.GetClusterRoute()+"/status", ""); err != nil {
  2894  		return nil, BuildErrorResponse(r, err)
  2895  	} else {
  2896  		defer closeBody(r)
  2897  		return ClusterInfosFromJson(r.Body), BuildResponse(r)
  2898  	}
  2899  }
  2900  
  2901  // LDAP Section
  2902  
  2903  // SyncLdap will force a sync with the configured LDAP server.
  2904  func (c *Client4) SyncLdap() (bool, *Response) {
  2905  	if r, err := c.DoApiPost(c.GetLdapRoute()+"/sync", ""); err != nil {
  2906  		return false, BuildErrorResponse(r, err)
  2907  	} else {
  2908  		defer closeBody(r)
  2909  		return CheckStatusOK(r), BuildResponse(r)
  2910  	}
  2911  }
  2912  
  2913  // TestLdap will attempt to connect to the configured LDAP server and return OK if configured
  2914  // correctly.
  2915  func (c *Client4) TestLdap() (bool, *Response) {
  2916  	if r, err := c.DoApiPost(c.GetLdapRoute()+"/test", ""); err != nil {
  2917  		return false, BuildErrorResponse(r, err)
  2918  	} else {
  2919  		defer closeBody(r)
  2920  		return CheckStatusOK(r), BuildResponse(r)
  2921  	}
  2922  }
  2923  
  2924  // Audits Section
  2925  
  2926  // GetAudits returns a list of audits for the whole system.
  2927  func (c *Client4) GetAudits(page int, perPage int, etag string) (Audits, *Response) {
  2928  	query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
  2929  	if r, err := c.DoApiGet("/audits"+query, etag); err != nil {
  2930  		return nil, BuildErrorResponse(r, err)
  2931  	} else {
  2932  		defer closeBody(r)
  2933  		return AuditsFromJson(r.Body), BuildResponse(r)
  2934  	}
  2935  }
  2936  
  2937  // Brand Section
  2938  
  2939  // GetBrandImage retrieves the previously uploaded brand image.
  2940  func (c *Client4) GetBrandImage() ([]byte, *Response) {
  2941  	if r, err := c.DoApiGet(c.GetBrandRoute()+"/image", ""); err != nil {
  2942  		return nil, BuildErrorResponse(r, err)
  2943  	} else {
  2944  		defer closeBody(r)
  2945  
  2946  		if r.StatusCode >= 300 {
  2947  			return nil, BuildErrorResponse(r, AppErrorFromJson(r.Body))
  2948  		} else if data, err := ioutil.ReadAll(r.Body); err != nil {
  2949  			return nil, BuildErrorResponse(r, NewAppError("GetBrandImage", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode))
  2950  		} else {
  2951  			return data, BuildResponse(r)
  2952  		}
  2953  	}
  2954  }
  2955  
  2956  // UploadBrandImage sets the brand image for the system.
  2957  func (c *Client4) UploadBrandImage(data []byte) (bool, *Response) {
  2958  	body := &bytes.Buffer{}
  2959  	writer := multipart.NewWriter(body)
  2960  
  2961  	if part, err := writer.CreateFormFile("image", "brand.png"); err != nil {
  2962  		return false, &Response{Error: NewAppError("UploadBrandImage", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)}
  2963  	} else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil {
  2964  		return false, &Response{Error: NewAppError("UploadBrandImage", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)}
  2965  	}
  2966  
  2967  	if err := writer.Close(); err != nil {
  2968  		return false, &Response{Error: NewAppError("UploadBrandImage", "model.client.set_profile_user.writer.app_error", nil, err.Error(), http.StatusBadRequest)}
  2969  	}
  2970  
  2971  	rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetBrandRoute()+"/image", bytes.NewReader(body.Bytes()))
  2972  	rq.Header.Set("Content-Type", writer.FormDataContentType())
  2973  
  2974  	if len(c.AuthToken) > 0 {
  2975  		rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
  2976  	}
  2977  
  2978  	if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil {
  2979  		return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetBrandRoute()+"/image", "model.client.connecting.app_error", nil, err.Error(), http.StatusForbidden)}
  2980  	} else {
  2981  		defer closeBody(rp)
  2982  
  2983  		if rp.StatusCode >= 300 {
  2984  			return false, BuildErrorResponse(rp, AppErrorFromJson(rp.Body))
  2985  		} else {
  2986  			return CheckStatusOK(rp), BuildResponse(rp)
  2987  		}
  2988  	}
  2989  }
  2990  
  2991  // Logs Section
  2992  
  2993  // GetLogs page of logs as a string array.
  2994  func (c *Client4) GetLogs(page, perPage int) ([]string, *Response) {
  2995  	query := fmt.Sprintf("?page=%v&logs_per_page=%v", page, perPage)
  2996  	if r, err := c.DoApiGet("/logs"+query, ""); err != nil {
  2997  		return nil, BuildErrorResponse(r, err)
  2998  	} else {
  2999  		defer closeBody(r)
  3000  		return ArrayFromJson(r.Body), BuildResponse(r)
  3001  	}
  3002  }
  3003  
  3004  // PostLog is a convenience Web Service call so clients can log messages into
  3005  // the server-side logs.  For example we typically log javascript error messages
  3006  // into the server-side.  It returns the log message if the logging was successful.
  3007  func (c *Client4) PostLog(message map[string]string) (map[string]string, *Response) {
  3008  	if r, err := c.DoApiPost("/logs", MapToJson(message)); err != nil {
  3009  		return nil, BuildErrorResponse(r, err)
  3010  	} else {
  3011  		defer closeBody(r)
  3012  		return MapFromJson(r.Body), BuildResponse(r)
  3013  	}
  3014  }
  3015  
  3016  // OAuth Section
  3017  
  3018  // CreateOAuthApp will register a new OAuth 2.0 client application with Mattermost acting as an OAuth 2.0 service provider.
  3019  func (c *Client4) CreateOAuthApp(app *OAuthApp) (*OAuthApp, *Response) {
  3020  	if r, err := c.DoApiPost(c.GetOAuthAppsRoute(), app.ToJson()); err != nil {
  3021  		return nil, BuildErrorResponse(r, err)
  3022  	} else {
  3023  		defer closeBody(r)
  3024  		return OAuthAppFromJson(r.Body), BuildResponse(r)
  3025  	}
  3026  }
  3027  
  3028  // UpdateOAuthApp
  3029  func (c *Client4) UpdateOAuthApp(app *OAuthApp) (*OAuthApp, *Response) {
  3030  	if r, err := c.DoApiPut(c.GetOAuthAppRoute(app.Id), app.ToJson()); err != nil {
  3031  		return nil, BuildErrorResponse(r, err)
  3032  	} else {
  3033  		defer closeBody(r)
  3034  		return OAuthAppFromJson(r.Body), BuildResponse(r)
  3035  	}
  3036  }
  3037  
  3038  // GetOAuthApps gets a page of registered OAuth 2.0 client applications with Mattermost acting as an OAuth 2.0 service provider.
  3039  func (c *Client4) GetOAuthApps(page, perPage int) ([]*OAuthApp, *Response) {
  3040  	query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
  3041  	if r, err := c.DoApiGet(c.GetOAuthAppsRoute()+query, ""); err != nil {
  3042  		return nil, BuildErrorResponse(r, err)
  3043  	} else {
  3044  		defer closeBody(r)
  3045  		return OAuthAppListFromJson(r.Body), BuildResponse(r)
  3046  	}
  3047  }
  3048  
  3049  // GetOAuthApp gets a registered OAuth 2.0 client application with Mattermost acting as an OAuth 2.0 service provider.
  3050  func (c *Client4) GetOAuthApp(appId string) (*OAuthApp, *Response) {
  3051  	if r, err := c.DoApiGet(c.GetOAuthAppRoute(appId), ""); err != nil {
  3052  		return nil, BuildErrorResponse(r, err)
  3053  	} else {
  3054  		defer closeBody(r)
  3055  		return OAuthAppFromJson(r.Body), BuildResponse(r)
  3056  	}
  3057  }
  3058  
  3059  // GetOAuthAppInfo gets a sanitized version of a registered OAuth 2.0 client application with Mattermost acting as an OAuth 2.0 service provider.
  3060  func (c *Client4) GetOAuthAppInfo(appId string) (*OAuthApp, *Response) {
  3061  	if r, err := c.DoApiGet(c.GetOAuthAppRoute(appId)+"/info", ""); err != nil {
  3062  		return nil, BuildErrorResponse(r, err)
  3063  	} else {
  3064  		defer closeBody(r)
  3065  		return OAuthAppFromJson(r.Body), BuildResponse(r)
  3066  	}
  3067  }
  3068  
  3069  // DeleteOAuthApp deletes a registered OAuth 2.0 client application.
  3070  func (c *Client4) DeleteOAuthApp(appId string) (bool, *Response) {
  3071  	if r, err := c.DoApiDelete(c.GetOAuthAppRoute(appId)); err != nil {
  3072  		return false, BuildErrorResponse(r, err)
  3073  	} else {
  3074  		defer closeBody(r)
  3075  		return CheckStatusOK(r), BuildResponse(r)
  3076  	}
  3077  }
  3078  
  3079  // RegenerateOAuthAppSecret regenerates the client secret for a registered OAuth 2.0 client application.
  3080  func (c *Client4) RegenerateOAuthAppSecret(appId string) (*OAuthApp, *Response) {
  3081  	if r, err := c.DoApiPost(c.GetOAuthAppRoute(appId)+"/regen_secret", ""); err != nil {
  3082  		return nil, BuildErrorResponse(r, err)
  3083  	} else {
  3084  		defer closeBody(r)
  3085  		return OAuthAppFromJson(r.Body), BuildResponse(r)
  3086  	}
  3087  }
  3088  
  3089  // GetAuthorizedOAuthAppsForUser gets a page of OAuth 2.0 client applications the user has authorized to use access their account.
  3090  func (c *Client4) GetAuthorizedOAuthAppsForUser(userId string, page, perPage int) ([]*OAuthApp, *Response) {
  3091  	query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
  3092  	if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/oauth/apps/authorized"+query, ""); err != nil {
  3093  		return nil, BuildErrorResponse(r, err)
  3094  	} else {
  3095  		defer closeBody(r)
  3096  		return OAuthAppListFromJson(r.Body), BuildResponse(r)
  3097  	}
  3098  }
  3099  
  3100  // AuthorizeOAuthApp will authorize an OAuth 2.0 client application to access a user's account and provide a redirect link to follow.
  3101  func (c *Client4) AuthorizeOAuthApp(authRequest *AuthorizeRequest) (string, *Response) {
  3102  	if r, err := c.DoApiRequest(http.MethodPost, c.Url+"/oauth/authorize", authRequest.ToJson(), ""); err != nil {
  3103  		return "", BuildErrorResponse(r, err)
  3104  	} else {
  3105  		defer closeBody(r)
  3106  		return MapFromJson(r.Body)["redirect"], BuildResponse(r)
  3107  	}
  3108  }
  3109  
  3110  // DeauthorizeOAuthApp will deauthorize an OAuth 2.0 client application from accessing a user's account.
  3111  func (c *Client4) DeauthorizeOAuthApp(appId string) (bool, *Response) {
  3112  	requestData := map[string]string{"client_id": appId}
  3113  	if r, err := c.DoApiRequest(http.MethodPost, c.Url+"/oauth/deauthorize", MapToJson(requestData), ""); err != nil {
  3114  		return false, BuildErrorResponse(r, err)
  3115  	} else {
  3116  		defer closeBody(r)
  3117  		return CheckStatusOK(r), BuildResponse(r)
  3118  	}
  3119  }
  3120  
  3121  // GetOAuthAccessToken is a test helper function for the OAuth access token endpoint.
  3122  func (c *Client4) GetOAuthAccessToken(data url.Values) (*AccessResponse, *Response) {
  3123  	rq, _ := http.NewRequest(http.MethodPost, c.Url+"/oauth/access_token", strings.NewReader(data.Encode()))
  3124  	rq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  3125  
  3126  	if len(c.AuthToken) > 0 {
  3127  		rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
  3128  	}
  3129  
  3130  	if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil {
  3131  		return nil, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.Url+"/oauth/access_token", "model.client.connecting.app_error", nil, err.Error(), 403)}
  3132  	} else {
  3133  		defer closeBody(rp)
  3134  		if rp.StatusCode >= 300 {
  3135  			return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body))
  3136  		} else {
  3137  			return AccessResponseFromJson(rp.Body), BuildResponse(rp)
  3138  		}
  3139  	}
  3140  }
  3141  
  3142  // Elasticsearch Section
  3143  
  3144  // TestElasticsearch will attempt to connect to the configured Elasticsearch server and return OK if configured
  3145  // correctly.
  3146  func (c *Client4) TestElasticsearch() (bool, *Response) {
  3147  	if r, err := c.DoApiPost(c.GetElasticsearchRoute()+"/test", ""); err != nil {
  3148  		return false, BuildErrorResponse(r, err)
  3149  	} else {
  3150  		defer closeBody(r)
  3151  		return CheckStatusOK(r), BuildResponse(r)
  3152  	}
  3153  }
  3154  
  3155  // PurgeElasticsearchIndexes immediately deletes all Elasticsearch indexes.
  3156  func (c *Client4) PurgeElasticsearchIndexes() (bool, *Response) {
  3157  	if r, err := c.DoApiPost(c.GetElasticsearchRoute()+"/purge_indexes", ""); err != nil {
  3158  		return false, BuildErrorResponse(r, err)
  3159  	} else {
  3160  		defer closeBody(r)
  3161  		return CheckStatusOK(r), BuildResponse(r)
  3162  	}
  3163  }
  3164  
  3165  // Data Retention Section
  3166  
  3167  // GetDataRetentionPolicy will get the current server data retention policy details.
  3168  func (c *Client4) GetDataRetentionPolicy() (*DataRetentionPolicy, *Response) {
  3169  	if r, err := c.DoApiGet(c.GetDataRetentionRoute()+"/policy", ""); err != nil {
  3170  		return nil, BuildErrorResponse(r, err)
  3171  	} else {
  3172  		defer closeBody(r)
  3173  		return DataRetentionPolicyFromJson(r.Body), BuildResponse(r)
  3174  	}
  3175  }
  3176  
  3177  // Commands Section
  3178  
  3179  // CreateCommand will create a new command if the user have the right permissions.
  3180  func (c *Client4) CreateCommand(cmd *Command) (*Command, *Response) {
  3181  	if r, err := c.DoApiPost(c.GetCommandsRoute(), cmd.ToJson()); err != nil {
  3182  		return nil, BuildErrorResponse(r, err)
  3183  	} else {
  3184  		defer closeBody(r)
  3185  		return CommandFromJson(r.Body), BuildResponse(r)
  3186  	}
  3187  }
  3188  
  3189  // UpdateCommand updates a command based on the provided Command struct
  3190  func (c *Client4) UpdateCommand(cmd *Command) (*Command, *Response) {
  3191  	if r, err := c.DoApiPut(c.GetCommandRoute(cmd.Id), cmd.ToJson()); err != nil {
  3192  		return nil, BuildErrorResponse(r, err)
  3193  	} else {
  3194  		defer closeBody(r)
  3195  		return CommandFromJson(r.Body), BuildResponse(r)
  3196  	}
  3197  }
  3198  
  3199  // DeleteCommand deletes a command based on the provided command id string
  3200  func (c *Client4) DeleteCommand(commandId string) (bool, *Response) {
  3201  	if r, err := c.DoApiDelete(c.GetCommandRoute(commandId)); err != nil {
  3202  		return false, BuildErrorResponse(r, err)
  3203  	} else {
  3204  		defer closeBody(r)
  3205  		return CheckStatusOK(r), BuildResponse(r)
  3206  	}
  3207  }
  3208  
  3209  // ListCommands will retrieve a list of commands available in the team.
  3210  func (c *Client4) ListCommands(teamId string, customOnly bool) ([]*Command, *Response) {
  3211  	query := fmt.Sprintf("?team_id=%v&custom_only=%v", teamId, customOnly)
  3212  	if r, err := c.DoApiGet(c.GetCommandsRoute()+query, ""); err != nil {
  3213  		return nil, BuildErrorResponse(r, err)
  3214  	} else {
  3215  		defer closeBody(r)
  3216  		return CommandListFromJson(r.Body), BuildResponse(r)
  3217  	}
  3218  }
  3219  
  3220  // ExecuteCommand executes a given slash command.
  3221  func (c *Client4) ExecuteCommand(channelId, command string) (*CommandResponse, *Response) {
  3222  	commandArgs := &CommandArgs{
  3223  		ChannelId: channelId,
  3224  		Command:   command,
  3225  	}
  3226  	if r, err := c.DoApiPost(c.GetCommandsRoute()+"/execute", commandArgs.ToJson()); err != nil {
  3227  		return nil, BuildErrorResponse(r, err)
  3228  	} else {
  3229  		defer closeBody(r)
  3230  
  3231  		response, _ := CommandResponseFromJson(r.Body)
  3232  		return response, BuildResponse(r)
  3233  	}
  3234  }
  3235  
  3236  // ExecuteCommand executes a given slash command against the specified team
  3237  // Use this when executing slash commands in a DM/GM, since the team id cannot be inferred in that case
  3238  func (c *Client4) ExecuteCommandWithTeam(channelId, teamId, command string) (*CommandResponse, *Response) {
  3239  	commandArgs := &CommandArgs{
  3240  		ChannelId: channelId,
  3241  		TeamId:    teamId,
  3242  		Command:   command,
  3243  	}
  3244  	if r, err := c.DoApiPost(c.GetCommandsRoute()+"/execute", commandArgs.ToJson()); err != nil {
  3245  		return nil, BuildErrorResponse(r, err)
  3246  	} else {
  3247  		defer closeBody(r)
  3248  
  3249  		response, _ := CommandResponseFromJson(r.Body)
  3250  		return response, BuildResponse(r)
  3251  	}
  3252  }
  3253  
  3254  // ListCommands will retrieve a list of commands available in the team.
  3255  func (c *Client4) ListAutocompleteCommands(teamId string) ([]*Command, *Response) {
  3256  	if r, err := c.DoApiGet(c.GetTeamAutoCompleteCommandsRoute(teamId), ""); err != nil {
  3257  		return nil, BuildErrorResponse(r, err)
  3258  	} else {
  3259  		defer closeBody(r)
  3260  		return CommandListFromJson(r.Body), BuildResponse(r)
  3261  	}
  3262  }
  3263  
  3264  // RegenCommandToken will create a new token if the user have the right permissions.
  3265  func (c *Client4) RegenCommandToken(commandId string) (string, *Response) {
  3266  	if r, err := c.DoApiPut(c.GetCommandRoute(commandId)+"/regen_token", ""); err != nil {
  3267  		return "", BuildErrorResponse(r, err)
  3268  	} else {
  3269  		defer closeBody(r)
  3270  		return MapFromJson(r.Body)["token"], BuildResponse(r)
  3271  	}
  3272  }
  3273  
  3274  // Status Section
  3275  
  3276  // GetUserStatus returns a user based on the provided user id string.
  3277  func (c *Client4) GetUserStatus(userId, etag string) (*Status, *Response) {
  3278  	if r, err := c.DoApiGet(c.GetUserStatusRoute(userId), etag); err != nil {
  3279  		return nil, BuildErrorResponse(r, err)
  3280  	} else {
  3281  		defer closeBody(r)
  3282  		return StatusFromJson(r.Body), BuildResponse(r)
  3283  	}
  3284  }
  3285  
  3286  // GetUsersStatusesByIds returns a list of users status based on the provided user ids.
  3287  func (c *Client4) GetUsersStatusesByIds(userIds []string) ([]*Status, *Response) {
  3288  	if r, err := c.DoApiPost(c.GetUserStatusesRoute()+"/ids", ArrayToJson(userIds)); err != nil {
  3289  		return nil, BuildErrorResponse(r, err)
  3290  	} else {
  3291  		defer closeBody(r)
  3292  		return StatusListFromJson(r.Body), BuildResponse(r)
  3293  	}
  3294  }
  3295  
  3296  // UpdateUserStatus sets a user's status based on the provided user id string.
  3297  func (c *Client4) UpdateUserStatus(userId string, userStatus *Status) (*Status, *Response) {
  3298  	if r, err := c.DoApiPut(c.GetUserStatusRoute(userId), userStatus.ToJson()); err != nil {
  3299  		return nil, BuildErrorResponse(r, err)
  3300  	} else {
  3301  		defer closeBody(r)
  3302  		return StatusFromJson(r.Body), BuildResponse(r)
  3303  
  3304  	}
  3305  }
  3306  
  3307  // Webrtc Section
  3308  
  3309  // GetWebrtcToken returns a valid token, stun server and turn server with credentials to
  3310  // use with the Mattermost WebRTC service.
  3311  func (c *Client4) GetWebrtcToken() (*WebrtcInfoResponse, *Response) {
  3312  	if r, err := c.DoApiGet("/webrtc/token", ""); err != nil {
  3313  		return nil, BuildErrorResponse(r, err)
  3314  	} else {
  3315  		defer closeBody(r)
  3316  		return WebrtcInfoResponseFromJson(r.Body), BuildResponse(r)
  3317  	}
  3318  }
  3319  
  3320  // Emoji Section
  3321  
  3322  // CreateEmoji will save an emoji to the server if the current user has permission
  3323  // to do so. If successful, the provided emoji will be returned with its Id field
  3324  // filled in. Otherwise, an error will be returned.
  3325  func (c *Client4) CreateEmoji(emoji *Emoji, image []byte, filename string) (*Emoji, *Response) {
  3326  	body := &bytes.Buffer{}
  3327  	writer := multipart.NewWriter(body)
  3328  
  3329  	if part, err := writer.CreateFormFile("image", filename); err != nil {
  3330  		return nil, &Response{StatusCode: http.StatusForbidden, Error: NewAppError("CreateEmoji", "model.client.create_emoji.image.app_error", nil, err.Error(), 0)}
  3331  	} else if _, err = io.Copy(part, bytes.NewBuffer(image)); err != nil {
  3332  		return nil, &Response{StatusCode: http.StatusForbidden, Error: NewAppError("CreateEmoji", "model.client.create_emoji.image.app_error", nil, err.Error(), 0)}
  3333  	}
  3334  
  3335  	if err := writer.WriteField("emoji", emoji.ToJson()); err != nil {
  3336  		return nil, &Response{StatusCode: http.StatusForbidden, Error: NewAppError("CreateEmoji", "model.client.create_emoji.emoji.app_error", nil, err.Error(), 0)}
  3337  	}
  3338  
  3339  	if err := writer.Close(); err != nil {
  3340  		return nil, &Response{StatusCode: http.StatusForbidden, Error: NewAppError("CreateEmoji", "model.client.create_emoji.writer.app_error", nil, err.Error(), 0)}
  3341  	}
  3342  
  3343  	return c.DoEmojiUploadFile(c.GetEmojisRoute(), body.Bytes(), writer.FormDataContentType())
  3344  }
  3345  
  3346  // GetEmojiList returns a page of custom emoji on the system.
  3347  func (c *Client4) GetEmojiList(page, perPage int) ([]*Emoji, *Response) {
  3348  	query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
  3349  	if r, err := c.DoApiGet(c.GetEmojisRoute()+query, ""); err != nil {
  3350  		return nil, BuildErrorResponse(r, err)
  3351  	} else {
  3352  		defer closeBody(r)
  3353  		return EmojiListFromJson(r.Body), BuildResponse(r)
  3354  	}
  3355  }
  3356  
  3357  // GetSortedEmojiList returns a page of custom emoji on the system sorted based on the sort
  3358  // parameter, blank for no sorting and "name" to sort by emoji names.
  3359  func (c *Client4) GetSortedEmojiList(page, perPage int, sort string) ([]*Emoji, *Response) {
  3360  	query := fmt.Sprintf("?page=%v&per_page=%v&sort=%v", page, perPage, sort)
  3361  	if r, err := c.DoApiGet(c.GetEmojisRoute()+query, ""); err != nil {
  3362  		return nil, BuildErrorResponse(r, err)
  3363  	} else {
  3364  		defer closeBody(r)
  3365  		return EmojiListFromJson(r.Body), BuildResponse(r)
  3366  	}
  3367  }
  3368  
  3369  // DeleteEmoji delete an custom emoji on the provided emoji id string.
  3370  func (c *Client4) DeleteEmoji(emojiId string) (bool, *Response) {
  3371  	if r, err := c.DoApiDelete(c.GetEmojiRoute(emojiId)); err != nil {
  3372  		return false, BuildErrorResponse(r, err)
  3373  	} else {
  3374  		defer closeBody(r)
  3375  		return CheckStatusOK(r), BuildResponse(r)
  3376  	}
  3377  }
  3378  
  3379  // GetEmoji returns a custom emoji based on the emojiId string.
  3380  func (c *Client4) GetEmoji(emojiId string) (*Emoji, *Response) {
  3381  	if r, err := c.DoApiGet(c.GetEmojiRoute(emojiId), ""); err != nil {
  3382  		return nil, BuildErrorResponse(r, err)
  3383  	} else {
  3384  		defer closeBody(r)
  3385  		return EmojiFromJson(r.Body), BuildResponse(r)
  3386  	}
  3387  }
  3388  
  3389  // GetEmojiByName returns a custom emoji based on the name string.
  3390  func (c *Client4) GetEmojiByName(name string) (*Emoji, *Response) {
  3391  	if r, err := c.DoApiGet(c.GetEmojiByNameRoute(name), ""); err != nil {
  3392  		return nil, BuildErrorResponse(r, err)
  3393  	} else {
  3394  		defer closeBody(r)
  3395  		return EmojiFromJson(r.Body), BuildResponse(r)
  3396  	}
  3397  }
  3398  
  3399  // GetEmojiImage returns the emoji image.
  3400  func (c *Client4) GetEmojiImage(emojiId string) ([]byte, *Response) {
  3401  	if r, err := c.DoApiGet(c.GetEmojiRoute(emojiId)+"/image", ""); err != nil {
  3402  		return nil, BuildErrorResponse(r, err)
  3403  	} else {
  3404  		defer closeBody(r)
  3405  
  3406  		if data, err := ioutil.ReadAll(r.Body); err != nil {
  3407  			return nil, BuildErrorResponse(r, NewAppError("GetEmojiImage", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode))
  3408  		} else {
  3409  			return data, BuildResponse(r)
  3410  		}
  3411  	}
  3412  }
  3413  
  3414  // SearchEmoji returns a list of emoji matching some search criteria.
  3415  func (c *Client4) SearchEmoji(search *EmojiSearch) ([]*Emoji, *Response) {
  3416  	if r, err := c.DoApiPost(c.GetEmojisRoute()+"/search", search.ToJson()); err != nil {
  3417  		return nil, BuildErrorResponse(r, err)
  3418  	} else {
  3419  		defer closeBody(r)
  3420  		return EmojiListFromJson(r.Body), BuildResponse(r)
  3421  	}
  3422  }
  3423  
  3424  // AutocompleteEmoji returns a list of emoji starting with or matching name.
  3425  func (c *Client4) AutocompleteEmoji(name string, etag string) ([]*Emoji, *Response) {
  3426  	query := fmt.Sprintf("?name=%v", name)
  3427  	if r, err := c.DoApiGet(c.GetEmojisRoute()+"/autocomplete"+query, ""); err != nil {
  3428  		return nil, BuildErrorResponse(r, err)
  3429  	} else {
  3430  		defer closeBody(r)
  3431  		return EmojiListFromJson(r.Body), BuildResponse(r)
  3432  	}
  3433  }
  3434  
  3435  // Reaction Section
  3436  
  3437  // SaveReaction saves an emoji reaction for a post. Returns the saved reaction if successful, otherwise an error will be returned.
  3438  func (c *Client4) SaveReaction(reaction *Reaction) (*Reaction, *Response) {
  3439  	if r, err := c.DoApiPost(c.GetReactionsRoute(), reaction.ToJson()); err != nil {
  3440  		return nil, BuildErrorResponse(r, err)
  3441  	} else {
  3442  		defer closeBody(r)
  3443  		return ReactionFromJson(r.Body), BuildResponse(r)
  3444  	}
  3445  }
  3446  
  3447  // GetReactions returns a list of reactions to a post.
  3448  func (c *Client4) GetReactions(postId string) ([]*Reaction, *Response) {
  3449  	if r, err := c.DoApiGet(c.GetPostRoute(postId)+"/reactions", ""); err != nil {
  3450  		return nil, BuildErrorResponse(r, err)
  3451  	} else {
  3452  		defer closeBody(r)
  3453  		return ReactionsFromJson(r.Body), BuildResponse(r)
  3454  	}
  3455  }
  3456  
  3457  // DeleteReaction deletes reaction of a user in a post.
  3458  func (c *Client4) DeleteReaction(reaction *Reaction) (bool, *Response) {
  3459  	if r, err := c.DoApiDelete(c.GetUserRoute(reaction.UserId) + c.GetPostRoute(reaction.PostId) + fmt.Sprintf("/reactions/%v", reaction.EmojiName)); err != nil {
  3460  		return false, BuildErrorResponse(r, err)
  3461  	} else {
  3462  		defer closeBody(r)
  3463  		return CheckStatusOK(r), BuildResponse(r)
  3464  	}
  3465  }
  3466  
  3467  // Timezone Section
  3468  
  3469  // GetSupportedTimezone returns a page of supported timezones on the system.
  3470  func (c *Client4) GetSupportedTimezone() (SupportedTimezones, *Response) {
  3471  	if r, err := c.DoApiGet(c.GetTimezonesRoute(), ""); err != nil {
  3472  		return nil, BuildErrorResponse(r, err)
  3473  	} else {
  3474  		defer closeBody(r)
  3475  		return TimezonesFromJson(r.Body), BuildResponse(r)
  3476  	}
  3477  }
  3478  
  3479  // Open Graph Metadata Section
  3480  
  3481  // OpenGraph return the open graph metadata for a particular url if the site have the metadata
  3482  func (c *Client4) OpenGraph(url string) (map[string]string, *Response) {
  3483  	requestBody := make(map[string]string)
  3484  	requestBody["url"] = url
  3485  
  3486  	if r, err := c.DoApiPost(c.GetOpenGraphRoute(), MapToJson(requestBody)); err != nil {
  3487  		return nil, BuildErrorResponse(r, err)
  3488  	} else {
  3489  		defer closeBody(r)
  3490  		return MapFromJson(r.Body), BuildResponse(r)
  3491  	}
  3492  }
  3493  
  3494  // Jobs Section
  3495  
  3496  // GetJob gets a single job.
  3497  func (c *Client4) GetJob(id string) (*Job, *Response) {
  3498  	if r, err := c.DoApiGet(c.GetJobsRoute()+fmt.Sprintf("/%v", id), ""); err != nil {
  3499  		return nil, BuildErrorResponse(r, err)
  3500  	} else {
  3501  		defer closeBody(r)
  3502  		return JobFromJson(r.Body), BuildResponse(r)
  3503  	}
  3504  }
  3505  
  3506  // Get all jobs, sorted with the job that was created most recently first.
  3507  func (c *Client4) GetJobs(page int, perPage int) ([]*Job, *Response) {
  3508  	if r, err := c.DoApiGet(c.GetJobsRoute()+fmt.Sprintf("?page=%v&per_page=%v", page, perPage), ""); err != nil {
  3509  		return nil, BuildErrorResponse(r, err)
  3510  	} else {
  3511  		defer closeBody(r)
  3512  		return JobsFromJson(r.Body), BuildResponse(r)
  3513  	}
  3514  }
  3515  
  3516  // GetJobsByType gets all jobs of a given type, sorted with the job that was created most recently first.
  3517  func (c *Client4) GetJobsByType(jobType string, page int, perPage int) ([]*Job, *Response) {
  3518  	if r, err := c.DoApiGet(c.GetJobsRoute()+fmt.Sprintf("/type/%v?page=%v&per_page=%v", jobType, page, perPage), ""); err != nil {
  3519  		return nil, BuildErrorResponse(r, err)
  3520  	} else {
  3521  		defer closeBody(r)
  3522  		return JobsFromJson(r.Body), BuildResponse(r)
  3523  	}
  3524  }
  3525  
  3526  // CreateJob creates a job based on the provided job struct.
  3527  func (c *Client4) CreateJob(job *Job) (*Job, *Response) {
  3528  	if r, err := c.DoApiPost(c.GetJobsRoute(), job.ToJson()); err != nil {
  3529  		return nil, BuildErrorResponse(r, err)
  3530  	} else {
  3531  		defer closeBody(r)
  3532  		return JobFromJson(r.Body), BuildResponse(r)
  3533  	}
  3534  }
  3535  
  3536  // CancelJob requests the cancellation of the job with the provided Id.
  3537  func (c *Client4) CancelJob(jobId string) (bool, *Response) {
  3538  	if r, err := c.DoApiPost(c.GetJobsRoute()+fmt.Sprintf("/%v/cancel", jobId), ""); err != nil {
  3539  		return false, BuildErrorResponse(r, err)
  3540  	} else {
  3541  		defer closeBody(r)
  3542  		return CheckStatusOK(r), BuildResponse(r)
  3543  	}
  3544  }
  3545  
  3546  // Roles Section
  3547  
  3548  // GetRole gets a single role by ID.
  3549  func (c *Client4) GetRole(id string) (*Role, *Response) {
  3550  	if r, err := c.DoApiGet(c.GetRolesRoute()+fmt.Sprintf("/%v", id), ""); err != nil {
  3551  		return nil, BuildErrorResponse(r, err)
  3552  	} else {
  3553  		defer closeBody(r)
  3554  		return RoleFromJson(r.Body), BuildResponse(r)
  3555  	}
  3556  }
  3557  
  3558  // GetRoleByName gets a single role by Name.
  3559  func (c *Client4) GetRoleByName(name string) (*Role, *Response) {
  3560  	if r, err := c.DoApiGet(c.GetRolesRoute()+fmt.Sprintf("/name/%v", name), ""); err != nil {
  3561  		return nil, BuildErrorResponse(r, err)
  3562  	} else {
  3563  		defer closeBody(r)
  3564  		return RoleFromJson(r.Body), BuildResponse(r)
  3565  	}
  3566  }
  3567  
  3568  // GetRolesByNames returns a list of roles based on the provided role names.
  3569  func (c *Client4) GetRolesByNames(roleNames []string) ([]*Role, *Response) {
  3570  	if r, err := c.DoApiPost(c.GetRolesRoute()+"/names", ArrayToJson(roleNames)); err != nil {
  3571  		return nil, BuildErrorResponse(r, err)
  3572  	} else {
  3573  		defer closeBody(r)
  3574  		return RoleListFromJson(r.Body), BuildResponse(r)
  3575  	}
  3576  }
  3577  
  3578  // PatchRole partially updates a role in the system. Any missing fields are not updated.
  3579  func (c *Client4) PatchRole(roleId string, patch *RolePatch) (*Role, *Response) {
  3580  	if r, err := c.DoApiPut(c.GetRolesRoute()+fmt.Sprintf("/%v/patch", roleId), patch.ToJson()); err != nil {
  3581  		return nil, BuildErrorResponse(r, err)
  3582  	} else {
  3583  		defer closeBody(r)
  3584  		return RoleFromJson(r.Body), BuildResponse(r)
  3585  	}
  3586  }
  3587  
  3588  // Schemes Section
  3589  
  3590  // CreateScheme creates a new Scheme.
  3591  func (c *Client4) CreateScheme(scheme *Scheme) (*Scheme, *Response) {
  3592  	if r, err := c.DoApiPost(c.GetSchemesRoute(), scheme.ToJson()); err != nil {
  3593  		return nil, BuildErrorResponse(r, err)
  3594  	} else {
  3595  		defer closeBody(r)
  3596  		return SchemeFromJson(r.Body), BuildResponse(r)
  3597  	}
  3598  }
  3599  
  3600  // GetScheme gets a single scheme by ID.
  3601  func (c *Client4) GetScheme(id string) (*Scheme, *Response) {
  3602  	if r, err := c.DoApiGet(c.GetSchemeRoute(id), ""); err != nil {
  3603  		return nil, BuildErrorResponse(r, err)
  3604  	} else {
  3605  		defer closeBody(r)
  3606  		return SchemeFromJson(r.Body), BuildResponse(r)
  3607  	}
  3608  }
  3609  
  3610  // Get all schemes, sorted with the most recently created first, optionally filtered by scope.
  3611  func (c *Client4) GetSchemes(scope string, page int, perPage int) ([]*Scheme, *Response) {
  3612  	if r, err := c.DoApiGet(c.GetSchemesRoute()+fmt.Sprintf("?scope=%v&page=%v&per_page=%v", scope, page, perPage), ""); err != nil {
  3613  		return nil, BuildErrorResponse(r, err)
  3614  	} else {
  3615  		defer closeBody(r)
  3616  		return SchemesFromJson(r.Body), BuildResponse(r)
  3617  	}
  3618  }
  3619  
  3620  // DeleteScheme deletes a single scheme by ID.
  3621  func (c *Client4) DeleteScheme(id string) (bool, *Response) {
  3622  	if r, err := c.DoApiDelete(c.GetSchemeRoute(id)); err != nil {
  3623  		return false, BuildErrorResponse(r, err)
  3624  	} else {
  3625  		defer closeBody(r)
  3626  		return CheckStatusOK(r), BuildResponse(r)
  3627  	}
  3628  }
  3629  
  3630  // PatchScheme partially updates a scheme in the system. Any missing fields are not updated.
  3631  func (c *Client4) PatchScheme(id string, patch *SchemePatch) (*Scheme, *Response) {
  3632  	if r, err := c.DoApiPut(c.GetSchemeRoute(id)+"/patch", patch.ToJson()); err != nil {
  3633  		return nil, BuildErrorResponse(r, err)
  3634  	} else {
  3635  		defer closeBody(r)
  3636  		return SchemeFromJson(r.Body), BuildResponse(r)
  3637  	}
  3638  }
  3639  
  3640  // Get the teams using this scheme, sorted alphabetically by display name.
  3641  func (c *Client4) GetTeamsForScheme(schemeId string, page int, perPage int) ([]*Team, *Response) {
  3642  	if r, err := c.DoApiGet(c.GetSchemeRoute(schemeId)+fmt.Sprintf("/teams?page=%v&per_page=%v", page, perPage), ""); err != nil {
  3643  		return nil, BuildErrorResponse(r, err)
  3644  	} else {
  3645  		defer closeBody(r)
  3646  		return TeamListFromJson(r.Body), BuildResponse(r)
  3647  	}
  3648  }
  3649  
  3650  // Get the channels using this scheme, sorted alphabetically by display name.
  3651  func (c *Client4) GetChannelsForScheme(schemeId string, page int, perPage int) (ChannelList, *Response) {
  3652  	if r, err := c.DoApiGet(c.GetSchemeRoute(schemeId)+fmt.Sprintf("/channels?page=%v&per_page=%v", page, perPage), ""); err != nil {
  3653  		return nil, BuildErrorResponse(r, err)
  3654  	} else {
  3655  		defer closeBody(r)
  3656  		return *ChannelListFromJson(r.Body), BuildResponse(r)
  3657  	}
  3658  }
  3659  
  3660  // Plugin Section
  3661  
  3662  // UploadPlugin takes an io.Reader stream pointing to the contents of a .tar.gz plugin.
  3663  // WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE.
  3664  func (c *Client4) UploadPlugin(file io.Reader) (*Manifest, *Response) {
  3665  	body := new(bytes.Buffer)
  3666  	writer := multipart.NewWriter(body)
  3667  
  3668  	if part, err := writer.CreateFormFile("plugin", "plugin.tar.gz"); err != nil {
  3669  		return nil, &Response{Error: NewAppError("UploadPlugin", "model.client.writer.app_error", nil, err.Error(), 0)}
  3670  	} else if _, err = io.Copy(part, file); err != nil {
  3671  		return nil, &Response{Error: NewAppError("UploadPlugin", "model.client.writer.app_error", nil, err.Error(), 0)}
  3672  	}
  3673  
  3674  	if err := writer.Close(); err != nil {
  3675  		return nil, &Response{Error: NewAppError("UploadPlugin", "model.client.writer.app_error", nil, err.Error(), 0)}
  3676  	}
  3677  
  3678  	rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetPluginsRoute(), body)
  3679  	rq.Header.Set("Content-Type", writer.FormDataContentType())
  3680  
  3681  	if len(c.AuthToken) > 0 {
  3682  		rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
  3683  	}
  3684  
  3685  	if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil {
  3686  		return nil, BuildErrorResponse(rp, NewAppError("UploadPlugin", "model.client.connecting.app_error", nil, err.Error(), 0))
  3687  	} else {
  3688  		defer closeBody(rp)
  3689  
  3690  		if rp.StatusCode >= 300 {
  3691  			return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body))
  3692  		} else {
  3693  			return ManifestFromJson(rp.Body), BuildResponse(rp)
  3694  		}
  3695  	}
  3696  }
  3697  
  3698  // GetPlugins will return a list of plugin manifests for currently active plugins.
  3699  // WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE.
  3700  func (c *Client4) GetPlugins() (*PluginsResponse, *Response) {
  3701  	if r, err := c.DoApiGet(c.GetPluginsRoute(), ""); err != nil {
  3702  		return nil, BuildErrorResponse(r, err)
  3703  	} else {
  3704  		defer closeBody(r)
  3705  		return PluginsResponseFromJson(r.Body), BuildResponse(r)
  3706  	}
  3707  }
  3708  
  3709  // GetPluginStatuses will return the plugins installed on any server in the cluster, for reporting
  3710  // to the administrator via the system console.
  3711  // WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE.
  3712  func (c *Client4) GetPluginStatuses() (PluginStatuses, *Response) {
  3713  	if r, err := c.DoApiGet(c.GetPluginsRoute(), "/statuses"); err != nil {
  3714  		return nil, BuildErrorResponse(r, err)
  3715  	} else {
  3716  		defer closeBody(r)
  3717  		return PluginStatusesFromJson(r.Body), BuildResponse(r)
  3718  	}
  3719  }
  3720  
  3721  // RemovePlugin will deactivate and delete a plugin.
  3722  // WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE.
  3723  func (c *Client4) RemovePlugin(id string) (bool, *Response) {
  3724  	if r, err := c.DoApiDelete(c.GetPluginRoute(id)); err != nil {
  3725  		return false, BuildErrorResponse(r, err)
  3726  	} else {
  3727  		defer closeBody(r)
  3728  		return CheckStatusOK(r), BuildResponse(r)
  3729  	}
  3730  }
  3731  
  3732  // GetWebappPlugins will return a list of plugins that the webapp should download.
  3733  // WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE.
  3734  func (c *Client4) GetWebappPlugins() ([]*Manifest, *Response) {
  3735  	if r, err := c.DoApiGet(c.GetPluginsRoute()+"/webapp", ""); err != nil {
  3736  		return nil, BuildErrorResponse(r, err)
  3737  	} else {
  3738  		defer closeBody(r)
  3739  		return ManifestListFromJson(r.Body), BuildResponse(r)
  3740  	}
  3741  }
  3742  
  3743  // ActivatePlugin will activate an plugin installed.
  3744  // WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE.
  3745  func (c *Client4) EnablePlugin(id string) (bool, *Response) {
  3746  	if r, err := c.DoApiPost(c.GetPluginRoute(id)+"/enable", ""); err != nil {
  3747  		return false, BuildErrorResponse(r, err)
  3748  	} else {
  3749  		defer closeBody(r)
  3750  		return CheckStatusOK(r), BuildResponse(r)
  3751  	}
  3752  }
  3753  
  3754  // DeactivatePlugin will deactivate an active plugin.
  3755  // WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE.
  3756  func (c *Client4) DisablePlugin(id string) (bool, *Response) {
  3757  	if r, err := c.DoApiPost(c.GetPluginRoute(id)+"/disable", ""); err != nil {
  3758  		return false, BuildErrorResponse(r, err)
  3759  	} else {
  3760  		defer closeBody(r)
  3761  		return CheckStatusOK(r), BuildResponse(r)
  3762  	}
  3763  }
  3764  
  3765  // UpdateChannelScheme will update a channel's scheme.
  3766  func (c *Client4) UpdateChannelScheme(channelId, schemeId string) (bool, *Response) {
  3767  	sip := &SchemeIDPatch{SchemeID: &schemeId}
  3768  	if r, err := c.DoApiPut(c.GetChannelSchemeRoute(channelId), sip.ToJson()); err != nil {
  3769  		return false, BuildErrorResponse(r, err)
  3770  	} else {
  3771  		defer closeBody(r)
  3772  		return CheckStatusOK(r), BuildResponse(r)
  3773  	}
  3774  }
  3775  
  3776  // UpdateTeamScheme will update a team's scheme.
  3777  func (c *Client4) UpdateTeamScheme(teamId, schemeId string) (bool, *Response) {
  3778  	sip := &SchemeIDPatch{SchemeID: &schemeId}
  3779  	if r, err := c.DoApiPut(c.GetTeamSchemeRoute(teamId), sip.ToJson()); err != nil {
  3780  		return false, BuildErrorResponse(r, err)
  3781  	} else {
  3782  		defer closeBody(r)
  3783  		return CheckStatusOK(r), BuildResponse(r)
  3784  	}
  3785  }
  3786  
  3787  // GetRedirectLocation retrieves the value of the 'Location' header of an HTTP response for a given URL.
  3788  func (c *Client4) GetRedirectLocation(urlParam, etag string) (string, *Response) {
  3789  	url := fmt.Sprintf("%s?url=%s", c.GetRedirectLocationRoute(), url.QueryEscape(urlParam))
  3790  	if r, err := c.DoApiGet(url, etag); err != nil {
  3791  		return "", BuildErrorResponse(r, err)
  3792  	} else {
  3793  		defer closeBody(r)
  3794  		return MapFromJson(r.Body)["location"], BuildResponse(r)
  3795  	}
  3796  }