Skip to content

Commit 3676faf

Browse files
qwerty287delvhnoerw6543zeripath
authored
Add API to get/edit wiki (#17278)
* Add API to get/edit wiki * Add swagger docs, various improvements * fmt * Fix lint and rm comment * Add page parameter * Add pagination to pages * Add tests * fmt * Update func names * Update error handling * Update type name * Fix lint * Don't delete Home * Update func name * Update routers/api/v1/repo/wiki.go Co-authored-by: delvh <[email protected]> * Remove unnecessary check * Fix lint * Use English strings * Update integrations/api_wiki_test.go Co-authored-by: delvh <[email protected]> * Update func and test names * Remove unsed check and avoid duplicated error reports * Improve error handling * Return after error * Document 404 error * Update swagger * Fix lint * Apply suggestions from code review Co-authored-by: delvh <[email protected]> * Document file encoding * fmt * Apply suggestions * Use convert * Fix integration test * simplify permissions * unify duplicate key Title/Name * improve types & return UTC timestamps * improve types pt.2 - add WikiPageMetaData.LastCommit - add WikiPageMetaData.HTMLURL - replace WikiPageMetaData.Updated with .LastCommit.Committer.Created also delete convert.ToWikiPage(), as it received too many arguments and only had one callsite anyway. sorry for bad advice earlier 🙃 * WikiPage.Content is base64 encoded * simplify error handling in wikiContentsByName() * update swagger * fix & DRY findWikiRepoCommit() error handling ListWikiPages() previously wrote error twice when repo wiki didn't exist * rename Content -> ContentBase64 * Fix test * Fix tests * Update var name * suburl -> sub_url Co-authored-by: delvh <[email protected]> Co-authored-by: Norwin <[email protected]> Co-authored-by: 6543 <[email protected]> Co-authored-by: zeripath <[email protected]> Co-authored-by: wxiaoguang <[email protected]>
1 parent 843bc9d commit 3676faf

File tree

8 files changed

+1336
-1
lines changed

8 files changed

+1336
-1
lines changed

integrations/api_wiki_test.go

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
// Copyright 2021 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package integrations
6+
7+
import (
8+
"encoding/base64"
9+
"fmt"
10+
"net/http"
11+
"testing"
12+
13+
api "code.gitea.io/gitea/modules/structs"
14+
15+
"github.com/stretchr/testify/assert"
16+
)
17+
18+
func TestAPIGetWikiPage(t *testing.T) {
19+
defer prepareTestEnv(t)()
20+
21+
username := "user2"
22+
session := loginUser(t, username)
23+
24+
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/page/Home", username, "repo1")
25+
26+
req := NewRequest(t, "GET", urlStr)
27+
resp := session.MakeRequest(t, req, http.StatusOK)
28+
var page *api.WikiPage
29+
DecodeJSON(t, resp, &page)
30+
31+
assert.Equal(t, &api.WikiPage{
32+
WikiPageMetaData: &api.WikiPageMetaData{
33+
Title: "Home",
34+
HTMLURL: page.HTMLURL,
35+
SubURL: "Home",
36+
LastCommit: &api.WikiCommit{
37+
ID: "2c54faec6c45d31c1abfaecdab471eac6633738a",
38+
Author: &api.CommitUser{
39+
Identity: api.Identity{
40+
Name: "Ethan Koenig",
41+
42+
},
43+
Date: "2017-11-27T04:31:18Z",
44+
},
45+
Committer: &api.CommitUser{
46+
Identity: api.Identity{
47+
Name: "Ethan Koenig",
48+
49+
},
50+
Date: "2017-11-27T04:31:18Z",
51+
},
52+
Message: "Add Home.md\n",
53+
},
54+
},
55+
ContentBase64: base64.RawStdEncoding.EncodeToString(
56+
[]byte("# Home page\n\nThis is the home page!\n"),
57+
),
58+
CommitCount: 1,
59+
Sidebar: "",
60+
Footer: "",
61+
}, page)
62+
}
63+
64+
func TestAPIListWikiPages(t *testing.T) {
65+
defer prepareTestEnv(t)()
66+
67+
username := "user2"
68+
session := loginUser(t, username)
69+
70+
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/pages", username, "repo1")
71+
72+
req := NewRequest(t, "GET", urlStr)
73+
resp := session.MakeRequest(t, req, http.StatusOK)
74+
75+
var meta []*api.WikiPageMetaData
76+
DecodeJSON(t, resp, &meta)
77+
78+
dummymeta := []*api.WikiPageMetaData{
79+
{
80+
Title: "Home",
81+
HTMLURL: meta[0].HTMLURL,
82+
SubURL: "Home",
83+
LastCommit: &api.WikiCommit{
84+
ID: "2c54faec6c45d31c1abfaecdab471eac6633738a",
85+
Author: &api.CommitUser{
86+
Identity: api.Identity{
87+
Name: "Ethan Koenig",
88+
89+
},
90+
Date: "2017-11-27T04:31:18Z",
91+
},
92+
Committer: &api.CommitUser{
93+
Identity: api.Identity{
94+
Name: "Ethan Koenig",
95+
96+
},
97+
Date: "2017-11-27T04:31:18Z",
98+
},
99+
Message: "Add Home.md\n",
100+
},
101+
},
102+
{
103+
Title: "Page With Image",
104+
HTMLURL: meta[1].HTMLURL,
105+
SubURL: "Page-With-Image",
106+
LastCommit: &api.WikiCommit{
107+
ID: "0cf15c3f66ec8384480ed9c3cf87c9e97fbb0ec3",
108+
Author: &api.CommitUser{
109+
Identity: api.Identity{
110+
Name: "Gabriel Silva Simões",
111+
112+
},
113+
Date: "2019-01-25T01:41:55Z",
114+
},
115+
Committer: &api.CommitUser{
116+
Identity: api.Identity{
117+
Name: "Gabriel Silva Simões",
118+
119+
},
120+
Date: "2019-01-25T01:41:55Z",
121+
},
122+
Message: "Add jpeg.jpg and page with image\n",
123+
},
124+
},
125+
{
126+
Title: "Page With Spaced Name",
127+
HTMLURL: meta[2].HTMLURL,
128+
SubURL: "Page-With-Spaced-Name",
129+
LastCommit: &api.WikiCommit{
130+
ID: "c10d10b7e655b3dab1f53176db57c8219a5488d6",
131+
Author: &api.CommitUser{
132+
Identity: api.Identity{
133+
Name: "Gabriel Silva Simões",
134+
135+
},
136+
Date: "2019-01-25T01:39:51Z",
137+
},
138+
Committer: &api.CommitUser{
139+
Identity: api.Identity{
140+
Name: "Gabriel Silva Simões",
141+
142+
},
143+
Date: "2019-01-25T01:39:51Z",
144+
},
145+
Message: "Add page with spaced name\n",
146+
},
147+
},
148+
{
149+
Title: "Unescaped File",
150+
HTMLURL: meta[3].HTMLURL,
151+
SubURL: "Unescaped-File",
152+
LastCommit: &api.WikiCommit{
153+
ID: "0dca5bd9b5d7ef937710e056f575e86c0184ba85",
154+
Author: &api.CommitUser{
155+
Identity: api.Identity{
156+
Name: "6543",
157+
158+
},
159+
Date: "2021-07-19T16:42:46Z",
160+
},
161+
Committer: &api.CommitUser{
162+
Identity: api.Identity{
163+
Name: "6543",
164+
165+
},
166+
Date: "2021-07-19T16:42:46Z",
167+
},
168+
Message: "add unescaped file\n",
169+
},
170+
},
171+
}
172+
173+
assert.Equal(t, dummymeta, meta)
174+
}
175+
176+
func TestAPINewWikiPage(t *testing.T) {
177+
for _, title := range []string{
178+
"New page",
179+
"&&&&",
180+
} {
181+
defer prepareTestEnv(t)()
182+
username := "user2"
183+
session := loginUser(t, username)
184+
token := getTokenForLoggedInUser(t, session)
185+
186+
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/new?token=%s", username, "repo1", token)
187+
188+
req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateWikiPageOptions{
189+
Title: title,
190+
ContentBase64: base64.StdEncoding.EncodeToString([]byte("Wiki page content for API unit tests")),
191+
Message: "",
192+
})
193+
session.MakeRequest(t, req, http.StatusCreated)
194+
}
195+
}
196+
197+
func TestAPIEditWikiPage(t *testing.T) {
198+
defer prepareTestEnv(t)()
199+
username := "user2"
200+
session := loginUser(t, username)
201+
token := getTokenForLoggedInUser(t, session)
202+
203+
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/page/Page-With-Spaced-Name?token=%s", username, "repo1", token)
204+
205+
req := NewRequestWithJSON(t, "PATCH", urlStr, &api.CreateWikiPageOptions{
206+
Title: "edited title",
207+
ContentBase64: base64.StdEncoding.EncodeToString([]byte("Edited wiki page content for API unit tests")),
208+
Message: "",
209+
})
210+
session.MakeRequest(t, req, http.StatusOK)
211+
}
212+
213+
func TestAPIListPageRevisions(t *testing.T) {
214+
defer prepareTestEnv(t)()
215+
username := "user2"
216+
session := loginUser(t, username)
217+
218+
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/revisions/Home", username, "repo1")
219+
220+
req := NewRequest(t, "GET", urlStr)
221+
resp := session.MakeRequest(t, req, http.StatusOK)
222+
223+
var revisions *api.WikiCommitList
224+
DecodeJSON(t, resp, &revisions)
225+
226+
dummyrevisions := &api.WikiCommitList{
227+
WikiCommits: []*api.WikiCommit{
228+
{
229+
ID: "2c54faec6c45d31c1abfaecdab471eac6633738a",
230+
Author: &api.CommitUser{
231+
Identity: api.Identity{
232+
Name: "Ethan Koenig",
233+
234+
},
235+
Date: "2017-11-27T04:31:18Z",
236+
},
237+
Committer: &api.CommitUser{
238+
Identity: api.Identity{
239+
Name: "Ethan Koenig",
240+
241+
},
242+
Date: "2017-11-27T04:31:18Z",
243+
},
244+
Message: "Add Home.md\n",
245+
},
246+
},
247+
Count: 1,
248+
}
249+
250+
assert.Equal(t, dummyrevisions, revisions)
251+
}

