Skip to content

feat(api): return skill labels from the skill listing endpoints on request - #730

Merged
XiaoSeS merged 3 commits into
mainfrom
feat/skills-list-labels
Aug 25, 2026
Merged

feat(api): return skill labels from the skill listing endpoints on request#730
XiaoSeS merged 3 commits into
mainfrom
feat/skills-list-labels

Conversation

@FenjuFu

@FenjuFu FenjuFu commented Aug 19, 2026

Copy link
Copy Markdown
Member

Summary

  • What changed? GET /api/v1/skills and GET /api/web/skills accept includeLabels=true and return each skill's labels inline.
  • Why is this needed? Closes [Feature] /api/v1/skills API can return skill labels #710. Labels were reachable only one skill at a time through /api/{v1,web}/skills/{namespace}/{slug}/labels, so a client rendering a list had to issue a follow-up request per row.
curl -H "Authorization: Bearer $TOKEN" \
  "$SKILLHUB/api/v1/skills?limit=2&includeLabels=true"
{
  "items": [
    {
      "slug": "global/demo-skill",
      "displayName": "Demo Skill",
      "labels": [
        { "slug": "audited", "type": "PRIVILEGED", "displayName": "Audited" },
        { "slug": "automation", "type": "RECOMMENDED", "displayName": "Automation" }
      ]
    }
  ],
  "nextCursor": null
}

The issue named /api/v1/skills first and /api/web/skills as the alternative, so both surfaces take the parameter. The payload shape is the existing SkillLabelDto (slug, type, displayName), same as the per-skill labels endpoint, with the display name localized through LabelLocalizationService exactly as it is there.

The default response does not change

labels is null unless the caller opts in, and both records carry @JsonInclude(NON_NULL) on that component only — not on the record — so no other nullable field starts being dropped. Without includeLabels, responses are byte-identical to today, which matters for /api/v1/skills: it is the ClawHub compatibility surface with legacy clients on it.

SkillSummaryResponse and ClawHubSkillListResponse.SkillListItem each keep a constructor at the previous arity that delegates with null, so the seven existing construction sites are untouched.

One batch, not one per row

Naively reusing the per-skill path would issue three queries per row — 75 for a default page of 25. SkillLabelProjectionService resolves the whole page in three: assignments (findBySkillIdIn), definitions, translations. The unit test asserts that arity directly with verify(..., times(1)), so a regression to N+1 fails the build rather than quietly slowing the endpoint down.

Validation

  • Backend tests passed
  • Frontend typecheck/build passed (not applicable: no frontend changes)
  • OpenAPI SDK regenerated or checked when API contracts changed — the added parameter is annotated for the generated spec; no existing operation, path, or response field changed
  • Smoke test run when relevant (not applicable)

I could not run mvn test — no JDK is available on the machine this was written on, so CI is the first real execution. Tests are included and I would ask a reviewer to weight CI over my checklist here:

  • SkillLabelProjectionServiceTest — per-skill grouping, the type-then-slug ordering, assignments whose definition is missing, empty/null pages touching no repository, and the three-query batch arity.
  • SkillSearchControllerTestlabels absent by default, populated with includeLabels=true, and an empty array for a skill that has none.
  • ClawHubCompatAppServiceTest — the same two cases on the /api/v1/skills compatibility surface.

Risk

  • User-facing impact: additive. Callers that do not pass includeLabels see no change.
  • Deployment or migration impact: none. No schema or config change; skill_label and the label definition tables are read as they already are.
  • Rollback approach: revert the commit. Nothing persists the new flag.

Notes

  • Related issue: [Feature] /api/v1/skills API can return skill labels #710
  • Follow-up work: SkillLabelAppService.toDtos now overlaps with SkillLabelProjectionService.toDto. Folding the former into the projection service would mean changing SkillLabelAppService's constructor, which I left alone to keep this diff to the feature; worth doing as a separate cleanup.
  • Labels are projected from what the search layer already returned, so a skill the caller cannot see is not reachable through this parameter.

@CLAassistant

CLAassistant commented Aug 19, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

…quest

Skill labels were reachable only one skill at a time, through
/api/{v1,web}/skills/{namespace}/{slug}/labels, so a client rendering a list had
to issue a follow-up request per row.

Add includeLabels=true to GET /api/v1/skills and GET /api/web/skills. The labels
array is populated only when the parameter is set and left out of the payload
otherwise, so existing responses are byte-identical.

Labels for the whole page are resolved by SkillLabelProjectionService in three
queries — assignments, definitions, translations — rather than three per skill.

Closes #710

Signed-off-by: FenjuFu <fufenjupku@gmail.com>
@FenjuFu
FenjuFu force-pushed the feat/skills-list-labels branch 2 times, most recently from 24af499 to 3329900 Compare August 19, 2026 07:35

@XiaoSeS XiaoSeS left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

维护者建议调整 API 形状:请把当前 includeLabels=true 改成通用扩展参数 include=labels

原因:

  • 这是列表接口的可选关联展开能力,不应为每个可选关系增加一个布尔参数。
  • 后续如果还要展开 author、stats、versions 等字段,可以自然扩展为 include=labels,stats,API 兼容性更好。
  • 当前 PR 尚未合入,直接调整参数名不会破坏已发布行为。

建议实现边界:

  • 支持 GET /api/v1/skills?include=labels
  • 支持 GET /api/web/skills?include=labels
  • 默认不传 include 时响应保持现状,不返回 labels。
  • 当前只接受 labels;未知 include 值建议返回 400,避免调用方拼错参数后静默降级。
  • 同步更新测试和 OpenAPI 生成文件。

这个调整完成后,再按维护者验证流程做本地镜像/runtime 验证。

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
@FenjuFu

FenjuFu commented Aug 25, 2026

Copy link
Copy Markdown
Member Author

@XiaoSeS Thanks for pushing the API-shape follow-ups. I reviewed the current head fc448c5: both list endpoints now use include=labels, unsupported include values return 400 before search work begins, tests cover the parser/controller behavior, and the generated OpenAPI file is updated. All remote checks, DCO, and CLA are green. Your earlier changes-requested review is still the active review decision; could you re-review or dismiss it now that your two commits are on the branch?

@XiaoSeS XiaoSeS left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已按维护者建议完成调整并验证:\n\n- API 参数从 includeLabels=true 改为通用扩展参数 include=labels。\n- /api/v1/skills 与 /api/web/skills 默认响应保持不返回 labels。\n- include=labels 返回 labels 数组;未知 include 值返回 400。\n- 已补充 controller/parser/projection 测试并同步 OpenAPI 生成文件。\n- 本地源码验证、latest main + PR head 的 release Compose 运行态验证均通过。\n\n本轮 review 解除前置 changes-requested,后续可按维护者合并流程处理。

@XiaoSeS
XiaoSeS merged commit 91d0ae1 into main Aug 25, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] /api/v1/skills API can return skill labels

3 participants