fix(wiki,notification): guard wiki edit renames, show real revision totals and tidy notification view

Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
bircni committed 2026-10-18 20:01:48 +02:00
1 parent f7ed123cec
commit dd9e05c6b9
6 files changed
+145 -24

No files matched your search

+39 -11
View File
@@ -150,22 +150,45 @@ func (s Wiki) Create(ctx context.Context, opts WikiOptions) (WikiPage, error) {
item, err := s.page(ctx, func(ctx context.Context, c *gitea.Client, owner, repo string) (*gitea.WikiPage, *gitea.Response, error) {
return c.Wiki.CreatePage(ctx, owner, repo, wikiOptions(opts))
})
if IsNotFound(err) {
// Gitea answers a bare 404 when the wiki is disabled or the repository is missing, which
// says nothing about the page being created.
repository, checkErr := (Repositories{Client: s.Client}).get(ctx, s.Repository)
if checkErr != nil {
return WikiPage{}, checkErr
}
if !repository.HasWiki {
return WikiPage{}, fmt.Errorf("the wiki of %s is disabled; turn it on with: tea repo edit --enable-wiki -R %s", s.Repository, s.Repository)
}
}
return item, Classify(err, "wiki page", strconv.Quote(opts.Title), s.Repository)
}
// Edit replaces page content and optionally changes its title.
func (s Wiki) Edit(ctx context.Context, name string, opts WikiOptions) (WikiPage, error) {
// Edit replaces page content and optionally changes its title. It reports whether the title
// changed. Gitea's edit creates a page that does not exist and overwrites one the new title
// names, so both are checked first.
func (s Wiki) Edit(ctx context.Context, name string, opts WikiOptions) (WikiPage, bool, error) {
current, err := s.View(ctx, name)
if err != nil {
return WikiPage{}, false, err
}
renamed := opts.Title != "" && opts.Title != current.Title
if opts.Title == "" {
current, err := s.View(ctx, name)
if err != nil {
return WikiPage{}, err
}
opts.Title = current.Title
}
if renamed {
existing, err := s.View(ctx, opts.Title)
if err == nil && existing.Title != current.Title {
return WikiPage{}, false, &ConflictError{Reason: ReasonExists, Kind: "wiki page", Key: strconv.Quote(opts.Title), Scope: s.Repository}
}
if err != nil && !IsNotFound(err) {
return WikiPage{}, false, err
}
}
item, err := s.page(ctx, func(ctx context.Context, c *gitea.Client, owner, repo string) (*gitea.WikiPage, *gitea.Response, error) {
return c.Wiki.EditPage(ctx, owner, repo, name, wikiOptions(opts))
})
return item, Classify(err, "wiki page", strconv.Quote(name), s.Repository)
return item, renamed, Classify(err, "wiki page", strconv.Quote(name), s.Repository)
}
// Delete removes a named wiki page.
@@ -176,13 +199,18 @@ func (s Wiki) Delete(ctx context.Context, name string) error {
return Classify(err, "wiki page", strconv.Quote(name), s.Repository)
}
// Revisions returns one server-paginated page of wiki commits.
func (s Wiki) Revisions(ctx context.Context, name string, page int) ([]WikiRevision, error) {
// wikiRevisionPageSize is Gitea's default number of wiki commits per page ([git] COMMITS_RANGE_SIZE).
const wikiRevisionPageSize = 50
// Revisions returns one server-paginated page of wiki commits and the total the server counted.
func (s Wiki) Revisions(ctx context.Context, name string, page int) (Page[WikiRevision], error) {
revisions, err := repoCall(ctx, s.Client, s.Repository, func(ctx context.Context, c *gitea.Client, owner, repo string) (*gitea.WikiCommitList, *gitea.Response, error) {
return c.Wiki.GetPageRevisions(ctx, owner, repo, name, gitea.ListWikiPageRevisionsOptions{Page: page})
})
if err != nil {
return nil, err
return Page[WikiRevision]{}, err
}
return convertAll(revisions.WikiCommits, wikiRevisionFrom), nil
items := convertAll(revisions.WikiCommits, wikiRevisionFrom)
total := int(revisions.Count)
return Page[WikiRevision]{Items: items, Total: total, Counted: true, More: (page-1)*wikiRevisionPageSize+len(items) < total}, nil
}
+17 -2
View File
@@ -29,7 +29,7 @@ func TestWikiContentAndNamedPagePath(t *testing.T) {
_, _ = w.Write([]byte(`{"title":"Name with spaces","content_base64":"w7xuaWNvZGUKdGV4dA==","last_commit":{"sha":"abc","author":{"name":"Alice","email":"author@example.test","date":"2026-10-04T00:00:00Z"}}}`))
}))
service := Wiki{Client: client, Repository: "alice/project"}
item, err := service.Edit(context.Background(), "Name with spaces", WikiOptions{Body: "ünicode\ntext"})
item, _, err := service.Edit(context.Background(), "Name with spaces", WikiOptions{Body: "ünicode\ntext"})
require.NoError(t, err)
require.Equal(t, "ünicode\ntext", item.Body)
require.Equal(t, "Alice", item.Author)
@@ -96,7 +96,9 @@ func TestWikiListAndRevisionsMapCommits(t *testing.T) {
require.Equal(t, []WikiPage{{Title: "Draft"}}, last.Items)
revisions, err := service.Revisions(context.Background(), "Home", 3)
require.NoError(t, err)
require.Equal(t, []WikiRevision{{SHA: "abc", Message: "Edit", Author: "Alice", CreatedAt: "2026-10-04T00:00:00Z"}, {SHA: "def"}}, revisions)
require.Equal(t, []WikiRevision{{SHA: "abc", Message: "Edit", Author: "Alice", CreatedAt: "2026-10-04T00:00:00Z"}, {SHA: "def"}}, revisions.Items)
require.True(t, revisions.Counted)
require.Equal(t, 2, revisions.Total)
}
func TestWikiCreateAndDelete(t *testing.T) {
@@ -119,3 +121,16 @@ func TestWikiCreateAndDelete(t *testing.T) {
require.Equal(t, WikiPage{Title: "New", Body: "body", CommitCount: 1, Sidebar: "side", Footer: "foot"}, page)
require.NoError(t, service.Delete(context.Background(), "Old page"))
}
func TestWikiCreateExplainsADisabledWiki(t *testing.T) {
client := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/api/v1/repos/alice/project" {
_, _ = w.Write([]byte(`{"full_name":"alice/project","has_wiki":false}`))
return
}
http.NotFound(w, r)
}))
_, err := (Wiki{Client: client, Repository: "alice/project"}).Create(context.Background(), WikiOptions{Title: "New", Body: "body"})
require.ErrorContains(t, err, "the wiki of alice/project is disabled")
require.NotErrorAs(t, err, new(*NotFoundError))
}