modules/convert/wiki.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2021 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package convert
6+
7+
import (
8+
"time"
9+
10+
"code.gitea.io/gitea/models"
11+
"code.gitea.io/gitea/modules/git"
12+
api "code.gitea.io/gitea/modules/structs"
13+
"code.gitea.io/gitea/modules/util"
14+
wiki_service "code.gitea.io/gitea/services/wiki"
15+
)
16+
17+
// ToWikiCommit convert a git commit into a WikiCommit
18+
func ToWikiCommit(commit *git.Commit) *api.WikiCommit {
19+
return &api.WikiCommit{
20+
ID: commit.ID.String(),
21+
Author: &api.CommitUser{
22+
Identity: api.Identity{
23+
Name: commit.Author.Name,
24+
Email: commit.Author.Email,
25+
},
26+
Date: commit.Author.When.UTC().Format(time.RFC3339),
27+
},
28+
Committer: &api.CommitUser{
29+
Identity: api.Identity{
30+
Name: commit.Committer.Name,
31+
Email: commit.Committer.Email,
32+
},
33+
Date: commit.Committer.When.UTC().Format(time.RFC3339),
34+
},
35+
Message: commit.CommitMessage,
36+
}
37+
}
38+
39+
// ToWikiCommitList convert a list of git commits into a WikiCommitList
40+
func ToWikiCommitList(commits []*git.Commit, total int64) *api.WikiCommitList {
41+
result := make([]*api.WikiCommit, len(commits))
42+
for i := range commits {
43+
result[i] = ToWikiCommit(commits[i])
44+
}
45+
return &api.WikiCommitList{
46+
WikiCommits: result,
47+
Count: total,
48+
}
49+
}
50+
51+
// ToWikiPageMetaData converts meta information to a WikiPageMetaData
52+
func ToWikiPageMetaData(title string, lastCommit *git.Commit, repo *models.Repository) *api.WikiPageMetaData {
53+
suburl := wiki_service.NameToSubURL(title)
54+
return &api.WikiPageMetaData{
55+
Title: title,
56+
HTMLURL: util.URLJoin(repo.HTMLURL(), "wiki", suburl),
57+
SubURL: suburl,
58+
LastCommit: ToWikiCommit(lastCommit),
59+
}
60+
}

