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
|
||||
gitlink-cli search +users -k "zhangsan"
|
||||
|
||||
# List recommended / featured projects
|
||||
gitlink-cli search +recommend
|
||||
```
|
||||
|
||||
### User Profile
|
||||
|
|
|
|||
|
|
@ -473,6 +473,9 @@ gitlink-cli search +repos -k "machine learning"
|
|||
|
||||
# 搜索用户
|
||||
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.root.long": "Manage repositories, issues, pull requests, releases, CI and workflows on 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.short": "Search operations",
|
||||
"cmd.search.users.short": "Search users",
|
||||
|
|
|
|||
|
|
@ -90,6 +90,8 @@
|
|||
"cmd.repo.tree.short": "列出仓库文件和目录",
|
||||
"cmd.root.long": "用于管理 GitLink 上的仓库、议题、拉取请求、发布、CI 和工作流。",
|
||||
"cmd.root.short": "GitLink CLI - GitLink 命令行工具",
|
||||
"cmd.search.recommend.long": "列出平台推荐/精选项目(ID、名称、访问量、作者、分类)。",
|
||||
"cmd.search.recommend.short": "列出推荐项目",
|
||||
"cmd.search.repos.short": "搜索仓库",
|
||||
"cmd.search.short": "搜索操作",
|
||||
"cmd.search.users.short": "搜索用户",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package search
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
|
||||
"github.com/gitlink-org/gitlink-cli/internal/i18n"
|
||||
|
|
@ -52,6 +53,26 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
|
|||
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 ---
|
||||
|
||||
func TestSearchReposHTTPError(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue