Skip to content

Commit a9ba713

Browse files
lunnyAbdulrhmnGhanem
authored andcommitted
Never use /api/v1 from Gitea UI Pages (go-gitea#19318)
Reusing `/api/v1` from Gitea UI Pages have pros and cons. Pros: 1) Less code copy Cons: 1) API/v1 have to support shared session with page requests. 2) You need to consider for each other when you want to change something about api/v1 or page. This PR moves all dependencies to API/v1 from UI Pages. Partially replace go-gitea#16052
1 parent 0ccf8d9 commit a9ba713

32 files changed

+1082
-74
lines changed

integrations/issue_test.go

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package integrations
77
import (
88
"fmt"
99
"net/http"
10+
"net/url"
1011
"path"
1112
"strconv"
1213
"strings"
@@ -20,6 +21,7 @@ import (
2021
"code.gitea.io/gitea/modules/indexer/issues"
2122
"code.gitea.io/gitea/modules/references"
2223
"code.gitea.io/gitea/modules/setting"
24+
api "code.gitea.io/gitea/modules/structs"
2325
"code.gitea.io/gitea/modules/test"
2426

2527
"github.com/PuerkitoBio/goquery"
@@ -347,3 +349,209 @@ func TestIssueRedirect(t *testing.T) {
347349
resp = session.MakeRequest(t, req, http.StatusSeeOther)
348350
assert.Equal(t, "/"+path.Join("org26", "repo_external_tracker_alpha", "pulls", "1"), test.RedirectURL(resp))
349351
}
352+
353+
func TestSearchIssues(t *testing.T) {
354+
defer prepareTestEnv(t)()
355+
356+
session := loginUser(t, "user2")
357+
358+
link, _ := url.Parse("/issues/search")
359+
req := NewRequest(t, "GET", link.String())
360+
resp := session.MakeRequest(t, req, http.StatusOK)
361+
var apiIssues []*api.Issue
362+
DecodeJSON(t, resp, &apiIssues)
363+
assert.Len(t, apiIssues, 10)
364+
365+
req = NewRequest(t, "GET", link.String())
366+
resp = session.MakeRequest(t, req, http.StatusOK)
367+
DecodeJSON(t, resp, &apiIssues)
368+
assert.Len(t, apiIssues, 10)
369+
370+
since := "2000-01-01T00%3A50%3A01%2B00%3A00" // 946687801
371+
before := time.Unix(999307200, 0).Format(time.RFC3339)
372+
query := url.Values{}
373+
query.Add("since", since)
374+
query.Add("before", before)
375+
link.RawQuery = query.Encode()
376+
req = NewRequest(t, "GET", link.String())
377+
resp = session.MakeRequest(t, req, http.StatusOK)
378+
DecodeJSON(t, resp, &apiIssues)
379+
assert.Len(t, apiIssues, 8)
380+
query.Del("since")
381+
query.Del("before")
382+
383+
query.Add("state", "closed")
384+
link.RawQuery = query.Encode()
385+
req = NewRequest(t, "GET", link.String())
386+
resp = session.MakeRequest(t, req, http.StatusOK)
387+
DecodeJSON(t, resp, &apiIssues)
388+
assert.Len(t, apiIssues, 2)
389+
390+
query.Set("state", "all")
391+
link.RawQuery = query.Encode()
392+
req = NewRequest(t, "GET", link.String())
393+
resp = session.MakeRequest(t, req, http.StatusOK)
394+
DecodeJSON(t, resp, &apiIssues)
395+
assert.EqualValues(t, "15", resp.Header().Get("X-Total-Count"))
396+
assert.Len(t, apiIssues, 10) // there are more but 10 is page item limit
397+
398+
query.Add("limit", "20")
399+
link.RawQuery = query.Encode()
400+
req = NewRequest(t, "GET", link.String())
401+
resp = session.MakeRequest(t, req, http.StatusOK)
402+
DecodeJSON(t, resp, &apiIssues)
403+
assert.Len(t, apiIssues, 15)
404+
405+
query = url.Values{"assigned": {"true"}, "state": {"all"}}
406+
link.RawQuery = query.Encode()
407+
req = NewRequest(t, "GET", link.String())
408+
resp = session.MakeRequest(t, req, http.StatusOK)
409+
DecodeJSON(t, resp, &apiIssues)
410+
assert.Len(t, apiIssues, 1)
411+
412+
query = url.Values{"milestones": {"milestone1"}, "state": {"all"}}
413+
link.RawQuery = query.Encode()
414+
req = NewRequest(t, "GET", link.String())
415+
resp = session.MakeRequest(t, req, http.StatusOK)
416+
DecodeJSON(t, resp, &apiIssues)
417+
assert.Len(t, apiIssues, 1)
418+
419+
query = url.Values{"milestones": {"milestone1,milestone3"}, "state": {"all"}}
420+
link.RawQuery = query.Encode()
421+
req = NewRequest(t, "GET", link.String())
422+
resp = session.MakeRequest(t, req, http.StatusOK)
423+
DecodeJSON(t, resp, &apiIssues)
424+
assert.Len(t, apiIssues, 2)
425+
426+
query = url.Values{"owner": {"user2"}} // user
427+
link.RawQuery = query.Encode()
428+
req = NewRequest(t, "GET", link.String())
429+
resp = session.MakeRequest(t, req, http.StatusOK)
430+
DecodeJSON(t, resp, &apiIssues)
431+
assert.Len(t, apiIssues, 6)
432+
433+
query = url.Values{"owner": {"user3"}} // organization
434+
link.RawQuery = query.Encode()
435+
req = NewRequest(t, "GET", link.String())
436+
resp = session.MakeRequest(t, req, http.StatusOK)
437+
DecodeJSON(t, resp, &apiIssues)
438+
assert.Len(t, apiIssues, 3)
439+
440+
query = url.Values{"owner": {"user3"}, "team": {"team1"}} // organization + team
441+
link.RawQuery = query.Encode()
442+
req = NewRequest(t, "GET", link.String())
443+
resp = session.MakeRequest(t, req, http.StatusOK)
444+
DecodeJSON(t, resp, &apiIssues)
445+
assert.Len(t, apiIssues, 2)
446+
}
447+
448+
func TestSearchIssuesWithLabels(t *testing.T) {
449+
defer prepareTestEnv(t)()
450+
451+
session := loginUser(t, "user1")
452+
453+
link, _ := url.Parse("/api/v1/repos/issues/search")
454+
req := NewRequest(t, "GET", link.String())
455+
resp := session.MakeRequest(t, req, http.StatusOK)
456+
var apiIssues []*api.Issue
457+
DecodeJSON(t, resp, &apiIssues)
458+
459+
assert.Len(t, apiIssues, 10)
460+
461+
query := url.Values{}
462+
link.RawQuery = query.Encode()
463+
req = NewRequest(t, "GET", link.String())
464+
resp = session.MakeRequest(t, req, http.StatusOK)
465+
DecodeJSON(t, resp, &apiIssues)
466+
assert.Len(t, apiIssues, 10)
467+
468+
query.Add("labels", "label1")
469+
link.RawQuery = query.Encode()
470+
req = NewRequest(t, "GET", link.String())
471+
resp = session.MakeRequest(t, req, http.StatusOK)
472+
DecodeJSON(t, resp, &apiIssues)
473+
assert.Len(t, apiIssues, 2)
474+
475+
// multiple labels
476+
query.Set("labels", "label1,label2")
477+
link.RawQuery = query.Encode()
478+
req = NewRequest(t, "GET", link.String())
479+
resp = session.MakeRequest(t, req, http.StatusOK)
480+
DecodeJSON(t, resp, &apiIssues)
481+
assert.Len(t, apiIssues, 2)
482+
483+
// an org label
484+
query.Set("labels", "orglabel4")
485+
link.RawQuery = query.Encode()
486+
req = NewRequest(t, "GET", link.String())
487+
resp = session.MakeRequest(t, req, http.StatusOK)
488+
DecodeJSON(t, resp, &apiIssues)
489+
assert.Len(t, apiIssues, 1)
490+
491+
// org and repo label
492+
query.Set("labels", "label2,orglabel4")
493+
query.Add("state", "all")
494+
link.RawQuery = query.Encode()
495+
req = NewRequest(t, "GET", link.String())
496+
resp = session.MakeRequest(t, req, http.StatusOK)
497+
DecodeJSON(t, resp, &apiIssues)
498+
assert.Len(t, apiIssues, 2)
499+
500+
// org and repo label which share the same issue
501+
query.Set("labels", "label1,orglabel4")
502+
link.RawQuery = query.Encode()
503+
req = NewRequest(t, "GET", link.String())
504+
resp = session.MakeRequest(t, req, http.StatusOK)
505+
DecodeJSON(t, resp, &apiIssues)
506+
assert.Len(t, apiIssues, 2)
507+
}
508+
509+
func TestGetIssueInfo(t *testing.T) {
510+
defer prepareTestEnv(t)()
511+
512+
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue)
513+
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository)
514+
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
515+
assert.NoError(t, issue.LoadAttributes())
516+
assert.Equal(t, int64(1019307200), int64(issue.DeadlineUnix))
517+
assert.Equal(t, api.StateOpen, issue.State())
518+
519+
session := loginUser(t, owner.Name)
520+
521+
urlStr := fmt.Sprintf("/%s/%s/issues/%d/info", owner.Name, repo.Name, issue.Index)
522+
req := NewRequest(t, "GET", urlStr)
523+
resp := session.MakeRequest(t, req, http.StatusOK)
524+
var apiIssue api.Issue
525+
DecodeJSON(t, resp, &apiIssue)
526+
527+
assert.EqualValues(t, issue.ID, apiIssue.ID)
528+
}
529+
530+
func TestUpdateIssueDeadline(t *testing.T) {
531+
defer prepareTestEnv(t)()
532+
533+
issueBefore := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue)
534+
repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}).(*repo_model.Repository)
535+
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}).(*user_model.User)
536+
assert.NoError(t, issueBefore.LoadAttributes())
537+
assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix))
538+
assert.Equal(t, api.StateOpen, issueBefore.State())
539+
540+
session := loginUser(t, owner.Name)
541+
542+
issueURL := fmt.Sprintf("%s/%s/issues/%d", owner.Name, repoBefore.Name, issueBefore.Index)
543+
req := NewRequest(t, "GET", issueURL)
544+
resp := session.MakeRequest(t, req, http.StatusOK)
545+
htmlDoc := NewHTMLParser(t, resp.Body)
546+
547+
urlStr := issueURL + "/deadline?_csrf=" + htmlDoc.GetCSRF()
548+
req = NewRequestWithJSON(t, "POST", urlStr, map[string]string{
549+
"due_date": "2022-04-06T00:00:00.000Z",
550+
})
551+
552+
resp = session.MakeRequest(t, req, http.StatusCreated)
553+
var apiIssue api.IssueDeadline
554+
DecodeJSON(t, resp, &apiIssue)
555+
556+
assert.EqualValues(t, "2022-04-06", apiIssue.Deadline.Format("2006-01-02"))
557+
}

