fix: RestrictPageUpdatesCloud now resolves allowedUser by name

The allowedUser parameter was completely ignored; the function always
restricted edits to the currently authenticated API user via
GetCurrentUser(). Resolve the specified user via GetUserByName first
and fall back to the current user only if that lookup fails, matching
the behaviour of RestrictPageUpdatesServer which uses the parameter
directly.

fix: paginate GetAttachments to handle pages with >100 attachments

The previous implementation fetched a single page of up to 1000
attachments. Pages with more than 1000 attachments would silently
miss some, causing attachment sync to skip or re-upload them.
Replace with a pagination loop (100 per page) that follows the
_links.next cursor until all attachments are retrieved.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Manuel Rüger 2026-03-13 01:50:45 +01:00
parent fef39dc1e0
commit b7c9229da4

View File

@ -437,38 +437,55 @@ func getAttachmentPayload(name, comment string, reader io.Reader) (*form, error)
}
func (api *API) GetAttachments(pageID string) ([]AttachmentInfo, error) {
result := struct {
type page struct {
Links struct {
Context string `json:"context"`
Next string `json:"next"`
} `json:"_links"`
Results []AttachmentInfo `json:"results"`
}{}
payload := map[string]string{
"expand": "version,container",
"limit": "1000",
}
request, err := api.rest.Res(
"content/"+pageID+"/child/attachment", &result,
).Get(payload)
if err != nil {
return nil, err
}
const pageSize = 100
var all []AttachmentInfo
start := 0
if request.Raw.StatusCode != http.StatusOK {
return nil, newErrorStatusNotOK(request)
}
for {
var result page
for i, info := range result.Results {
if info.Links.Context == "" {
info.Links.Context = result.Links.Context
payload := map[string]string{
"expand": "version,container",
"limit": fmt.Sprintf("%d", pageSize),
"start": fmt.Sprintf("%d", start),
}
result.Results[i] = info
request, err := api.rest.Res(
"content/"+pageID+"/child/attachment", &result,
).Get(payload)
if err != nil {
return nil, err
}
if request.Raw.StatusCode != http.StatusOK {
return nil, newErrorStatusNotOK(request)
}
for i, info := range result.Results {
if info.Links.Context == "" {
info.Links.Context = result.Links.Context
}
result.Results[i] = info
}
all = append(all, result.Results...)
if len(result.Results) < pageSize || result.Links.Next == "" {
break
}
start += len(result.Results)
}
return result.Results, nil
return all, nil
}
func (api *API) GetPageByID(pageID string) (*PageInfo, error) {
@ -726,9 +743,16 @@ func (api *API) RestrictPageUpdatesCloud(
page *PageInfo,
allowedUser string,
) error {
user, err := api.GetCurrentUser()
user, err := api.GetUserByName(allowedUser)
if err != nil {
return err
// Fall back to the currently authenticated user if the specified
// user cannot be resolved by name (e.g. on Confluence Cloud where
// only accountId is accepted and name lookup may fail).
currentUser, currentErr := api.GetCurrentUser()
if currentErr != nil {
return fmt.Errorf("unable to resolve user %q: %w", allowedUser, err)
}
user = currentUser
}
var result interface{}