Skip to content

Commit 6b0df6d

Browse files
authored
Add activity feeds API (#23494)
Close #5666 Add APIs for getting activity feeds.
1 parent d149093 commit 6b0df6d

File tree

10 files changed

+665
-0
lines changed

10 files changed

+665
-0
lines changed

models/activities/action.go

+61
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,67 @@ const (
6666
ActionAutoMergePullRequest // 27
6767
)
6868

69+
func (at ActionType) String() string {
70+
switch at {
71+
case ActionCreateRepo:
72+
return "create_repo"
73+
case ActionRenameRepo:
74+
return "rename_repo"
75+
case ActionStarRepo:
76+
return "star_repo"
77+
case ActionWatchRepo:
78+
return "watch_repo"
79+
case ActionCommitRepo:
80+
return "commit_repo"
81+
case ActionCreateIssue:
82+
return "create_issue"
83+
case ActionCreatePullRequest:
84+
return "create_pull_request"
85+
case ActionTransferRepo:
86+
return "transfer_repo"
87+
case ActionPushTag:
88+
return "push_tag"
89+
case ActionCommentIssue:
90+
return "comment_issue"
91+
case ActionMergePullRequest:
92+
return "merge_pull_request"
93+
case ActionCloseIssue:
94+
return "close_issue"
95+
case ActionReopenIssue:
96+
return "reopen_issue"
97+
case ActionClosePullRequest:
98+
return "close_pull_request"
99+
case ActionReopenPullRequest:
100+
return "reopen_pull_request"
101+
case ActionDeleteTag:
102+
return "delete_tag"
103+
case ActionDeleteBranch:
104+
return "delete_branch"
105+
case ActionMirrorSyncPush:
106+
return "mirror_sync_push"
107+
case ActionMirrorSyncCreate:
108+
return "mirror_sync_create"
109+
case ActionMirrorSyncDelete:
110+
return "mirror_sync_delete"
111+
case ActionApprovePullRequest:
112+
return "approve_pull_request"
113+
case ActionRejectPullRequest:
114+
return "reject_pull_request"
115+
case ActionCommentPull:
116+
return "comment_pull"
117+
case ActionPublishRelease:
118+
return "publish_release"
119+
case ActionPullReviewDismissed:
120+
return "pull_review_dismissed"
121+
case ActionPullRequestReadyForReview:
122+
return "pull_request_ready_for_review"
123+
case ActionAutoMergePullRequest:
124+
return "auto_merge_pull_request"
125+
default:
126+
return "action-" + strconv.Itoa(int(at))
127+
}
128+
}
129+
69130
// Action represents user operation type and other information to
70131
// repository. It implemented interface base.Actioner so that can be
71132
// used in template render.

modules/structs/activity.go

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package structs
5+
6+
import "time"
7+
8+
type Activity struct {
9+
ID int64 `json:"id"`
10+
UserID int64 `json:"user_id"` // Receiver user
11+
OpType string `json:"op_type"`
12+
ActUserID int64 `json:"act_user_id"`
13+
ActUser *User `json:"act_user"`
14+
RepoID int64 `json:"repo_id"`
15+
Repo *Repository `json:"repo"`
16+
CommentID int64 `json:"comment_id"`
17+
Comment *Comment `json:"comment"`
18+
RefName string `json:"ref_name"`
19+
IsPrivate bool `json:"is_private"`
20+
Content string `json:"content"`
21+
Created time.Time `json:"created"`
22+
}

routers/api/v1/api.go

+5
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,8 @@ func Routes(ctx gocontext.Context) *web.Route {
754754
Post(bind(api.CreateAccessTokenOption{}), user.CreateAccessToken)
755755
m.Combo("/{id}").Delete(user.DeleteAccessToken)
756756
}, reqBasicAuth())
757+
758+
m.Get("/activities/feeds", user.ListUserActivityFeeds)
757759
}, context_service.UserAssignmentAPI())
758760
})
759761

@@ -1177,6 +1179,7 @@ func Routes(ctx gocontext.Context) *web.Route {
11771179
m.Get("/issue_config", context.ReferencesGitRepo(), repo.GetIssueConfig)
11781180
m.Get("/issue_config/validate", context.ReferencesGitRepo(), repo.ValidateIssueConfig)
11791181
m.Get("/languages", reqRepoReader(unit.TypeCode), repo.GetLanguages)
1182+
m.Get("/activities/feeds", repo.ListRepoActivityFeeds)
11801183
}, repoAssignment())
11811184
})
11821185