integrations/org_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"strings"
1111
"testing"
1212

13+
"code.gitea.io/gitea/models/unittest"
14+
user_model "code.gitea.io/gitea/models/user"
1315
api "code.gitea.io/gitea/modules/structs"
1416

1517
"github.com/stretchr/testify/assert"
@@ -173,3 +175,30 @@ func TestOrgRestrictedUser(t *testing.T) {
173175
req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s", orgName, repoName))
174176
restrictedSession.MakeRequest(t, req, http.StatusOK)
175177
}
178+
179+
func TestTeamSearch(t *testing.T) {
180+
defer prepareTestEnv(t)()
181+
182+
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
183+
org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
184+
185+
var results TeamSearchResults
186+
187+
session := loginUser(t, user.Name)
188+
csrf := GetCSRF(t, session, "/"+org.Name)
189+
req := NewRequestf(t, "GET", "/org/%s/teams/-/search?q=%s", org.Name, "_team")
190+
req.Header.Add("X-Csrf-Token", csrf)
191+
resp := session.MakeRequest(t, req, http.StatusOK)
192+
DecodeJSON(t, resp, &results)
193+
assert.NotEmpty(t, results.Data)
194+
assert.Len(t, results.Data, 1)
195+
assert.Equal(t, "test_team", results.Data[0].Name)
196+
197+
// no access if not organization member
198+
user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User)
199+
session = loginUser(t, user5.Name)
200+
csrf = GetCSRF(t, session, "/"+org.Name)
201+
req = NewRequestf(t, "GET", "/org/%s/teams/-/search?q=%s", org.Name, "team")
202+
req.Header.Add("X-Csrf-Token", csrf)
203+
session.MakeRequest(t, req, http.StatusNotFound)
204+
}

integrations/repo_topic_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright 2022 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+
"net/http"
9+
"net/url"
10+
"testing"
11+
12+
api "code.gitea.io/gitea/modules/structs"
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func TestTopicSearch(t *testing.T) {
17+
defer prepareTestEnv(t)()
18+
searchURL, _ := url.Parse("/explore/topics/search")
19+
var topics struct {
20+
TopicNames []*api.TopicResponse `json:"topics"`
21+
}
22+
23+
query := url.Values{"page": []string{"1"}, "limit": []string{"4"}}
24+
25+
searchURL.RawQuery = query.Encode()
26+
res := MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
27+
DecodeJSON(t, res, &topics)
28+
assert.Len(t, topics.TopicNames, 4)
29+
assert.EqualValues(t, "6", res.Header().Get("x-total-count"))
30+
31+
query.Add("q", "topic")
32+
searchURL.RawQuery = query.Encode()
33+
res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
34+
DecodeJSON(t, res, &topics)
35+
assert.Len(t, topics.TopicNames, 2)
36+
37+
query.Set("q", "database")
38+
searchURL.RawQuery = query.Encode()
39+
res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
40+
DecodeJSON(t, res, &topics)
41+
if assert.Len(t, topics.TopicNames, 1) {
42+
assert.EqualValues(t, 2, topics.TopicNames[0].ID)
43+
assert.EqualValues(t, "database", topics.TopicNames[0].Name)
44+
assert.EqualValues(t, 1, topics.TopicNames[0].RepoCount)
45+
}
46+
}

