feat(list): label/member +list 补齐分页与 --all;member list 改走 v1 端点修复非管理员 403
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
343c5494fb
commit
4435dd251d
|
|
@ -16,15 +16,24 @@
|
|||
- 遵循 `total_count`:达到总数即停止;另设最大页数护栏,防止
|
||||
忽略 `page` 参数的端点造成死循环。
|
||||
- `PaginateAll` 保持原签名,委托给 `PaginateAllKey`。
|
||||
- 九个分页 list 命令新增 `--all` 布尔参数(默认 false):
|
||||
- 十一个分页 list 命令新增 `--all` 布尔参数(默认 false):
|
||||
- `issue +list --all`(合并结果同样应用 number/database_id 规范化)
|
||||
- `pr +list --all`、`branch +list --all`、`release +list --all`
|
||||
- `milestone +list --all`、`org +list --all`、`repo +list --all`
|
||||
- `search +repos --all`、`search +users --all`
|
||||
- `label +list --all`、`member +list --all`
|
||||
- 对应资源键:`issues`/`pulls`/`branches`/`releases`/`milestones`/
|
||||
`organizations`/`projects`/`users`(均生产实测确认)
|
||||
`organizations`/`projects`/`users`/`issue_tags`/`collaborators`
|
||||
(均生产实测确认)
|
||||
- 输出与单页响应同构:`{"total_count": N, "<资源名>": [...]}`。
|
||||
- 总数字段兼容 `total_count` 与 `count`(如 `/users/:login/projects`)。
|
||||
- 修复两个既有分页语义缺口(均生产实测确认端点本身分页):
|
||||
- `label +list` 完全没有 `--page/--limit`(端点实际返回 `total_count`),现已补齐;
|
||||
- `member +list` 既无分页又走遗留路径(非管理员直接 403),现改走
|
||||
`/v1/:owner/:repo/collaborators`(支持分页且普通成员可读)。
|
||||
- 未加 `--all` 的 list 端点均经生产验证为非分页或未部署:
|
||||
`licenses`/`ignores` 返回全量数组;`pm/pipelines` 404 未部署;
|
||||
`pipeline +runs` 用 `total_data` 非标准包裹;dataset 端点未部署(平台 issue #144255)。
|
||||
- 中英文 i18n 新增 `flag.all` 文案。
|
||||
|
||||
## 命令示例
|
||||
|
|
|
|||
|
|
@ -32,16 +32,28 @@ func Shortcuts() []*common.Shortcut {
|
|||
{Name: "only-name", Usage: "Return only label id and name: true or false"},
|
||||
{Name: "sort-by", Usage: "Sort field: updated_on, created_on, issues_count"},
|
||||
{Name: "sort-direction", Usage: "Sort direction: asc or desc"},
|
||||
{Name: "page", Short: "p", Usage: "Page number", Default: "1"},
|
||||
{Name: "limit", Short: "l", Usage: "Items per page", Default: "20"},
|
||||
{Name: "all", Usage: "Fetch all pages automatically (ignores --page)", Bool: true, Default: "false"},
|
||||
},
|
||||
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"))
|
||||
setQueryIfPresent(q, "keyword", ctx.Arg("keyword"))
|
||||
setQueryIfPresent(q, "only_name", ctx.Arg("only-name"))
|
||||
setQueryIfPresent(q, "order_by", ctx.Arg("sort-by"))
|
||||
setQueryIfPresent(q, "order_direction", ctx.Arg("sort-direction"))
|
||||
if ctx.Arg("all") == "true" {
|
||||
items, err := ctx.PaginateAllKey(labelPath(ctx), q, "issue_tags")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(common.NewListEnvelope("issue_tags", items))
|
||||
}
|
||||
env, err := ctx.CallAPIWithQuery("GET", labelPath(ctx), q)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -26,11 +26,26 @@ func Shortcuts() []*common.Shortcut {
|
|||
{
|
||||
Name: "list",
|
||||
Description: "List repository members",
|
||||
Flags: []common.Flag{
|
||||
{Name: "page", Short: "p", Usage: "Page number", Default: "1"},
|
||||
{Name: "limit", Short: "l", Usage: "Items per page", Default: "20"},
|
||||
{Name: "all", Usage: "Fetch all pages automatically (ignores --page)", Bool: true, Default: "false"},
|
||||
},
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
env, err := ctx.CallAPI("GET", collaboratorsPath(ctx), nil)
|
||||
q := url.Values{}
|
||||
q.Set("page", ctx.Arg("page"))
|
||||
q.Set("limit", ctx.Arg("limit"))
|
||||
if ctx.Arg("all") == "true" {
|
||||
items, err := ctx.PaginateAllKey(collaboratorsV1Path(ctx), q, "collaborators")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(common.NewListEnvelope("collaborators", items))
|
||||
}
|
||||
env, err := ctx.CallAPIWithQuery("GET", collaboratorsV1Path(ctx), q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -254,6 +269,12 @@ func collaboratorsPath(ctx *common.RuntimeContext) string {
|
|||
return fmt.Sprintf("/%s/%s/collaborators", ctx.Owner, ctx.Repo)
|
||||
}
|
||||
|
||||
// collaboratorsV1Path is the v1 read endpoint, which supports pagination and
|
||||
// does not require admin permission (the legacy path rejects non-admins).
|
||||
func collaboratorsV1Path(ctx *common.RuntimeContext) string {
|
||||
return fmt.Sprintf("/v1/%s/%s/collaborators", ctx.Owner, ctx.Repo)
|
||||
}
|
||||
|
||||
func collaboratorsRemovePath(ctx *common.RuntimeContext) string {
|
||||
return fmt.Sprintf("%s/remove", collaboratorsPath(ctx))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ import (
|
|||
|
||||
func TestMemberList(t *testing.T) {
|
||||
server := newMemberTestServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
assertRequest(t, r, "GET", "/owner/repo/collaborators.json")
|
||||
writeJSON(t, w, map[string]interface{}{"total_count": 1, "members": []interface{}{}})
|
||||
assertRequest(t, r, "GET", "/v1/owner/repo/collaborators.json")
|
||||
writeJSON(t, w, map[string]interface{}{"total_count": 1, "collaborators": []interface{}{}})
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue