mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	Add language statistics API endpoint (#11737)
* Add language statistics API * Add tests
This commit is contained in:
		
							
								
								
									
										46
									
								
								integrations/api_repo_languages_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								integrations/api_repo_languages_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | // Copyright 2020 The Gitea Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package integrations | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/url" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestRepoLanguages(t *testing.T) { | ||||||
|  | 	onGiteaRun(t, func(t *testing.T, u *url.URL) { | ||||||
|  | 		session := loginUser(t, "user2") | ||||||
|  |  | ||||||
|  | 		// Request editor page | ||||||
|  | 		req := NewRequest(t, "GET", "/user2/repo1/_new/master/") | ||||||
|  | 		resp := session.MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
|  | 		doc := NewHTMLParser(t, resp.Body) | ||||||
|  | 		lastCommit := doc.GetInputValueByName("last_commit") | ||||||
|  | 		assert.NotEmpty(t, lastCommit) | ||||||
|  |  | ||||||
|  | 		// Save new file to master branch | ||||||
|  | 		req = NewRequestWithValues(t, "POST", "/user2/repo1/_new/master/", map[string]string{ | ||||||
|  | 			"_csrf":         doc.GetCSRF(), | ||||||
|  | 			"last_commit":   lastCommit, | ||||||
|  | 			"tree_path":     "test.go", | ||||||
|  | 			"content":       "package main", | ||||||
|  | 			"commit_choice": "direct", | ||||||
|  | 		}) | ||||||
|  | 		session.MakeRequest(t, req, http.StatusFound) | ||||||
|  |  | ||||||
|  | 		// Save new file to master branch | ||||||
|  | 		req = NewRequest(t, "GET", "/api/v1/repos/user2/repo1/languages") | ||||||
|  | 		resp = session.MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
|  | 		var languages map[string]int64 | ||||||
|  | 		DecodeJSON(t, resp, &languages) | ||||||
|  |  | ||||||
|  | 		assert.InDeltaMapValues(t, map[string]int64{"Go": 12}, languages, 0) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
| @@ -855,6 +855,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||||
| 							Delete(reqToken(), repo.DeleteTopic) | 							Delete(reqToken(), repo.DeleteTopic) | ||||||
| 					}, reqAdmin()) | 					}, reqAdmin()) | ||||||
| 				}, reqAnyRepoReader()) | 				}, reqAnyRepoReader()) | ||||||
|  | 				m.Get("/languages", reqRepoReader(models.UnitTypeCode), repo.GetLanguages) | ||||||
| 			}, repoAssignment()) | 			}, repoAssignment()) | ||||||
| 		}) | 		}) | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										84
									
								
								routers/api/v1/repo/language.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								routers/api/v1/repo/language.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | // Copyright 2020 The Gitea Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package repo | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"net/http" | ||||||
