Convert bool string comparisons to strconv.ParseBool for REST parameters (#13650)

* Convert bool string comparisons to strconv.ParseBool for REST parameters

* Log failed bool conversions

* Rename errors, changed log levels

* drop strconv.ParseBool error handling

If the query string parameter is omitted, strconv.ParseBool returns an error for the empty strings, which spams the logs. Instead, just assume the default semantics of a `false` return value if an error occurs.

* allow randomized Client4 booleans

It's hard to test api4's handling of the various boolean input values
accepted. Extend Client4 with support for overriding how it builds those
strings, and pick a random value on test startup.

* gofmt -s

Co-authored-by: mattermod <mattermod@users.noreply.github.com>
Co-authored-by: Ben Schumacher <ben.schumacher@mattermost.com>
Co-authored-by: Jesse Hallam <jesse.hallam@gmail.com>
Этот коммит содержится в:
Arianna Vespri
2020-05-23 23:01:31 +02:00
коммит произвёл GitHub
родитель f7a91c7cf9
Коммит 2135096d88
7 изменённых файлов: 96 добавлений и 73 удалений

Просмотреть файл

@@ -61,6 +61,40 @@ type Client4 struct {
AuthToken string
AuthType string
HttpHeader map[string]string // Headers to be copied over for each request
// TrueString is the string value sent to the server for true boolean query parameters.
trueString string
// FalseString is the string value sent to the server for false boolean query parameters.
falseString string
}
// SetBoolString is a helper method for overriding how true and false query string parameters are
// sent to the server.
//
// This method is only exposed for testing. It is never necessary to configure these values
// in production.
func (c *Client4) SetBoolString(value bool, valueStr string) {
if value {
c.trueString = valueStr
} else {
c.falseString = valueStr
}
}
// boolString builds the query string parameter for boolean values.
func (c *Client4) boolString(value bool) string {
if value && c.trueString != "" {
return c.trueString
} else if !value && c.falseString != "" {
return c.falseString
}
if value {
return "true"
} else {
return "false"
}
}
func closeBody(r *http.Response) {
@@ -81,7 +115,7 @@ func (c *Client4) Must(result interface{}, resp *Response) interface{} {
}
func NewAPIv4Client(url string) *Client4 {
return &Client4{url, url + API_URL_SUFFIX, &http.Client{}, "", "", map[string]string{}}
return &Client4{url, url + API_URL_SUFFIX, &http.Client{}, "", "", map[string]string{}, "", ""}
}
func BuildErrorResponse(r *http.Response, err *AppError) *Response {
@@ -650,7 +684,7 @@ func (c *Client4) LoginByLdap(loginId string, password string) (*User, *Response
m := make(map[string]string)
m["login_id"] = loginId
m["password"] = password
m["ldap_only"] = "true"
m["ldap_only"] = c.boolString(true)
return c.login(m)
}
@@ -1476,7 +1510,7 @@ func (c *Client4) GetBot(userId string, etag string) (*Bot, *Response) {
// GetBot fetches the given bot, even if it is deleted.
func (c *Client4) GetBotIncludeDeleted(userId string, etag string) (*Bot, *Response) {
r, err := c.DoApiGet(c.GetBotRoute(userId)+"?include_deleted=true", etag)
r, err := c.DoApiGet(c.GetBotRoute(userId)+"?include_deleted="+c.boolString(true), etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
@@ -1497,7 +1531,7 @@ func (c *Client4) GetBots(page, perPage int, etag string) ([]*Bot, *Response) {
// GetBotsIncludeDeleted fetches the given page of bots, including deleted.
func (c *Client4) GetBotsIncludeDeleted(page, perPage int, etag string) ([]*Bot, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v&include_deleted=true", page, perPage)
query := fmt.Sprintf("?page=%v&per_page=%v&include_deleted="+c.boolString(true), page, perPage)
r, err := c.DoApiGet(c.GetBotsRoute()+query, etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
@@ -1508,7 +1542,7 @@ func (c *Client4) GetBotsIncludeDeleted(page, perPage int, etag string) ([]*Bot,
// GetBotsOrphaned fetches the given page of bots, only including orphanded bots.
func (c *Client4) GetBotsOrphaned(page, perPage int, etag string) ([]*Bot, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v&only_orphaned=true", page, perPage)
query := fmt.Sprintf("?page=%v&per_page=%v&only_orphaned="+c.boolString(true), page, perPage)
r, err := c.DoApiGet(c.GetBotsRoute()+query, etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
@@ -1648,7 +1682,7 @@ func (c *Client4) GetAllTeams(etag string, page int, perPage int) ([]*Team, *Res
// GetAllTeamsWithTotalCount returns all teams based on permissions.
func (c *Client4) GetAllTeamsWithTotalCount(etag string, page int, perPage int) ([]*Team, int64, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v&include_total_count=true", page, perPage)
query := fmt.Sprintf("?page=%v&per_page=%v&include_total_count="+c.boolString(true), page, perPage)
r, err := c.DoApiGet(c.GetTeamsRoute()+query, etag)
if err != nil {
return nil, 0, BuildErrorResponse(r, err)
@@ -1800,7 +1834,7 @@ func (c *Client4) SoftDeleteTeam(teamId string) (bool, *Response) {
// PermanentDeleteTeam deletes the team, should only be used when needed for
// compliance and the like.
func (c *Client4) PermanentDeleteTeam(teamId string) (bool, *Response) {
r, err := c.DoApiDelete(c.GetTeamRoute(teamId) + "?permanent=true")
r, err := c.DoApiDelete(c.GetTeamRoute(teamId) + "?permanent=" + c.boolString(true))
if err != nil {
return false, BuildErrorResponse(r, err)
}
@@ -1920,7 +1954,7 @@ func (c *Client4) AddTeamMembersGracefully(teamId string, userIds []string) ([]*
members = append(members, member)
}
r, err := c.DoApiPost(c.GetTeamMembersRoute(teamId)+"/batch?graceful=true", TeamMembersToJson(members))
r, err := c.DoApiPost(c.GetTeamMembersRoute(teamId)+"/batch?graceful="+c.boolString(true), TeamMembersToJson(members))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
@@ -2038,7 +2072,7 @@ func (c *Client4) InviteGuestsToTeam(teamId string, userEmails []string, channel
// InviteUsersToTeam invite users by email to the team.
func (c *Client4) InviteUsersToTeamGracefully(teamId string, userEmails []string) ([]*EmailInviteWithError, *Response) {
r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/invite/email?graceful=true", ArrayToJson(userEmails))
r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/invite/email?graceful="+c.boolString(true), ArrayToJson(userEmails))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
@@ -2053,7 +2087,7 @@ func (c *Client4) InviteGuestsToTeamGracefully(teamId string, userEmails []strin
Channels: channels,
Message: message,
}
r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/invite-guests/email?graceful=true", guestsInvite.ToJson())
r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/invite-guests/email?graceful="+c.boolString(true), guestsInvite.ToJson())
if err != nil {
return nil, BuildErrorResponse(r, err)
}
@@ -2163,7 +2197,7 @@ func (c *Client4) GetAllChannels(page int, perPage int, etag string) (*ChannelLi
// GetAllChannelsWithCount get all the channels including the total count. Must be a system administrator.
func (c *Client4) GetAllChannelsWithCount(page int, perPage int, etag string) (*ChannelListWithTeamData, int64, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v&include_total_count=true", page, perPage)
query := fmt.Sprintf("?page=%v&per_page=%v&include_total_count="+c.boolString(true), page, perPage)
r, err := c.DoApiGet(c.GetChannelsRoute()+query, etag)
if err != nil {
return nil, 0, BuildErrorResponse(r, err)
@@ -2410,7 +2444,7 @@ func (c *Client4) GetChannelByName(channelName, teamId string, etag string) (*Ch
// GetChannelByNameIncludeDeleted returns a channel based on the provided channel name and team id strings. Other then GetChannelByName it will also return deleted channels.
func (c *Client4) GetChannelByNameIncludeDeleted(channelName, teamId string, etag string) (*Channel, *Response) {
r, err := c.DoApiGet(c.GetChannelByNameRoute(channelName, teamId)+"?include_deleted=true", etag)
r, err := c.DoApiGet(c.GetChannelByNameRoute(channelName, teamId)+"?include_deleted="+c.boolString(true), etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
@@ -2430,7 +2464,7 @@ func (c *Client4) GetChannelByNameForTeamName(channelName, teamName string, etag
// GetChannelByNameForTeamNameIncludeDeleted returns a channel based on the provided channel name and team name strings. Other then GetChannelByNameForTeamName it will also return deleted channels.
func (c *Client4) GetChannelByNameForTeamNameIncludeDeleted(channelName, teamName string, etag string) (*Channel, *Response) {
r, err := c.DoApiGet(c.GetChannelByNameForTeamNameRoute(channelName, teamName)+"?include_deleted=true", etag)
r, err := c.DoApiGet(c.GetChannelByNameForTeamNameRoute(channelName, teamName)+"?include_deleted="+c.boolString(true), etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
@@ -3048,7 +3082,7 @@ func (c *Client4) GetPing() (string, *Response) {
// GetPingWithServerStatus will return ok if several basic server health checks
// all pass successfully.
func (c *Client4) GetPingWithServerStatus() (string, *Response) {
r, err := c.DoApiGet(c.GetSystemRoute()+"/ping?get_server_status=true", "")
r, err := c.DoApiGet(c.GetSystemRoute()+"/ping?get_server_status="+c.boolString(true), "")
if r != nil && r.StatusCode == 500 {
defer r.Body.Close()
return STATUS_UNHEALTHY, BuildErrorResponse(r, err)
@@ -4638,7 +4672,7 @@ func (c *Client4) uploadPlugin(file io.Reader, force bool) (*Manifest, *Response
writer := multipart.NewWriter(body)
if force {
err := writer.WriteField("force", "true")
err := writer.WriteField("force", c.boolString(true))
if err != nil {
return nil, &Response{Error: NewAppError("UploadPlugin", "model.client.writer.app_error", nil, err.Error(), 0)}
}
@@ -4681,10 +4715,7 @@ func (c *Client4) uploadPlugin(file io.Reader, force bool) (*Manifest, *Response
}
func (c *Client4) InstallPluginFromUrl(downloadUrl string, force bool) (*Manifest, *Response) {
forceStr := "false"
if force {
forceStr = "true"
}
forceStr := c.boolString(force)
url := fmt.Sprintf("%s?plugin_download_url=%s&force=%s", c.GetPluginsRoute()+"/install_from_url", url.QueryEscape(downloadUrl), forceStr)
r, err := c.DoApiPost(url, "")