From 25dc1556cd70b567a4920beb002a0addfbfd6ef2 Mon Sep 17 00:00:00 2001 From: JakobDev Date: Tue, 23 May 2023 12:10:23 +0200 Subject: [PATCH] Add API for Label templates (#24602) This adds API that allows getting the Label templates of the Gitea Instance --- modules/structs/issue_label.go | 10 +++ routers/api/v1/api.go | 2 + routers/api/v1/misc/label_templates.go | 60 +++++++++++++ routers/api/v1/swagger/misc.go | 14 +++ services/convert/issue.go | 22 +++++ templates/swagger/v1_json.tmpl | 89 +++++++++++++++++++ tests/integration/api_label_templates_test.go | 61 +++++++++++++ 7 files changed, 258 insertions(+) create mode 100644 routers/api/v1/misc/label_templates.go create mode 100644 tests/integration/api_label_templates_test.go diff --git a/modules/structs/issue_label.go b/modules/structs/issue_label.go index 5bb6cc3b8..2610d3e93 100644 --- a/modules/structs/issue_label.go +++ b/modules/structs/issue_label.go @@ -44,3 +44,13 @@ type IssueLabelsOption struct { // list of label IDs Labels []int64 `json:"labels"` } + +// LabelTemplate info of a Label template +type LabelTemplate struct { + Name string `json:"name"` + // example: false + Exclusive bool `json:"exclusive"` + // example: 00aabb + Color string `json:"color"` + Description string `json:"description"` +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index f1e1cf946..53c58c4b9 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -723,6 +723,8 @@ func Routes(ctx gocontext.Context) *web.Route { m.Get("/gitignore/templates/{name}", misc.GetGitignoreTemplateInfo) m.Get("/licenses", misc.ListLicenseTemplates) m.Get("/licenses/{name}", misc.GetLicenseTemplateInfo) + m.Get("/label/templates", misc.ListLabelTemplates) + m.Get("/label/templates/{name}", misc.GetLabelTemplate) m.Group("/settings", func() { m.Get("/ui", settings.GetGeneralUISettings) m.Get("/api", settings.GetGeneralAPISettings) diff --git a/routers/api/v1/misc/label_templates.go b/routers/api/v1/misc/label_templates.go new file mode 100644 index 000000000..0e0ca39fc --- /dev/null +++ b/routers/api/v1/misc/label_templates.go @@ -0,0 +1,60 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package misc + +import ( + "net/http" + + "code.gitea.io/gitea/modules/context" + repo_module "code.gitea.io/gitea/modules/repository" + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/convert" +) + +// Shows a list of all Label templates +func ListLabelTemplates(ctx *context.APIContext) { + // swagger:operation GET /label/templates miscellaneous listLabelTemplates + // --- + // summary: Returns a list of all label templates + // produces: + // - application/json + // responses: + // "200": + // "$ref": "#/responses/LabelTemplateList" + result := make([]string, len(repo_module.LabelTemplateFiles)) + for i := range repo_module.LabelTemplateFiles { + result[i] = repo_module.LabelTemplateFiles[i].DisplayName + } + + ctx.JSON(http.StatusOK, result) +} + +// Shows all labels in a template +func GetLabelTemplate(ctx *context.APIContext) { + // swagger:operation GET /label/templates/{name} miscellaneous getLabelTemplateInfo + // --- + // summary: Returns all labels in a template + // produces: + // - application/json + // parameters: + // - name: name + // in: path + // description: name of the template + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/LabelTemplateInfo" + // "404": + // "$ref": "#/responses/notFound" + name := util.PathJoinRelX(ctx.Params("name")) + + labels, err := repo_module.LoadTemplateLabelsByDisplayName(name) + if err != nil { + ctx.NotFound() + return + } + + ctx.JSON(http.StatusOK, convert.ToLabelTemplateList(labels)) +} diff --git a/routers/api/v1/swagger/misc.go b/routers/api/v1/swagger/misc.go index 5778b659b..df8a813df 100644 --- a/routers/api/v1/swagger/misc.go +++ b/routers/api/v1/swagger/misc.go @@ -48,3 +48,17 @@ type swaggerResponseStringSlice struct { // in:body Body []string `json:"body"` } + +// LabelTemplateList +// swagger:response LabelTemplateList +type swaggerResponseLabelTemplateList struct { + // in:body + Body []string `json:"body"` +} + +// LabelTemplateInfo +// swagger:response LabelTemplateInfo +type swaggerResponseLabelTemplateInfo struct { + // in:body + Body []api.LabelTemplate `json:"body"` +} diff --git a/services/convert/issue.go b/services/convert/issue.go index 6d31a123b..3d1b21c6b 100644 --- a/services/convert/issue.go +++ b/services/convert/issue.go @@ -13,6 +13,7 @@ import ( issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/label" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" @@ -238,3 +239,24 @@ func ToAPIMilestone(m *issues_model.Milestone) *api.Milestone { } return apiMilestone } + +// ToLabelTemplate converts Label to API format +func ToLabelTemplate(label *label.Label) *api.LabelTemplate { + result := &api.LabelTemplate{ + Name: label.Name, + Exclusive: label.Exclusive, + Color: strings.TrimLeft(label.Color, "#"), + Description: label.Description, + } + + return result +} + +// ToLabelTemplateList converts list of Label to API format +func ToLabelTemplateList(labels []*label.Label) []*api.LabelTemplate { + result := make([]*api.LabelTemplate, len(labels)) + for i := range labels { + result[i] = ToLabelTemplate(labels[i]) + } + return result +} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 3c56fc9ef..15402ecdf 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -929,6 +929,52 @@ } } }, + "/label/templates": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "miscellaneous" + ], + "summary": "Returns a list of all label templates", + "operationId": "listLabelTemplates", + "responses": { + "200": { + "$ref": "#/responses/LabelTemplateList" + } + } + } + }, + "/label/templates/{name}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "miscellaneous" + ], + "summary": "Returns all labels in a template", + "operationId": "getLabelTemplateInfo", + "parameters": [ + { + "type": "string", + "description": "name of the template", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/LabelTemplateInfo" + }, + "404": { + "$ref": "#/responses/notFound" + } + } + } + }, "/licenses": { "get": { "produces": [ @@ -18858,6 +18904,31 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, + "LabelTemplate": { + "description": "LabelTemplate info of a Label template", + "type": "object", + "properties": { + "color": { + "type": "string", + "x-go-name": "Color", + "example": "00aabb" + }, + "description": { + "type": "string", + "x-go-name": "Description" + }, + "exclusive": { + "type": "boolean", + "x-go-name": "Exclusive", + "example": false + }, + "name": { + "type": "string", + "x-go-name": "Name" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, "LicenseTemplateInfo": { "description": "LicensesInfo contains information about a License", "type": "object", @@ -21797,6 +21868,24 @@ } } }, + "LabelTemplateInfo": { + "description": "LabelTemplateInfo", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/LabelTemplate" + } + } + }, + "LabelTemplateList": { + "description": "LabelTemplateList", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, "LanguageStatistics": { "description": "LanguageStatistics", "schema": { diff --git a/tests/integration/api_label_templates_test.go b/tests/integration/api_label_templates_test.go new file mode 100644 index 000000000..007e97901 --- /dev/null +++ b/tests/integration/api_label_templates_test.go @@ -0,0 +1,61 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "net/url" + "strings" + "testing" + + repo_module "code.gitea.io/gitea/modules/repository" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/tests" + + "github.com/stretchr/testify/assert" +) + +func TestAPIListLabelTemplates(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + req := NewRequest(t, "GET", "/api/v1/label/templates") + resp := MakeRequest(t, req, http.StatusOK) + + var templateList []string + DecodeJSON(t, resp, &templateList) + + for i := range repo_module.LabelTemplateFiles { + assert.Equal(t, repo_module.LabelTemplateFiles[i].DisplayName, templateList[i]) + } +} + +func TestAPIGetLabelTemplateInfo(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + // If Gitea has for some reason no Label templates, we need to skip this test + if len(repo_module.LabelTemplateFiles) == 0 { + return + } + + // Use the first template for the test + templateName := repo_module.LabelTemplateFiles[0].DisplayName + + urlStr := fmt.Sprintf("/api/v1/label/templates/%s", url.PathEscape(templateName)) + req := NewRequest(t, "GET", urlStr) + resp := MakeRequest(t, req, http.StatusOK) + + var templateInfo []api.LabelTemplate + DecodeJSON(t, resp, &templateInfo) + + labels, err := repo_module.LoadTemplateLabelsByDisplayName(templateName) + assert.NoError(t, err) + + for i := range labels { + assert.Equal(t, strings.TrimLeft(labels[i].Color, "#"), templateInfo[i].Color) + assert.Equal(t, labels[i].Description, templateInfo[i].Description) + assert.Equal(t, labels[i].Exclusive, templateInfo[i].Exclusive) + assert.Equal(t, labels[i].Name, templateInfo[i].Name) + } +}