fix: close response body on all paths in newErrorStatusNotOK

The 401 and 404 early-return paths returned without closing the HTTP
response body, leaking the underlying connection. Move the
defer body.Close() to the top of the function so it runs regardless
of which code path is taken.

fix: add HTTP status check to GetCurrentUser

GetCurrentUser did not validate the HTTP response status code. A
401/403/500 response was silently ignored and returned a zero-value
User pointer, causing callers (e.g. RestrictPageUpdatesCloud fallback)
to use an empty accountId.

fix: return nil on HTTP 204 from DeletePageLabel instead of panicking

DeletePageLabel accepted both 200 OK and 204 No Content as success, but
then unconditionally did request.Response.(*LabelInfo). On a 204 the
response body is empty so request.Response is nil; the type assertion
panics. Return (nil, nil) for 204 responses.

fix: paginate GetPageLabels to handle pages with >50 labels

A single request with the default page size silently truncated label
lists longer than the API default (~50). Add a pagination loop
matching the pattern used by GetAttachments.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Manuel Rüger 2026-03-13 02:11:18 +01:00
parent 14219aea59
commit 0d7caab5d8

View File

@ -661,26 +661,54 @@ func (api *API) DeletePageLabel(page *PageInfo, label string) (*LabelInfo, error
return nil, err
}
if request.Raw.StatusCode != http.StatusOK && request.Raw.StatusCode != http.StatusNoContent {
return nil, newErrorStatusNotOK(request)
if request.Raw.StatusCode == http.StatusNoContent {
return nil, nil
}
return request.Response.(*LabelInfo), nil
}
func (api *API) GetPageLabels(page *PageInfo, prefix string) (*LabelInfo, error) {
request, err := api.rest.Res(
"content/"+page.ID+"/label", &LabelInfo{},
).Get(map[string]string{"prefix": prefix})
if err != nil {
return nil, err
type labelPage struct {
Links struct {
Next string `json:"next"`
} `json:"_links"`
Labels []Label `json:"results"`
Size int `json:"number"`
}
if request.Raw.StatusCode != http.StatusOK {
return nil, newErrorStatusNotOK(request)
const pageSize = 50
var all []Label
start := 0
for {
var result labelPage
request, err := api.rest.Res(
"content/"+page.ID+"/label", &result,
).Get(map[string]string{
"prefix": prefix,
"limit": fmt.Sprintf("%d", pageSize),
"start": fmt.Sprintf("%d", start),
})
if err != nil {
return nil, err
}
if request.Raw.StatusCode != http.StatusOK {
return nil, newErrorStatusNotOK(request)
}
all = append(all, result.Labels...)
if len(result.Labels) < pageSize || result.Links.Next == "" {
break
}
start += len(result.Labels)
}
return request.Response.(*LabelInfo), nil
return &LabelInfo{Labels: all, Size: len(all)}, nil
}
func (api *API) GetUserByName(name string) (*User, error) {
@ -734,7 +762,7 @@ func (api *API) GetUserByName(name string) (*User, error) {
func (api *API) GetCurrentUser() (*User, error) {
var user User
_, err := api.rest.
request, err := api.rest.
Res("user").
Res("current", &user).
Get()
@ -742,6 +770,10 @@ func (api *API) GetCurrentUser() (*User, error) {
return nil, err
}
if request.Raw.StatusCode != http.StatusOK {
return nil, newErrorStatusNotOK(request)
}
return &user, nil
}
@ -845,6 +877,10 @@ func (api *API) RestrictPageUpdates(
}
func newErrorStatusNotOK(request *gopencils.Resource) error {
defer func() {
_ = request.Raw.Body.Close()
}()
if request.Raw.StatusCode == http.StatusUnauthorized {
return errors.New(
"the Confluence API returned unexpected status: 401 (Unauthorized)",
@ -857,10 +893,6 @@ func newErrorStatusNotOK(request *gopencils.Resource) error {
)
}
defer func() {
_ = request.Raw.Body.Close()
}()
output, _ := io.ReadAll(request.Raw.Body)
return fmt.Errorf(