modules/structs/repo_wiki.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2021 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package structs
6+
7+
// WikiCommit page commit/revision
8+
type WikiCommit struct {
9+
ID string `json:"sha"`
10+
Author *CommitUser `json:"author"`
11+
Committer *CommitUser `json:"commiter"`
12+
Message string `json:"message"`
13+
}
14+
15+
// WikiPage a wiki page
16+
type WikiPage struct {
17+
*WikiPageMetaData
18+
// Page content, base64 encoded
19+
ContentBase64 string `json:"content_base64"`
20+
CommitCount int64 `json:"commit_count"`
21+
Sidebar string `json:"sidebar"`
22+
Footer string `json:"footer"`
23+
}
24+
25+
// WikiPageMetaData wiki page meta information
26+
type WikiPageMetaData struct {
27+
Title string `json:"title"`
28+
HTMLURL string `json:"html_url"`
29+
SubURL string `json:"sub_url"`
30+
LastCommit *WikiCommit `json:"last_commit"`
31+
}
32+
33+
// CreateWikiPageOptions form for creating wiki
34+
type CreateWikiPageOptions struct {
35+
// page title. leave empty to keep unchanged
36+
Title string `json:"title"`
37+
// content must be base64 encoded
38+
ContentBase64 string `json:"content_base64"`
39+
// optional commit message summarizing the change
40+
Message string `json:"message"`
41+
}
42+
43+
// WikiCommitList commit/revision list
44+
type WikiCommitList struct {
45+
WikiCommits []*WikiCommit `json:"commits"`
46+
Count int64 `json:"count"`
47+
}

