feat(issue): add metadata lookup shortcuts
This commit is contained in:
parent
e3bb4d2711
commit
34be160d96
|
|
@ -267,6 +267,15 @@ gitlink-cli issue +assigners --owner Gitlink --repo forgeplus
|
|||
|
||||
# List issue authors
|
||||
gitlink-cli issue +authors --owner Gitlink --repo forgeplus
|
||||
|
||||
# List issue priorities
|
||||
gitlink-cli issue +priorities --owner Gitlink --repo forgeplus
|
||||
|
||||
# List issue tags
|
||||
gitlink-cli issue +tags --owner Gitlink --repo forgeplus --only-name
|
||||
|
||||
# List issue statuses
|
||||
gitlink-cli issue +statuses --owner Gitlink --repo forgeplus
|
||||
```
|
||||
|
||||
### Label Management
|
||||
|
|
|
|||
|
|
@ -278,6 +278,15 @@ gitlink-cli issue +assigners --owner Gitlink --repo forgeplus
|
|||
|
||||
# 列出 Issue 发布人
|
||||
gitlink-cli issue +authors --owner Gitlink --repo forgeplus
|
||||
|
||||
# 列出 Issue 优先级
|
||||
gitlink-cli issue +priorities --owner Gitlink --repo forgeplus
|
||||
|
||||
# 列出 Issue 标签
|
||||
gitlink-cli issue +tags --owner Gitlink --repo forgeplus --only-name
|
||||
|
||||
# 列出 Issue 状态
|
||||
gitlink-cli issue +statuses --owner Gitlink --repo forgeplus
|
||||
```
|
||||
|
||||
### 标签管理
|
||||
|
|
|
|||
|
|
@ -266,6 +266,81 @@ func Shortcuts() []*common.Shortcut {
|
|||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "priorities",
|
||||
Description: "List issue priorities",
|
||||
Flags: []common.Flag{
|
||||
{Name: "keyword", Short: "k", Usage: "Search keyword"},
|
||||
},
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
q := url.Values{}
|
||||
if keyword := ctx.Arg("keyword"); keyword != "" {
|
||||
q.Set("keyword", keyword)
|
||||
}
|
||||
env, err := ctx.CallAPIWithQuery("GET", v1RepoPath(ctx)+"/issue_priorities", q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "tags",
|
||||
Description: "List issue tags",
|
||||
Flags: []common.Flag{
|
||||
{Name: "keyword", Short: "k", Usage: "Search keyword"},
|
||||
{Name: "only-name", Usage: "Only return tag names and IDs", Bool: true, Default: "false"},
|
||||
{Name: "order-by", Usage: "Order by: updated_on, created_on, issues_count"},
|
||||
{Name: "order-direction", Usage: "Order direction: asc or desc"},
|
||||
},
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
q := url.Values{}
|
||||
if keyword := ctx.Arg("keyword"); keyword != "" {
|
||||
q.Set("keyword", keyword)
|
||||
}
|
||||
if parseBool(ctx.Arg("only-name")) {
|
||||
q.Set("only_name", "true")
|
||||
}
|
||||
if orderBy := ctx.Arg("order-by"); orderBy != "" {
|
||||
q.Set("order_by", orderBy)
|
||||
}
|
||||
if orderDirection := ctx.Arg("order-direction"); orderDirection != "" {
|
||||
q.Set("order_direction", orderDirection)
|
||||
}
|
||||
env, err := ctx.CallAPIWithQuery("GET", v1RepoPath(ctx)+"/issue_tags", q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "statuses",
|
||||
Description: "List issue statuses",
|
||||
Flags: []common.Flag{
|
||||
{Name: "page", Short: "p", Usage: "Page number", Default: "1"},
|
||||
{Name: "limit", Short: "l", Usage: "Items per page", Default: "20"},
|
||||
},
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
q := url.Values{}
|
||||
q.Set("page", ctx.Arg("page"))
|
||||
q.Set("limit", ctx.Arg("limit"))
|
||||
env, err := ctx.CallAPIWithQuery("GET", v1RepoPath(ctx)+"/issue_statues", q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -470,6 +470,78 @@ func TestBatchCloseWithFailedClose(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// --- metadata lookup shortcuts ---
|
||||
|
||||
func TestIssuePrioritiesShortcut(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "GET" || r.URL.Path != "/v1/owner/repo/issue_priorities.json" {
|
||||
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
|
||||
}
|
||||
assertEqual(t, r.URL.Query().Get("keyword"), "normal")
|
||||
writeJSON(w, []map[string]interface{}{
|
||||
{"id": 2, "name": "Normal"},
|
||||
})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runShortcut(t, server, "priorities", map[string]string{"keyword": "normal"})
|
||||
if err != nil {
|
||||
t.Fatalf("priorities shortcut failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssueTagsShortcutWithFilters(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "GET" || r.URL.Path != "/v1/owner/repo/issue_tags.json" {
|
||||
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
|
||||
}
|
||||
query := r.URL.Query()
|
||||
assertEqual(t, query.Get("keyword"), "bug")
|
||||
assertEqual(t, query.Get("only_name"), "true")
|
||||
assertEqual(t, query.Get("order_by"), "issues_count")
|
||||
assertEqual(t, query.Get("order_direction"), "desc")
|
||||
writeJSON(w, map[string]interface{}{
|
||||
"total_count": 1,
|
||||
"issue_tags": []map[string]interface{}{
|
||||
{"id": 3, "name": "bug"},
|
||||
},
|
||||
})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runShortcut(t, server, "tags", map[string]string{
|
||||
"keyword": "bug",
|
||||
"only-name": "true",
|
||||
"order-by": "issues_count",
|
||||
"order-direction": "desc",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("tags shortcut failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssueStatusesShortcut(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "GET" || r.URL.Path != "/v1/owner/repo/issue_statues.json" {
|
||||
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
|
||||
}
|
||||
assertEqual(t, r.URL.Query().Get("page"), "2")
|
||||
assertEqual(t, r.URL.Query().Get("limit"), "10")
|
||||
writeJSON(w, map[string]interface{}{
|
||||
"total_count": 1,
|
||||
"statues": []map[string]interface{}{
|
||||
{"id": 1, "name": "Open"},
|
||||
},
|
||||
})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runShortcut(t, server, "statuses", map[string]string{"page": "2", "limit": "10"})
|
||||
if err != nil {
|
||||
t.Fatalf("statuses shortcut failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// --- HTTP error paths ---
|
||||
|
||||
func TestIssueListHTTPError(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ metadata:
|
|||
| `issue +comment` | 添加评论 | 是 |
|
||||
| `issue +assigners` | 查询 Issue 负责人列表 | 否(公开项目) |
|
||||
| `issue +authors` | 查询 Issue 发布人列表 | 否(公开项目) |
|
||||
| `issue +statuses` | 查询 Issue 状态列表 | 否(公开项目) |
|
||||
| `issue +tags` | 查询 Issue 标签列表 | 否(公开项目) |
|
||||
| `issue +priorities` | 查询 Issue 优先级列表 | 否(公开项目) |
|
||||
|
||||
## 使用示例
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
# Issue Priorities
|
||||
|
||||
Use `issue +priorities` to list the available Issue priority values for a repository.
|
||||
This is useful before creating or updating an Issue that needs a specific `priority_id`.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
gitlink-cli issue +priorities --owner Gitlink --repo forgeplus
|
||||
gitlink-cli issue +priorities --owner Gitlink --repo forgeplus --keyword normal
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--owner` | Repository owner |
|
||||
| `--repo` | Repository name |
|
||||
| `--keyword`, `-k` | Optional search keyword |
|
||||
|
||||
## API
|
||||
|
||||
```http
|
||||
GET /api/v1/{owner}/{repo}/issue_priorities.json
|
||||
```
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
# Issue Statuses
|
||||
|
||||
Use `issue +statuses` to list available Issue status values for a repository.
|
||||
This helps users find `status_id` values before creating reports or updating Issues.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
gitlink-cli issue +statuses --owner Gitlink --repo forgeplus
|
||||
gitlink-cli issue +statuses --owner Gitlink --repo forgeplus --page 1 --limit 50
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--owner` | Repository owner |
|
||||
| `--repo` | Repository name |
|
||||
| `--page`, `-p` | Page number |
|
||||
| `--limit`, `-l` | Items per page |
|
||||
|
||||
## API
|
||||
|
||||
```http
|
||||
GET /api/v1/{owner}/{repo}/issue_statues.json
|
||||
```
|
||||
|
||||
The API path and response field use `statues` as documented by GitLink OpenAPI.
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
# Issue Tags
|
||||
|
||||
Use `issue +tags` to list the Issue tags available in a repository.
|
||||
This helps users find tag IDs before creating or updating Issues.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
gitlink-cli issue +tags --owner Gitlink --repo forgeplus
|
||||
gitlink-cli issue +tags --owner Gitlink --repo forgeplus --only-name --keyword bug
|
||||
gitlink-cli issue +tags --owner Gitlink --repo forgeplus --order-by issues_count --order-direction desc
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--owner` | Repository owner |
|
||||
| `--repo` | Repository name |
|
||||
| `--keyword`, `-k` | Optional search keyword |
|
||||
| `--only-name` | Return only tag names and IDs |
|
||||
| `--order-by` | Sort field: `updated_on`, `created_on`, or `issues_count` |
|
||||
| `--order-direction` | Sort direction: `asc` or `desc` |
|
||||
|
||||
## API
|
||||
|
||||
```http
|
||||
GET /api/v1/{owner}/{repo}/issue_tags.json
|
||||
```
|
||||
|
||||
Query parameters are mapped as `keyword`, `only_name`, `order_by`, and `order_direction`.
|
||||
Loading…
Reference in New Issue