From 8d47d6cdb9f7d0822ebf72fdebb63e68cb01be89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=98=99=E2=97=A6=20The=20Tablet=20=E2=9D=80=20GamerGirla?= =?UTF-8?q?ndCo=20=E2=97=A6=E2=9D=A7?= Date: Sat, 20 Dec 2025 15:23:24 -0500 Subject: [PATCH] feat: add endpoint to retrieve root-level groups in org --- routers/api/v1/api.go | 1 + routers/api/v1/org/group.go | 78 +++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 routers/api/v1/org/group.go diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 7a38791a11..fbe299a91c 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1734,6 +1734,7 @@ func Routes() *web.Router { }) }, reqToken(), reqOrgOwnership()) m.Group("/groups", func() { + m.Get("", org.GetOrgGroups) m.Post("/new", reqToken(), reqGroupMembership(perm.AccessModeWrite, true), group.NewGroup) }) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true), checkTokenPublicOnly()) diff --git a/routers/api/v1/org/group.go b/routers/api/v1/org/group.go new file mode 100644 index 0000000000..5173fe752a --- /dev/null +++ b/routers/api/v1/org/group.go @@ -0,0 +1,78 @@ +package org + +import ( + "net/http" + + group_model "code.gitea.io/gitea/models/group" + "code.gitea.io/gitea/models/organization" + "code.gitea.io/gitea/models/perm" + "code.gitea.io/gitea/models/unit" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/convert" +) + +func GetOrgGroups(ctx *context.APIContext) { + // swagger:operation GET /orgs/{org}/groups organization orgListGroups + // --- + // summary: List an organization's root-level groups + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of the organization + // type: string + // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results + // type: integer + // responses: + // "200": + // "$ref": "#/responses/GroupList" + // "404": + // "$ref": "#/responses/notFound" + var doerID int64 + if ctx.Doer != nil { + doerID = ctx.Doer.ID + } + org, err := organization.GetOrgByName(ctx, ctx.PathParam("org")) + if err != nil { + if organization.IsErrOrgNotExist(err) { + ctx.APIError(http.StatusUnprocessableEntity, err) + } else { + ctx.APIErrorInternal(err) + } + return + } + + if !organization.HasOrgOrUserVisible(ctx, org.AsUser(), ctx.Doer) { + ctx.APIErrorNotFound("HasOrgOrUserVisible", nil) + return + } + groups, err := group_model.FindGroupsByCond(ctx, &group_model.FindGroupsOptions{ + ParentGroupID: 0, + ActorID: doerID, + OwnerID: org.ID, + }, group_model. + AccessibleGroupCondition(ctx.Doer, unit.TypeInvalid, perm.AccessModeRead)) + if err != nil { + ctx.APIErrorInternal(err) + return + } + apiGroups := make([]*api.Group, len(groups)) + for i, group := range groups { + apiGroups[i], err = convert.ToAPIGroup(ctx, group, ctx.Doer) + if err != nil { + ctx.APIErrorInternal(err) + return + } + } + ctx.SetTotalCountHeader(int64(len(groups))) + ctx.JSON(http.StatusOK, apiGroups) +}