Skip to content

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

Open
FenjuFu wants to merge 1 commit into
mainfrom
feat/skills-list-labels
Open

feat(api): return skill labels from the skill listing endpoints on request#730
FenjuFu wants to merge 1 commit 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
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

2 participants