integrations/user_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ import (
88
"net/http"
99
"testing"
1010

11+
"code.gitea.io/gitea/models"
12+
repo_model "code.gitea.io/gitea/models/repo"
1113
"code.gitea.io/gitea/models/unittest"
1214
user_model "code.gitea.io/gitea/models/user"
15+
api "code.gitea.io/gitea/modules/structs"
1316
"code.gitea.io/gitea/modules/test"
1417
"code.gitea.io/gitea/modules/translation/i18n"
1518

@@ -222,3 +225,26 @@ func testExportUserGPGKeys(t *testing.T, user, expected string) {
222225
// t.Log(resp.Body.String())
223226
assert.Equal(t, expected, resp.Body.String())
224227
}
228+
229+
func TestListStopWatches(t *testing.T) {
230+
defer prepareTestEnv(t)()
231+
232+
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
233+
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
234+
235+
session := loginUser(t, owner.Name)
236+
req := NewRequestf(t, "GET", "/user/stopwatches")
237+
resp := session.MakeRequest(t, req, http.StatusOK)
238+
var apiWatches []*api.StopWatch
239+
DecodeJSON(t, resp, &apiWatches)
240+
stopwatch := unittest.AssertExistsAndLoadBean(t, &models.Stopwatch{UserID: owner.ID}).(*models.Stopwatch)
241+
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: stopwatch.IssueID}).(*models.Issue)
242+
if assert.Len(t, apiWatches, 1) {
243+
assert.EqualValues(t, stopwatch.CreatedUnix.AsTime().Unix(), apiWatches[0].Created.Unix())
244+
assert.EqualValues(t, issue.Index, apiWatches[0].IssueIndex)
245+
assert.EqualValues(t, issue.Title, apiWatches[0].IssueTitle)
246+
assert.EqualValues(t, repo.Name, apiWatches[0].RepoName)
247+
assert.EqualValues(t, repo.OwnerName, apiWatches[0].RepoOwnerName)
248+
assert.Greater(t, int64(apiWatches[0].Seconds), int64(0))
249+
}
250+
}

modules/context/api.go

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -191,22 +191,6 @@ func (ctx *APIContext) SetLinkHeader(total, pageSize int) {
191191
}
192192
}
193193

194-
// SetTotalCountHeader set "X-Total-Count" header
195-
func (ctx *APIContext) SetTotalCountHeader(total int64) {
196-
ctx.RespHeader().Set("X-Total-Count", fmt.Sprint(total))
197-
ctx.AppendAccessControlExposeHeaders("X-Total-Count")
198-
}
199-
200-
// AppendAccessControlExposeHeaders append headers by name to "Access-Control-Expose-Headers" header
201-
func (ctx *APIContext) AppendAccessControlExposeHeaders(names ...string) {
202-
val := ctx.RespHeader().Get("Access-Control-Expose-Headers")
203-
if len(val) != 0 {
204-
ctx.RespHeader().Set("Access-Control-Expose-Headers", fmt.Sprintf("%s, %s", val, strings.Join(names, ", ")))
205-
} else {
206-
ctx.RespHeader().Set("Access-Control-Expose-Headers", strings.Join(names, ", "))
207-
}
208-
}
209-
210194
// RequireCSRF requires a validated a CSRF token
211195
func (ctx *APIContext) RequireCSRF() {
212196
headerToken := ctx.Req.Header.Get(ctx.csrf.GetHeaderName())

0 commit comments

Comments
 (0)