@@ -1234,6 +1237,7 @@ func Routes(ctx gocontext.Context) *web.Route {
12341237
Patch(bind(api.EditHookOption{}), org.EditHook).
12351238
Delete(org.DeleteHook)
12361239
}, reqToken(auth_model.AccessTokenScopeAdminOrgHook), reqOrgOwnership(), reqWebhooksEnabled())
1240+
m.Get("/activities/feeds", org.ListOrgActivityFeeds)
12371241
}, orgAssignment(true))
12381242
m.Group("/teams/{teamid}", func() {
12391243
m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeam).
@@ -1253,6 +1257,7 @@ func Routes(ctx gocontext.Context) *web.Route {
12531257
Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), org.RemoveTeamRepository).
12541258
Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamRepo)
12551259
})
1260+
m.Get("/activities/feeds", org.ListTeamActivityFeeds)
12561261
}, orgAssignment(false, true), reqToken(""), reqTeamMembership())
12571262

12581263
m.Group("/admin", func() {

routers/api/v1/org/org.go

+67
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package org
77
import (
88
"net/http"
99

10+
activities_model "code.gitea.io/gitea/models/activities"
1011
"code.gitea.io/gitea/models/db"
1112
"code.gitea.io/gitea/models/organization"
1213
"code.gitea.io/gitea/models/perm"
@@ -370,3 +371,69 @@ func Delete(ctx *context.APIContext) {
370371
}
371372
ctx.Status(http.StatusNoContent)
372373
}
374+
375+
func ListOrgActivityFeeds(ctx *context.APIContext) {
376+
// swagger:operation GET /orgs/{org}/activities/feeds organization orgListActivityFeeds
377+
// ---
378+
// summary: List an organization's activity feeds
379+
// produces:
380+
// - application/json
381+
// parameters:
382+
// - name: org
383+
// in: path
384+
// description: name of the org
385+
// type: string
386+
// required: true
387+
// - name: date
388+
// in: query
389+
// description: the date of the activities to be found
390+
// type: string
391+
// format: date
392+
// - name: page
393+
// in: query
394+
// description: page number of results to return (1-based)
395+
// type: integer
396+
// - name: limit
397+
// in: query
398+
// description: page size of results
399+
// type: integer
400+
// responses:
401+
// "200":
402+
// "$ref": "#/responses/ActivityFeedsList"
403+
// "404":
404+
// "$ref": "#/responses/notFound"
405+
406+
includePrivate := false
407+
if ctx.IsSigned {
408+
if ctx.Doer.IsAdmin {
409+
includePrivate = true
410+
} else {
411+
org := organization.OrgFromUser(ctx.ContextUser)
412+
isMember, err := org.IsOrgMember(ctx.Doer.ID)
413+
if err != nil {
414+
ctx.Error(http.StatusInternalServerError, "IsOrgMember", err)
415+
return
416+
}
417+
includePrivate = isMember
418+
}
419+
}
420+
421+
listOptions := utils.GetListOptions(ctx)
422+
423+
opts := activities_model.GetFeedsOptions{
424+
RequestedUser: ctx.ContextUser,
425+
Actor: ctx.Doer,
426+
IncludePrivate: includePrivate,
427+
Date: ctx.FormString("date"),
428+
ListOptions: listOptions,
429+
}
430+
431+
feeds, count, err := activities_model.GetFeeds(ctx, opts)
432+
if err != nil {
433+
ctx.Error(http.StatusInternalServerError, "GetFeeds", err)
434+
return
435+
}
436+
ctx.SetTotalCountHeader(count)
437+
438+
ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer))
439+
}

routers/api/v1/org/team.go

+53
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net/http"
1010