routers/api/v1/api.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,13 @@ func mustEnableIssuesOrPulls(ctx *context.APIContext) {
521521
}
522522
}
523523

524+
func mustEnableWiki(ctx *context.APIContext) {
525+
if !(ctx.Repo.CanRead(models.UnitTypeWiki)) {
526+
ctx.NotFound()
527+
return
528+
}
529+
}
530+
524531
func mustNotBeArchived(ctx *context.APIContext) {
525532
if ctx.Repo.Repository.IsArchived {
526533
ctx.NotFound()
@@ -791,6 +798,15 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route {
791798
m.Combo("").Get(repo.ListTrackedTimesByRepository)
792799
m.Combo("/{timetrackingusername}").Get(repo.ListTrackedTimesByUser)
793800
}, mustEnableIssues, reqToken())
801+
m.Group("/wiki", func() {
802+
m.Combo("/page/{pageName}").
803+
Get(repo.GetWikiPage).
804+
Patch(mustNotBeArchived, reqRepoWriter(models.UnitTypeWiki), bind(api.CreateWikiPageOptions{}), repo.EditWikiPage).
805+
Delete(mustNotBeArchived, reqRepoWriter(models.UnitTypeWiki), repo.DeleteWikiPage)
806+
m.Get("/revisions/{pageName}", repo.ListPageRevisions)
807+
m.Post("/new", mustNotBeArchived, reqRepoWriter(models.UnitTypeWiki), bind(api.CreateWikiPageOptions{}), repo.NewWikiPage)
808+
m.Get("/pages", repo.ListWikiPages)
809+
}, mustEnableWiki)
794810
m.Group("/issues", func() {
795811
m.Combo("").Get(repo.ListIssues).
796812
Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue)

0 commit comments

Comments
 (0)