From 42c54d0396719b4128e7c112cf528d01178c2e07 Mon Sep 17 00:00:00 2001 From: eyad-hussein Date: Thu, 1 Aug 2024 16:02:36 +0300 Subject: [PATCH] feat: add pagination support and update swagger documentation --- modules/structs/project_column.go | 12 +-- routers/api/v1/api.go | 2 +- routers/api/v1/org/project.go | 29 +++++-- routers/api/v1/project/project.go | 8 +- routers/api/v1/project/project_column.go | 16 ++-- routers/api/v1/repo/project.go | 31 +++++-- routers/api/v1/user/project.go | 29 +++++-- templates/swagger/v1_json.tmpl | 106 ++++++++++++++++------- 8 files changed, 157 insertions(+), 76 deletions(-) diff --git a/modules/structs/project_column.go b/modules/structs/project_column.go index b4e5d18144..b57d23bd91 100644 --- a/modules/structs/project_column.go +++ b/modules/structs/project_column.go @@ -12,15 +12,15 @@ type Column struct { // EditProjectColumnOption options for editing a project column type EditProjectColumnOption struct { - Title string `binding:"MaxSize(100)"` - Sorting int8 - Color string `binding:"MaxSize(7)"` + Title string `json:"title" binding:"MaxSize(100)"` + Sorting int8 `json:"sorting"` + Color string `json:"color" binding:"MaxSize(7)"` } // CreateProjectColumnOption options for creating a project column type CreateProjectColumnOption struct { // required:true - Title string `binding:"Required;MaxSize(100)"` - Sorting int8 - Color string `binding:"MaxSize(7)"` + Title string `json:"title" binding:"Required;MaxSize(100)"` + Sorting int8 `json:"sorting"` + Color string `json:"color" binding:"MaxSize(7)"` } diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index b289ce670e..1db3e18f7c 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1158,7 +1158,7 @@ func Routes() *web.Router { m.Group("", func() { m.Patch("", bind(api.EditProjectColumnOption{}), project.EditProjectColumn) m.Delete("", project.DeleteProjectColumn) - m.Post("/default", project.SetDefaultProjectColumn) + m.Put("/default", project.SetDefaultProjectColumn) }, reqRepoWriter(unit.TypeProjects), mustNotBeArchived, reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite, true), reqProjectOwner()) }) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization, auth_model.AccessTokenScopeCategoryRepository), reqToken(), projectIDAssignmentAPI(), columnAssignment(), individualPermsChecker, reqRepoReader(unit.TypeProjects), mustEnableRepoProjects, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true)) diff --git a/routers/api/v1/org/project.go b/routers/api/v1/org/project.go index fbd8e05f96..1dbf429bc6 100644 --- a/routers/api/v1/org/project.go +++ b/routers/api/v1/org/project.go @@ -12,13 +12,14 @@ import ( "code.gitea.io/gitea/modules/optional" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) // CreateProject creates a new project for organization func CreateProject(ctx *context.APIContext) { - // swagger:operation POST /orgs/{org}/projects project createProject + // swagger:operation POST /orgs/{org}/projects project orgCreateProject // --- // summary: Create a new project // consumes: @@ -71,7 +72,7 @@ func CreateProject(ctx *context.APIContext) { // GetProjects returns a list of projects that belong to an organization func GetProjects(ctx *context.APIContext) { - // swagger:operation GET /orgs/{org}/projects project getProjects + // swagger:operation GET /orgs/{org}/projects project orgGetProjects // --- // summary: Get a list of projects // produces: @@ -82,6 +83,14 @@ func GetProjects(ctx *context.APIContext) { // description: organization name that the project belongs to // required: true // type: string + // - 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/ProjectList" @@ -92,23 +101,27 @@ func GetProjects(ctx *context.APIContext) { // "423": // "$ref": "#/responses/repoArchivedError" + listOptions := utils.GetListOptions(ctx) sortType := ctx.FormTrim("sort") isShowClosed := strings.ToLower(ctx.FormTrim("state")) == "closed" searchOptions := project_model.SearchOptions{ - IsClosed: optional.Some(isShowClosed), - OrderBy: project_model.GetSearchOrderByBySortType(sortType), - OwnerID: ctx.ContextUser.ID, - Type: project_model.TypeOrganization, + ListOptions: listOptions, + IsClosed: optional.Some(isShowClosed), + OrderBy: project_model.GetSearchOrderByBySortType(sortType), + OwnerID: ctx.ContextUser.ID, + Type: project_model.TypeOrganization, } - projects, err := db.Find[project_model.Project](ctx, &searchOptions) + projects, maxResults, err := db.FindAndCount[project_model.Project](ctx, &searchOptions) if err != nil { - ctx.ServerError("FindProjects", err) + ctx.Error(http.StatusInternalServerError, "db.FindAndCount[project_model.Project]", err) return } + ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) + ctx.SetTotalCountHeader(maxResults) ctx.JSON(http.StatusOK, convert.ToProjects(ctx, projects)) } diff --git a/routers/api/v1/project/project.go b/routers/api/v1/project/project.go index 53827358f5..47d2d01945 100644 --- a/routers/api/v1/project/project.go +++ b/routers/api/v1/project/project.go @@ -16,7 +16,7 @@ import ( // GetProject returns a project func GetProject(ctx *context.APIContext) { - // swagger:operation GET /projects/{project_id} project getProject + // swagger:operation GET /projects/{project_id} project projectGetProject // --- // summary: Get a project // produces: @@ -73,7 +73,7 @@ func GetProject(ctx *context.APIContext) { // EditProject edits a project func EditProject(ctx *context.APIContext) { - // swagger:operation PATCH /projects/{project_id} project editProject + // swagger:operation PATCH /projects/{project_id} project projectEditProject // --- // summary: Edit a project // produces: @@ -119,7 +119,7 @@ func EditProject(ctx *context.APIContext) { // DeleteProject deletes a project func DeleteProject(ctx *context.APIContext) { - // swagger:operation DELETE /projects/{project_id} project deleteProject + // swagger:operation DELETE /projects/{project_id} project projectDeleteProject // --- // summary: Delete a project // description: Deletes a specific project for a given user and repository. @@ -151,7 +151,7 @@ func DeleteProject(ctx *context.APIContext) { // ChangeProjectStatus updates the status of a project between "open" and "close" func ChangeProjectStatus(ctx *context.APIContext) { - // swagger:operation PATCH /projects/{project_id}/{action} project changeProjectStatus + // swagger:operation PATCH /projects/{project_id}/{action} project projectProjectChangeProjectStatus // --- // summary: Change the status of a project // produces: diff --git a/routers/api/v1/project/project_column.go b/routers/api/v1/project/project_column.go index 088a9ede31..cb247bbdbf 100644 --- a/routers/api/v1/project/project_column.go +++ b/routers/api/v1/project/project_column.go @@ -18,7 +18,7 @@ import ( // GetProjectColumn returns a project column func GetProjectColumn(ctx *context.APIContext) { - // swagger:operation GET /projects/columns/{column_id} project getProject + // swagger:operation GET /projects/columns/{column_id} project projectGetProjectColumn // --- // summary: Get a project column // produces: @@ -51,7 +51,7 @@ func GetProjectColumn(ctx *context.APIContext) { // GetProjectColumns returns a list of project columns func GetProjectColumns(ctx *context.APIContext) { - // swagger:operation GET /projects/{project_id}/columns project getProject + // swagger:operation GET /projects/{project_id}/columns project projectGetProjectColumns // --- // summary: Get a list of project columns // produces: @@ -91,7 +91,7 @@ func GetProjectColumns(ctx *context.APIContext) { // AddColumnToProject adds a new column to a project func AddColumnToProject(ctx *context.APIContext) { - // swagger:operation POST /projects/{project_id}/columns project addColumnToProject + // swagger:operation POST /projects/{project_id}/columns project projectAddColumnToProject // --- // summary: Add a column to a project // consumes: @@ -153,7 +153,7 @@ func AddColumnToProject(ctx *context.APIContext) { // EditProjectColumn edits a project column func EditProjectColumn(ctx *context.APIContext) { - // swagger:operation PATCH /projects/columns/{column_id} project editProjectColumn + // swagger:operation PATCH /projects/columns/{column_id} project projectEditProjectColumn // --- // summary: Edit a project column // consumes: @@ -212,7 +212,7 @@ func EditProjectColumn(ctx *context.APIContext) { // DeleteProjectColumn deletes a project column func DeleteProjectColumn(ctx *context.APIContext) { - // swagger:operation DELETE /projects/columns/{column_id} project deleteProjectColumn + // swagger:operation DELETE /projects/columns/{column_id} project projectDeleteProjectColumn // --- // summary: Delete a project column // parameters: @@ -241,7 +241,7 @@ func DeleteProjectColumn(ctx *context.APIContext) { // SetDefaultProjectColumn set default column for issues/pulls func SetDefaultProjectColumn(ctx *context.APIContext) { - // swagger:operation PUT /projects/columns/{column_id}/default project setDefaultProjectColumn + // swagger:operation PUT /projects/columns/{column_id}/default project projectSetDefaultProjectColumn // --- // summary: Set default column for issues/pulls // parameters: @@ -276,7 +276,7 @@ func SetDefaultProjectColumn(ctx *context.APIContext) { // MoveColumns moves or keeps columns in a project and sorts them inside that project func MoveColumns(ctx *context.APIContext) { - // swagger:operation PATCH /projects/{project_id}/columns/move project moveColumns + // swagger:operation PATCH /projects/{project_id}/columns/move project projectMoveColumns // --- // summary: Move columns in a project // consumes: @@ -337,7 +337,7 @@ func MoveColumns(ctx *context.APIContext) { // MoveIssues moves or keeps issues in a column and sorts them inside that column func MoveIssues(ctx *context.APIContext) { - // swagger:operation PATCH /projects/{project_id}/columns/{column_id}/move project moveIssues + // swagger:operation PATCH /projects/{project_id}/columns/{column_id}/move project projectMoveIssues // --- // summary: Move issues in a column // consumes: diff --git a/routers/api/v1/repo/project.go b/routers/api/v1/repo/project.go index 4e37ce9300..2d42640eae 100644 --- a/routers/api/v1/repo/project.go +++ b/routers/api/v1/repo/project.go @@ -17,13 +17,14 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) // GetProjects returns a list of projects for a given user and repository. func GetProjects(ctx *context.APIContext) { - // swagger:operation GET /repos/{owner}/{reponame}/projects project getProjects + // swagger:operation GET /repos/{owner}/{reponame}/projects project repoGetProjects // --- // summary: Get a list of projects // description: Returns a list of projects for a given user and repository. @@ -40,6 +41,14 @@ func GetProjects(ctx *context.APIContext) { // description: repository name. // required: true // type: string + // - 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/ProjectList" @@ -50,30 +59,34 @@ func GetProjects(ctx *context.APIContext) { // "423": // "$ref": "#/responses/repoArchivedError" + listOptions := utils.GetListOptions(ctx) sortType := ctx.FormTrim("sort") isShowClosed := strings.ToLower(ctx.FormTrim("state")) == "closed" searchOptions := project_model.SearchOptions{ - IsClosed: optional.Some(isShowClosed), - OrderBy: project_model.GetSearchOrderByBySortType(sortType), - RepoID: ctx.Repo.Repository.ID, - Type: project_model.TypeRepository, + ListOptions: listOptions, + IsClosed: optional.Some(isShowClosed), + OrderBy: project_model.GetSearchOrderByBySortType(sortType), + RepoID: ctx.Repo.Repository.ID, + Type: project_model.TypeRepository, } - projects, err := db.Find[project_model.Project](ctx, &searchOptions) + projects, maxResults, err := db.FindAndCount[project_model.Project](ctx, &searchOptions) if err != nil { - ctx.ServerError("FindProjects", err) + ctx.Error(http.StatusInternalServerError, "db.FindAndCount[project_model.Project]", err) return } + ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) + ctx.SetTotalCountHeader(maxResults) ctx.JSON(http.StatusOK, convert.ToProjects(ctx, projects)) } // CreateProject creates a new project func CreateProject(ctx *context.APIContext) { - // swagger:operation POST /repos/{owner}/{reponame}/projects project createProject + // swagger:operation POST /repos/{owner}/{reponame}/projects project repoCreateProject // --- // summary: Create a new project // description: Creates a new project for a given user and repository. @@ -132,7 +145,7 @@ func CreateProject(ctx *context.APIContext) { // UpdateIssueProject change an issue's project to another project in a repository func UpdateIssueProject(ctx *context.APIContext) { - // swagger:operation PUT /repos/{owner}/{reponame}/projects/{type} project updateIssueProject + // swagger:operation PUT /repos/{owner}/{reponame}/projects/{type} project repoUpdateIssueProject // --- // summary: Change an issue's project // consumes: diff --git a/routers/api/v1/user/project.go b/routers/api/v1/user/project.go index 533199dbb6..f71607e26e 100644 --- a/routers/api/v1/user/project.go +++ b/routers/api/v1/user/project.go @@ -12,13 +12,14 @@ import ( "code.gitea.io/gitea/modules/optional" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) // CreateProject creates a new project for a user func CreateProject(ctx *context.APIContext) { - // swagger:operation POST /user/projects project createProject + // swagger:operation POST /user/projects project userCreateProject // --- // summary: Create a new project for user // consumes: @@ -66,7 +67,7 @@ func CreateProject(ctx *context.APIContext) { // GetProjects returns a list of projects that belong to a user func GetProjects(ctx *context.APIContext) { - // swagger:operation GET /users/{username}/projects project getProjects + // swagger:operation GET /users/{username}/projects project userGetProjects // --- // summary: Get a list of projects // produces: @@ -77,6 +78,14 @@ func GetProjects(ctx *context.APIContext) { // description: owner of the project // required: true // type: string + // - 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/ProjectList" @@ -87,23 +96,27 @@ func GetProjects(ctx *context.APIContext) { // "423": // "$ref": "#/responses/repoArchivedError" + listOptions := utils.GetListOptions(ctx) sortType := ctx.FormTrim("sort") isShowClosed := strings.ToLower(ctx.FormTrim("state")) == "closed" searchOptions := project_model.SearchOptions{ - IsClosed: optional.Some(isShowClosed), - OrderBy: project_model.GetSearchOrderByBySortType(sortType), - OwnerID: ctx.ContextUser.ID, - Type: project_model.TypeIndividual, + ListOptions: listOptions, + IsClosed: optional.Some(isShowClosed), + OrderBy: project_model.GetSearchOrderByBySortType(sortType), + OwnerID: ctx.ContextUser.ID, + Type: project_model.TypeIndividual, } - projects, err := db.Find[project_model.Project](ctx, &searchOptions) + projects, maxResults, err := db.FindAndCount[project_model.Project](ctx, &searchOptions) if err != nil { - ctx.ServerError("FindProjects", err) + ctx.Error(http.StatusInternalServerError, "db.FindAndCount[project_model.Project]", err) return } + ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) + ctx.SetTotalCountHeader(maxResults) ctx.JSON(http.StatusOK, convert.ToProjects(ctx, projects)) } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 872561ca8d..7fdf8a72a8 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -2840,7 +2840,7 @@ "project" ], "summary": "Get a list of projects", - "operationId": "getProjects", + "operationId": "orgGetProjects", "parameters": [ { "type": "string", @@ -2848,6 +2848,18 @@ "name": "org", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results", + "name": "limit", + "in": "query" } ], "responses": { @@ -2876,7 +2888,7 @@ "project" ], "summary": "Create a new project", - "operationId": "createProject", + "operationId": "orgCreateProject", "parameters": [ { "type": "string", @@ -3524,7 +3536,7 @@ "project" ], "summary": "Get a project column", - "operationId": "getProject", + "operationId": "projectGetProjectColumn", "parameters": [ { "type": "integer", @@ -3554,7 +3566,7 @@ "project" ], "summary": "Delete a project column", - "operationId": "deleteProjectColumn", + "operationId": "projectDeleteProjectColumn", "parameters": [ { "type": "integer", @@ -3590,7 +3602,7 @@ "project" ], "summary": "Edit a project column", - "operationId": "editProjectColumn", + "operationId": "projectEditProjectColumn", "parameters": [ { "type": "integer", @@ -3637,7 +3649,7 @@ "project" ], "summary": "Set default column for issues/pulls", - "operationId": "setDefaultProjectColumn", + "operationId": "projectSetDefaultProjectColumn", "parameters": [ { "type": "integer", @@ -3672,7 +3684,7 @@ "project" ], "summary": "Get a project", - "operationId": "getProject", + "operationId": "projectGetProject", "parameters": [ { "type": "integer", @@ -3703,7 +3715,7 @@ "project" ], "summary": "Delete a project", - "operationId": "deleteProject", + "operationId": "projectDeleteProject", "parameters": [ { "type": "integer", @@ -3736,7 +3748,7 @@ "project" ], "summary": "Edit a project", - "operationId": "editProject", + "operationId": "projectEditProject", "parameters": [ { "type": "integer", @@ -3774,7 +3786,7 @@ "project" ], "summary": "Get a list of project columns", - "operationId": "getProject", + "operationId": "projectGetProjectColumns", "parameters": [ { "type": "integer", @@ -3810,7 +3822,7 @@ "project" ], "summary": "Add a column to a project", - "operationId": "addColumnToProject", + "operationId": "projectAddColumnToProject", "parameters": [ { "type": "integer", @@ -3860,7 +3872,7 @@ "project" ], "summary": "Move columns in a project", - "operationId": "moveColumns", + "operationId": "projectMoveColumns", "parameters": [ { "type": "integer", @@ -3904,7 +3916,7 @@ "project" ], "summary": "Move issues in a column", - "operationId": "moveIssues", + "operationId": "projectMoveIssues", "parameters": [ { "type": "integer", @@ -3955,7 +3967,7 @@ "project" ], "summary": "Change the status of a project", - "operationId": "changeProjectStatus", + "operationId": "projectProjectChangeProjectStatus", "parameters": [ { "type": "integer", @@ -4292,7 +4304,7 @@ "project" ], "summary": "Get a list of projects", - "operationId": "getProjects", + "operationId": "repoGetProjects", "parameters": [ { "type": "string", @@ -4307,6 +4319,18 @@ "name": "reponame", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results", + "name": "limit", + "in": "query" } ], "responses": { @@ -4336,7 +4360,7 @@ "project" ], "summary": "Create a new project", - "operationId": "createProject", + "operationId": "repoCreateProject", "parameters": [ { "type": "string", @@ -4390,7 +4414,7 @@ "project" ], "summary": "Change an issue's project", - "operationId": "updateIssueProject", + "operationId": "repoUpdateIssueProject", "parameters": [ { "type": "string", @@ -17845,7 +17869,7 @@ "project" ], "summary": "Create a new project for user", - "operationId": "createProject", + "operationId": "userCreateProject", "parameters": [ { "description": "Project data", @@ -18717,7 +18741,7 @@ "project" ], "summary": "Get a list of projects", - "operationId": "getProjects", + "operationId": "userGetProjects", "parameters": [ { "type": "string", @@ -18725,6 +18749,18 @@ "name": "username", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results", + "name": "limit", + "in": "query" } ], "responses": { @@ -20765,18 +20801,21 @@ "description": "CreateProjectColumnOption options for creating a project column", "type": "object", "required": [ - "Title" + "title" ], "properties": { - "Color": { - "type": "string" + "color": { + "type": "string", + "x-go-name": "Color" }, - "Sorting": { + "sorting": { "type": "integer", - "format": "int8" + "format": "int8", + "x-go-name": "Sorting" }, - "Title": { - "type": "string" + "title": { + "type": "string", + "x-go-name": "Title" } }, "x-go-package": "code.gitea.io/gitea/modules/structs" @@ -21807,15 +21846,18 @@ "description": "EditProjectColumnOption options for editing a project column", "type": "object", "properties": { - "Color": { - "type": "string" + "color": { + "type": "string", + "x-go-name": "Color" }, - "Sorting": { + "sorting": { "type": "integer", - "format": "int8" + "format": "int8", + "x-go-name": "Sorting" }, - "Title": { - "type": "string" + "title": { + "type": "string", + "x-go-name": "Title" } }, "x-go-package": "code.gitea.io/gitea/modules/structs"