1111
"code.gitea.io/gitea/models"
12+
activities_model "code.gitea.io/gitea/models/activities"
1213
"code.gitea.io/gitea/models/organization"
1314
"code.gitea.io/gitea/models/perm"
1415
access_model "code.gitea.io/gitea/models/perm/access"
@@ -792,3 +793,55 @@ func SearchTeam(ctx *context.APIContext) {
792793
"data": apiTeams,
793794
})
794795
}
796+
797+
func ListTeamActivityFeeds(ctx *context.APIContext) {
798+
// swagger:operation GET /teams/{id}/activities/feeds organization orgListTeamActivityFeeds
799+
// ---
800+
// summary: List a team's activity feeds
801+
// produces:
802+
// - application/json
803+
// parameters:
804+
// - name: id
805+
// in: path
806+
// description: id of the team
807+
// type: integer
808+
// format: int64
809+
// required: true
810+
// - name: date
811+
// in: query
812+
// description: the date of the activities to be found
813+
// type: string
814+
// format: date
815+
// - name: page
816+
// in: query
817+
// description: page number of results to return (1-based)
818+
// type: integer
819+
// - name: limit
820+
// in: query
821+
// description: page size of results
822+
// type: integer
823+
// responses:
824+
// "200":
825+
// "$ref": "#/responses/ActivityFeedsList"
826+
// "404":
827+
// "$ref": "#/responses/notFound"
828+
829+
listOptions := utils.GetListOptions(ctx)
830+
831+
opts := activities_model.GetFeedsOptions{
832+
RequestedTeam: ctx.Org.Team,
833+
Actor: ctx.Doer,
834+
IncludePrivate: true,
835+
Date: ctx.FormString("date"),
836+
ListOptions: listOptions,
837+
}
838+
839+
feeds, count, err := activities_model.GetFeeds(ctx, opts)
840+
if err != nil {
841+
ctx.Error(http.StatusInternalServerError, "GetFeeds", err)
842+
return
843+
}
844+
ctx.SetTotalCountHeader(count)
845+
846+
ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer))
847+
}

routers/api/v1/repo/repo.go

+57
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111
"time"
1212

13+
activities_model "code.gitea.io/gitea/models/activities"
1314
"code.gitea.io/gitea/models/db"
1415
"code.gitea.io/gitea/models/organization"
1516
"code.gitea.io/gitea/models/perm"
@@ -1199,3 +1200,59 @@ func ValidateIssueConfig(ctx *context.APIContext) {
11991200
ctx.JSON(http.StatusOK, api.IssueConfigValidation{Valid: false, Message: err.Error()})
12001201
}
12011202
}
1203+
1204+
func ListRepoActivityFeeds(ctx *context.APIContext) {
1205+
// swagger:operation GET /repos/{owner}/{repo}/activities/feeds repository repoListActivityFeeds
1206+
// ---
1207+
// summary: List a repository's activity feeds
1208+
// produces:
1209+
// - application/json
1210+
// parameters:
1211+
// - name: owner
1212+
// in: path
1213+
// description: owner of the repo
1214+
// type: string
1215+
// required: true
1216+
// - name: repo
1217+
// in: path
1218+
// description: name of the repo
1219+
// type: string
1220+
// required: true
1221+
// - name: date
1222+
// in: query
1223+
// description: the date of the activities to be found
1224+
// type: string
1225+
// format: date
1226+
// - name: page
1227+
// in: query
1228+
// description: page number of results to return (1-based)
1229+
// type: integer
1230+
// - name: limit
1231+
// in: query
1232+
// description: page size of results
1233+
// type: integer
1234+
// responses:
1235+
// "200":
1236+
// "$ref": "#/responses/ActivityFeedsList"
1237+
// "404":
1238+
// "$ref": "#/responses/notFound"
1239+
1240+
listOptions := utils.GetListOptions(ctx)
1241+
1242+
opts := activities_model.GetFeedsOptions{
1243+
RequestedRepo: ctx.Repo.Repository,
1244+
Actor: ctx.Doer,
1245+
IncludePrivate: true,
1246+
Date: ctx.FormString("date"),
1247+
ListOptions: listOptions,
1248+
}
1249+
1250+
feeds, count, err := activities_model.GetFeeds(ctx, opts)
1251+
if err != nil {
1252+
ctx.Error(http.StatusInternalServerError, "GetFeeds", err)
1253+
return
1254+
}
1255+
ctx.SetTotalCountHeader(count)
1256+
1257+
ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer))
1258+
}

routers/api/v1/swagger/activity.go

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package swagger
5+
6+
import (
7+
api "code.gitea.io/gitea/modules/structs"
8+
)
9+
10+
// ActivityFeedsList
11+
// swagger:response ActivityFeedsList
12+
type swaggerActivityFeedsList struct {
13+
// in:body
14+
Body []api.Activity `json:"body"`
15+
}

0 commit comments

Comments
 (0)