|  | 	"strconv" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models" | ||||||
|  | 	"code.gitea.io/gitea/modules/context" | ||||||
|  | 	"code.gitea.io/gitea/modules/log" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type languageResponse []*models.LanguageStat | ||||||
|  |  | ||||||
|  | func (l languageResponse) MarshalJSON() ([]byte, error) { | ||||||
|  | 	var buf bytes.Buffer | ||||||
|  | 	if _, err := buf.WriteString("{"); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	for i, lang := range l { | ||||||
|  | 		if i > 0 { | ||||||
|  | 			if _, err := buf.WriteString(","); err != nil { | ||||||
|  | 				return nil, err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if _, err := buf.WriteString(strconv.Quote(lang.Language)); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		if _, err := buf.WriteString(":"); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		if _, err := buf.WriteString(strconv.FormatInt(lang.Size, 10)); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if _, err := buf.WriteString("}"); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return buf.Bytes(), nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetLanguages returns languages and number of bytes of code written | ||||||
|  | func GetLanguages(ctx *context.APIContext) { | ||||||
|  | 	// swagger:operation GET /repos/{owner}/{repo}/languages repository repoGetLanguages | ||||||
|  | 	// --- | ||||||
|  | 	// summary: Get languages and number of bytes of code written | ||||||
|  | 	// produces: | ||||||
|  | 	//   - application/json | ||||||
|  | 	// parameters: | ||||||
|  | 	// - name: owner | ||||||
|  | 	//   in: path | ||||||
|  | 	//   description: owner of the repo | ||||||
|  | 	//   type: string | ||||||
|  | 	//   required: true | ||||||
|  | 	// - name: repo | ||||||
|  | 	//   in: path | ||||||
|  | 	//   description: name of the repo | ||||||
|  | 	//   type: string | ||||||
|  | 	//   required: true | ||||||
|  | 	// responses: | ||||||
|  | 	//   "404": | ||||||
|  | 	//     "$ref": "#/responses/notFound" | ||||||
|  | 	//   "200": | ||||||
|  | 	//     "$ref": "#/responses/LanguageStatistics" | ||||||
|  |  | ||||||
|  | 	langs, err := ctx.Repo.Repository.GetLanguageStats() | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error("GetLanguageStats failed: %v", err) | ||||||
|  | 		ctx.InternalServerError(err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	resp := make(languageResponse, len(langs)) | ||||||
|  | 	for i, v := range langs { | ||||||
|  | 		resp[i] = v | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.JSON(http.StatusOK, resp) | ||||||
|  | } | ||||||
| @@ -302,3 +302,10 @@ type swaggerTopicNames struct { | |||||||
| 	// in: body | 	// in: body | ||||||
| 	Body api.TopicName `json:"body"` | 	Body api.TopicName `json:"body"` | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // LanguageStatistics | ||||||
|  | // swagger:response LanguageStatistics | ||||||
|  | type swaggerLanguageStatistics struct { | ||||||
|  | 	// in: body | ||||||
|  | 	Body map[string]int64 `json:"body"` | ||||||
|  | } | ||||||
|   | |||||||
| @@ -6049,6 +6049,42 @@ | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "/repos/{owner}/{repo}/languages": { | ||||||
|  |       "get": { | ||||||
|  |         "produces": [ | ||||||
|  |           "application/json" | ||||||
|  |         ], | ||||||
|  |         "tags": [ | ||||||
|  |           "repository" | ||||||
|  |         ], | ||||||
|  |         "summary": "Get languages and number of bytes of code written", | ||||||
|  |         "operationId": "repoGetLanguages", | ||||||
|  |         "parameters": [ | ||||||
|  |           { | ||||||
|  |             "type": "string", | ||||||
|  |             "description": "owner of the repo", | ||||||
|  |             "name": "owner", | ||||||
|  |             "in": "path", | ||||||
|  |             "required": true | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "type": "string", | ||||||
|  |             "description": "name of the repo", | ||||||
|  |             "name": "repo", | ||||||
|  |             "in": "path", | ||||||
|  |             "required": true | ||||||
|  |           } | ||||||
|  |         ], | ||||||
|  |         "responses": { | ||||||
|  |           "200": { | ||||||
|  |             "$ref": "#/responses/LanguageStatistics" | ||||||
|  |           }, | ||||||
|  |           "404": { | ||||||
|  |             "$ref": "#/responses/notFound" | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "/repos/{owner}/{repo}/milestones": { |     "/repos/{owner}/{repo}/milestones": { | ||||||
|       "get": { |       "get": { | ||||||
|         "produces": [ |         "produces": [ | ||||||
| @@ -14917,6 +14953,16 @@ | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "LanguageStatistics": { | ||||||
|  |       "description": "LanguageStatistics", | ||||||
|  |       "schema": { | ||||||
|  |         "type": "object", | ||||||
|  |         "additionalProperties": { | ||||||
|  |           "type": "integer", | ||||||
|  |           "format": "int64" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "MarkdownRender": { |     "MarkdownRender": { | ||||||
|       "description": "MarkdownRender is a rendered markdown document", |       "description": "MarkdownRender is a rendered markdown document", | ||||||
|       "schema": { |       "schema": { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user