From 12e23ee19916f4ca3f10916be8bb8666fd0d6f58 Mon Sep 17 00:00:00 2001
From: 6543 <6543@obermui.de>
Date: Thu, 13 Jun 2024 11:13:11 +0200
Subject: [PATCH] [Refactor] Unify repo search order by logic (#30876)

have repo OrderBy definitions defined in one place and use a single type
for OrderBy database options

(cherry picked from commit bb04311b0b5b7a28f94c4bc409db1c4a04bcef17)
---
 models/db/search.go            |  6 ---
 models/repo/repo_list.go       | 25 ------------
 models/repo/search.go          | 72 +++++++++++++++++++++++++++++-----
 routers/api/v1/repo/repo.go    |  2 +-
 routers/web/explore/repo.go    | 42 ++++----------------
 templates/swagger/v1_json.tmpl |  2 +-
 6 files changed, 71 insertions(+), 78 deletions(-)

diff --git a/models/db/search.go b/models/db/search.go
index aa577f08e0..37565f45e1 100644
--- a/models/db/search.go
+++ b/models/db/search.go
@@ -18,12 +18,6 @@ const (
 	SearchOrderByRecentUpdated         SearchOrderBy = "updated_unix DESC"
 	SearchOrderByOldest                SearchOrderBy = "created_unix ASC"
 	SearchOrderByNewest                SearchOrderBy = "created_unix DESC"
-	SearchOrderBySize                  SearchOrderBy = "size ASC"
-	SearchOrderBySizeReverse           SearchOrderBy = "size DESC"
-	SearchOrderByGitSize               SearchOrderBy = "git_size ASC"
-	SearchOrderByGitSizeReverse        SearchOrderBy = "git_size DESC"
-	SearchOrderByLFSSize               SearchOrderBy = "lfs_size ASC"
-	SearchOrderByLFSSizeReverse        SearchOrderBy = "lfs_size DESC"
 	SearchOrderByID                    SearchOrderBy = "id ASC"
 	SearchOrderByIDReverse             SearchOrderBy = "id DESC"
 	SearchOrderByStars                 SearchOrderBy = "num_stars ASC"
diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go
index 987c7df9b0..6cce2d33a3 100644
--- a/models/repo/repo_list.go
+++ b/models/repo/repo_list.go
@@ -205,31 +205,6 @@ type SearchRepoOptions struct {
 	OnlyShowRelevant bool
 }
 
-// SearchOrderBy is used to sort the result
-type SearchOrderBy string
-
-func (s SearchOrderBy) String() string {
-	return string(s)
-}
-
-// Strings for sorting result
-const (
-	SearchOrderByAlphabetically        SearchOrderBy = "name ASC"
-	SearchOrderByAlphabeticallyReverse SearchOrderBy = "name DESC"
-	SearchOrderByLeastUpdated          SearchOrderBy = "updated_unix ASC"
-	SearchOrderByRecentUpdated         SearchOrderBy = "updated_unix DESC"
-	SearchOrderByOldest                SearchOrderBy = "created_unix ASC"
-	SearchOrderByNewest                SearchOrderBy = "created_unix DESC"
-	SearchOrderBySize                  SearchOrderBy = "size ASC"
-	SearchOrderBySizeReverse           SearchOrderBy = "size DESC"
-	SearchOrderByID                    SearchOrderBy = "id ASC"
-	SearchOrderByIDReverse             SearchOrderBy = "id DESC"
-	SearchOrderByStars                 SearchOrderBy = "num_stars ASC"
-	SearchOrderByStarsReverse          SearchOrderBy = "num_stars DESC"
-	SearchOrderByForks                 SearchOrderBy = "num_forks ASC"
-	SearchOrderByForksReverse          SearchOrderBy = "num_forks DESC"
-)
-
 // UserOwnedRepoCond returns user ownered repositories
 func UserOwnedRepoCond(userID int64) builder.Cond {
 	return builder.Eq{
diff --git a/models/repo/search.go b/models/repo/search.go
index 54d6dcfb44..c500d41be8 100644
--- a/models/repo/search.go
+++ b/models/repo/search.go
@@ -5,20 +5,72 @@ package repo
 
 import "code.gitea.io/gitea/models/db"
 
+// Strings for sorting result
+const (
+	// only used for repos
+	SearchOrderByAlphabetically        db.SearchOrderBy = "owner_name ASC, name ASC"
+	SearchOrderByAlphabeticallyReverse db.SearchOrderBy = "owner_name DESC, name DESC"
+	SearchOrderBySize                  db.SearchOrderBy = "size ASC"
+	SearchOrderBySizeReverse           db.SearchOrderBy = "size DESC"
+	SearchOrderByGitSize               db.SearchOrderBy = "git_size ASC"
+	SearchOrderByGitSizeReverse        db.SearchOrderBy = "git_size DESC"
+	SearchOrderByLFSSize               db.SearchOrderBy = "lfs_size ASC"
+	SearchOrderByLFSSizeReverse        db.SearchOrderBy = "lfs_size DESC"
+	// alias as also used elsewhere
+	SearchOrderByLeastUpdated  db.SearchOrderBy = db.SearchOrderByLeastUpdated
+	SearchOrderByRecentUpdated db.SearchOrderBy = db.SearchOrderByRecentUpdated
+	SearchOrderByOldest        db.SearchOrderBy = db.SearchOrderByOldest
+	SearchOrderByNewest        db.SearchOrderBy = db.SearchOrderByNewest
+	SearchOrderByID            db.SearchOrderBy = db.SearchOrderByID
+	SearchOrderByIDReverse     db.SearchOrderBy = db.SearchOrderByIDReverse
+	SearchOrderByStars         db.SearchOrderBy = db.SearchOrderByStars
+	SearchOrderByStarsReverse  db.SearchOrderBy = db.SearchOrderByStarsReverse
+	SearchOrderByForks         db.SearchOrderBy = db.SearchOrderByForks
+	SearchOrderByForksReverse  db.SearchOrderBy = db.SearchOrderByForksReverse
+)
+
 // SearchOrderByMap represents all possible search order
 var SearchOrderByMap = map[string]map[string]db.SearchOrderBy{
 	"asc": {
-		"alpha":   "owner_name ASC, name ASC",
-		"created": db.SearchOrderByOldest,
-		"updated": db.SearchOrderByLeastUpdated,
-		"size":    db.SearchOrderBySize,
-		"id":      db.SearchOrderByID,
+		"alpha":    SearchOrderByAlphabetically,
+		"created":  SearchOrderByOldest,
+		"updated":  SearchOrderByLeastUpdated,
+		"size":     SearchOrderBySize,
+		"git_size": SearchOrderByGitSize,
+		"lfs_size": SearchOrderByLFSSize,
+		"id":       SearchOrderByID,
+		"stars":    SearchOrderByStars,
+		"forks":    SearchOrderByForks,
 	},
 	"desc": {
-		"alpha":   "owner_name DESC, name DESC",
-		"created": db.SearchOrderByNewest,
-		"updated": db.SearchOrderByRecentUpdated,
-		"size":    db.SearchOrderBySizeReverse,
-		"id":      db.SearchOrderByIDReverse,
+		"alpha":    SearchOrderByAlphabeticallyReverse,
+		"created":  SearchOrderByNewest,
+		"updated":  SearchOrderByRecentUpdated,
+		"size":     SearchOrderBySizeReverse,
+		"git_size": SearchOrderByGitSizeReverse,
+		"lfs_size": SearchOrderByLFSSizeReverse,
+		"id":       SearchOrderByIDReverse,
+		"stars":    SearchOrderByStarsReverse,
+		"forks":    SearchOrderByForksReverse,
 	},
 }
+
+// SearchOrderByFlatMap is similar to SearchOrderByMap but use human language keywords
+// to decide between asc and desc
+var SearchOrderByFlatMap = map[string]db.SearchOrderBy{
+	"newest":                SearchOrderByMap["desc"]["created"],
+	"oldest":                SearchOrderByMap["asc"]["created"],
+	"leastupdate":           SearchOrderByMap["asc"]["updated"],
+	"reversealphabetically": SearchOrderByMap["desc"]["alpha"],
+	"alphabetically":        SearchOrderByMap["asc"]["alpha"],
+	"reversesize":           SearchOrderByMap["desc"]["size"],
+	"size":                  SearchOrderByMap["asc"]["size"],
+	"reversegitsize":        SearchOrderByMap["desc"]["git_size"],
+	"gitsize":               SearchOrderByMap["asc"]["git_size"],
+	"reverselfssize":        SearchOrderByMap["desc"]["lfs_size"],
+	"lfssize":               SearchOrderByMap["asc"]["lfs_size"],
+	"moststars":             SearchOrderByMap["desc"]["stars"],
+	"feweststars":           SearchOrderByMap["asc"]["stars"],
+	"mostforks":             SearchOrderByMap["desc"]["forks"],
+	"fewestforks":           SearchOrderByMap["asc"]["forks"],
+}
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index 7c0289d4a0..41368146da 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -107,7 +107,7 @@ func Search(ctx *context.APIContext) {
 	// - name: sort
 	//   in: query
 	//   description: sort repos by attribute. Supported values are
-	//                "alpha", "created", "updated", "size", and "id".
+	//                "alpha", "created", "updated", "size", "git_size", "lfs_size", "stars", "forks" and "id".
 	//                Default is "alpha"
 	//   type: string
 	// - name: order
diff --git a/routers/web/explore/repo.go b/routers/web/explore/repo.go
index cf7381512b..1d5fb2c149 100644
--- a/routers/web/explore/repo.go
+++ b/routers/web/explore/repo.go
@@ -6,6 +6,7 @@ package explore
 import (
 	"fmt"
 	"net/http"
+	"strings"
 
 	"code.gitea.io/gitea/models/db"
 	repo_model "code.gitea.io/gitea/models/repo"
@@ -57,47 +58,18 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
 		orderBy db.SearchOrderBy
 	)
 
-	sortOrder := ctx.FormString("sort")
+	sortOrder := strings.ToLower(ctx.FormString("sort"))
 	if sortOrder == "" {
 		sortOrder = setting.UI.ExploreDefaultSort
 	}
-	ctx.Data["SortType"] = sortOrder
 
-	switch sortOrder {
-	case "newest":
-		orderBy = db.SearchOrderByNewest
-	case "oldest":
-		orderBy = db.SearchOrderByOldest
-	case "leastupdate":
-		orderBy = db.SearchOrderByLeastUpdated
-	case "reversealphabetically":
-		orderBy = db.SearchOrderByAlphabeticallyReverse
-	case "alphabetically":
-		orderBy = db.SearchOrderByAlphabetically
-	case "reversesize":
-		orderBy = db.SearchOrderBySizeReverse
-	case "size":
-		orderBy = db.SearchOrderBySize
-	case "reversegitsize":
-		orderBy = db.SearchOrderByGitSizeReverse
-	case "gitsize":
-		orderBy = db.SearchOrderByGitSize
-	case "reverselfssize":
-		orderBy = db.SearchOrderByLFSSizeReverse
-	case "lfssize":
-		orderBy = db.SearchOrderByLFSSize
-	case "moststars":
-		orderBy = db.SearchOrderByStarsReverse
-	case "feweststars":
-		orderBy = db.SearchOrderByStars
-	case "mostforks":
-		orderBy = db.SearchOrderByForksReverse
-	case "fewestforks":
-		orderBy = db.SearchOrderByForks
-	default:
-		ctx.Data["SortType"] = "recentupdate"
+	if order, ok := repo_model.SearchOrderByFlatMap[sortOrder]; ok {
+		orderBy = order
+	} else {
+		sortOrder = "recentupdate"
 		orderBy = db.SearchOrderByRecentUpdated
 	}
+	ctx.Data["SortType"] = sortOrder
 
 	keyword := ctx.FormTrim("q")
 
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index f0a48495a9..002d81f0bb 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -3616,7 +3616,7 @@
           },
           {
             "type": "string",
-            "description": "sort repos by attribute. Supported values are \"alpha\", \"created\", \"updated\", \"size\", and \"id\". Default is \"alpha\"",
+            "description": "sort repos by attribute. Supported values are \"alpha\", \"created\", \"updated\", \"size\", \"git_size\", \"lfs_size\", \"stars\", \"forks\" and \"id\". Default is \"alpha\"",
             "name": "sort",
             "in": "query"
           },