feat(search): add recommended projects shortcut
Add `search +recommend`, listing the platform's recommended/featured projects via GET /api/projects/recommend — a discovery endpoint that had no shortcut coverage. The endpoint returns a bare JSON array; the command normalizes it into structured data for clean output. Includes unit tests, bilingual (en-US/zh-CN) help text, README updates, and a change note. Verified against production gitlink.org.cn. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
ef7a2c6ac1
commit
e5cb868442
|
|
@ -495,6 +495,9 @@ gitlink-cli search +repos -k "machine learning"
|
||||||
|
|
||||||
# Search users
|
# Search users
|
||||||
gitlink-cli search +users -k "zhangsan"
|
gitlink-cli search +users -k "zhangsan"
|
||||||
|
|
||||||
|
# List recommended / featured projects
|
||||||
|
gitlink-cli search +recommend
|
||||||
```
|
```
|
||||||
|
|
||||||
### User Profile
|
### User Profile
|
||||||
|
|
|
||||||
|
|
@ -473,6 +473,9 @@ gitlink-cli search +repos -k "machine learning"
|
||||||
|
|
||||||
# 搜索用户
|
# 搜索用户
|
||||||
gitlink-cli search +users -k "zhangsan"
|
gitlink-cli search +users -k "zhangsan"
|
||||||
|
|
||||||
|
# 列出推荐/精选项目
|
||||||
|
gitlink-cli search +recommend
|
||||||
```
|
```
|
||||||
|
|
||||||
### 用户画像
|
### 用户画像
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Search: Recommended Projects
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Adds `search +recommend`, which lists the platform's recommended / featured
|
||||||
|
projects. This wraps `GET /api/projects/recommend`, a discovery endpoint that
|
||||||
|
previously had no shortcut coverage.
|
||||||
|
|
||||||
|
## Command
|
||||||
|
|
||||||
|
| Command | Purpose | Endpoint |
|
||||||
|
|---------|---------|----------|
|
||||||
|
| `gitlink-cli search +recommend` | List recommended/featured projects | `GET /projects/recommend` |
|
||||||
|
|
||||||
|
Each item includes `id`, `identifier`, `name`, `visits`, `author`
|
||||||
|
(`name`/`login`/`image_url`) and `category`.
|
||||||
|
|
||||||
|
## Behaviour
|
||||||
|
|
||||||
|
- Takes no arguments. Honors the global `--format` (json/table/yaml).
|
||||||
|
- The endpoint returns a bare JSON array; the command normalizes it into
|
||||||
|
structured data so the output is clean (not an escaped JSON string).
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
Unit tests cover the endpoint path, the bare-array normalization, and HTTP
|
||||||
|
error handling.
|
||||||
|
|
||||||
|
## 中文说明
|
||||||
|
|
||||||
|
### 变更内容
|
||||||
|
|
||||||
|
- 新增 `search +recommend`,列出平台推荐/精选项目,封装此前无 shortcut 的
|
||||||
|
`GET /api/projects/recommend`。
|
||||||
|
- 无需参数,遵循全局 `--format`;对该接口返回的裸 JSON 数组做结构化归一,输出整洁。
|
||||||
|
|
||||||
|
### 价值
|
||||||
|
|
||||||
|
为项目发现提供入口(科研选题/技术调研时可快速看到平台精选项目),补全 Raw API 封装。
|
||||||
|
|
@ -90,6 +90,8 @@
|
||||||
"cmd.repo.tree.short": "List repository files and directories",
|
"cmd.repo.tree.short": "List repository files and directories",
|
||||||
"cmd.root.long": "Manage repositories, issues, pull requests, releases, CI and workflows on GitLink.",
|
"cmd.root.long": "Manage repositories, issues, pull requests, releases, CI and workflows on GitLink.",
|
||||||
"cmd.root.short": "GitLink CLI - command-line tool for GitLink",
|
"cmd.root.short": "GitLink CLI - command-line tool for GitLink",
|
||||||
|
"cmd.search.recommend.long": "List the platform's recommended/featured projects (id, name, visits, author, category).",
|
||||||
|
"cmd.search.recommend.short": "List recommended projects",
|
||||||
"cmd.search.repos.short": "Search repositories",
|
"cmd.search.repos.short": "Search repositories",
|
||||||
"cmd.search.short": "Search operations",
|
"cmd.search.short": "Search operations",
|
||||||
"cmd.search.users.short": "Search users",
|
"cmd.search.users.short": "Search users",
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,8 @@
|
||||||
"cmd.repo.tree.short": "列出仓库文件和目录",
|
"cmd.repo.tree.short": "列出仓库文件和目录",
|
||||||
"cmd.root.long": "用于管理 GitLink 上的仓库、议题、拉取请求、发布、CI 和工作流。",
|
"cmd.root.long": "用于管理 GitLink 上的仓库、议题、拉取请求、发布、CI 和工作流。",
|
||||||
"cmd.root.short": "GitLink CLI - GitLink 命令行工具",
|
"cmd.root.short": "GitLink CLI - GitLink 命令行工具",
|
||||||
|
"cmd.search.recommend.long": "列出平台推荐/精选项目(ID、名称、访问量、作者、分类)。",
|
||||||
|
"cmd.search.recommend.short": "列出推荐项目",
|
||||||
"cmd.search.repos.short": "搜索仓库",
|
"cmd.search.repos.short": "搜索仓库",
|
||||||
"cmd.search.short": "搜索操作",
|
"cmd.search.short": "搜索操作",
|
||||||
"cmd.search.users.short": "搜索用户",
|
"cmd.search.users.short": "搜索用户",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package search
|
package search
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/gitlink-org/gitlink-cli/internal/i18n"
|
"github.com/gitlink-org/gitlink-cli/internal/i18n"
|
||||||
|
|
@ -52,6 +53,26 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
|
||||||
return ctx.Output(env)
|
return ctx.Output(env)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "recommend",
|
||||||
|
Description: tr.T("cmd.search.recommend.short"),
|
||||||
|
Long: tr.T("cmd.search.recommend.long"),
|
||||||
|
Run: func(ctx *common.RuntimeContext) error {
|
||||||
|
env, err := ctx.CallAPI("GET", "/projects/recommend", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// This endpoint returns a bare JSON array, which the client
|
||||||
|
// surfaces as a raw string; normalize it to structured data.
|
||||||
|
if s, ok := env.Data.(string); ok {
|
||||||
|
var arr interface{}
|
||||||
|
if json.Unmarshal([]byte(s), &arr) == nil {
|
||||||
|
return ctx.OutputData(arr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctx.Output(env)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,37 @@ func TestSearchUsers(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- recommend ---
|
||||||
|
|
||||||
|
func TestSearchRecommend(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.URL.Path != "/projects/recommend.json" {
|
||||||
|
t.Fatalf("unexpected path: %s", r.URL.Path)
|
||||||
|
}
|
||||||
|
// bare JSON array (matches the real endpoint shape)
|
||||||
|
writeJSON(w, []interface{}{
|
||||||
|
map[string]interface{}{"identifier": "forgeplus", "name": "确实开源", "visits": float64(48497)},
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
if err := runShortcut(t, server, "recommend", nil); err != nil {
|
||||||
|
t.Fatalf("recommend failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSearchRecommendHTTPError(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte("server error"))
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
if err := runShortcut(t, server, "recommend", nil); err == nil {
|
||||||
|
t.Fatal("expected error for HTTP 500")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- HTTP error paths ---
|
// --- HTTP error paths ---
|
||||||
|
|
||||||
func TestSearchReposHTTPError(t *testing.T) {
|
func TestSearchReposHTTPError(t *testing.T) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue