Skip to content

Add tags and teams to User struct and hydrate them in ListUsers#614

Merged
saditya370 merged 3 commits intomainfrom
feat/user-tags-teams
May 5, 2026
Merged

Add tags and teams to User struct and hydrate them in ListUsers#614
saditya370 merged 3 commits intomainfrom
feat/user-tags-teams

Conversation

@saditya370
Copy link
Copy Markdown
Contributor

Summary

Adds Tags *TagConnection and Teams *TeamIdConnection to the User struct so ListUsers returns tag and team-membership data inline, mirroring the precedent on Service. Without this, callers (notably the OpsLevel MCP server) cannot answer questions like "which team does this user belong to?" or "which users are tagged as on-call leads?" without a follow-up query per user.

Changes

object.go

User gains two pointer-typed connection fields:

  • Tags *TagConnection (json:"tags,omitempty")
  • Teams *TeamIdConnection (json:"teams,omitempty")

These match the pattern used on Service.Tags. The graphql library auto-generates the inline selection set from the struct, so ListUsers now requests tags{nodes{id,key,value},pageInfo} and teams{nodes{alias,id},pageInfo} per user node.

user.go

  • Renamed User.Teams(...)User.GetTeams(...) to free the Teams field name. The previous method name collided with the new struct field. Aligns naming with UserId.GetTags.
  • New User.Hydrate(client) follows pagination on the embedded Tags and Teams connections when their inner pageInfo.hasNextPage is true. Mirrors Service.Hydrate.
  • ListUsers now invokes Hydrate on every node from the initial query — not just nodes merged from recursive list pages. This closes a silent-truncation bug: previously, if every user fit on the first outer page (the common case for small accounts), no user ever had its inner tag/team pagination followed, capping users at the schema-default 500 nodes per inner connection without a follow-up.

Tests

user_test.go

  • Updated 7 hardcoded query-string assertions to include the new tags/teams selections under each user node.
  • New TestListUserPopulatesTagsAndTeams — asserts the inline path works: a user returned from ListUsers with embedded tags+teams JSON gets those fields populated on the Go struct.
  • New TestListUserHydratesFirstPageInnerConnections — regression for the first-page hydration fix. Sets tags.pageInfo.hasNextPage=true on a user returned in the first outer page and asserts a follow-up UserTagsList query is issued and the merged tag list is returned.

team_test.go, cache_test.go, actions_test.go, clientGQL_test.go

Every test that selects User as a sub-field (Team.Manager, deletedMembers) now expects tags{...} and teams{...} inside the manager{...} (or equivalent) selection. Same pattern in all four files.

testdata/templates/query/team/get.tpl

The shared team_get template's manager{...} block was extended with tags and teams connection selections to match the new User shape.

Notes

  • TeamIdConnection (not TeamConnection) is used for the inline selection — minimal {alias, id} per team. Team name is intentionally not added; most callers can use the alias as a stable identifier.
  • The unparameterized tags{...} / teams{...} selections rely on the schema's default_page_size: 100 / default_max_page_size: 500. Hydrate follows hasNextPage for both, so power users with overflowing tags/teams are still fully populated.

Test plan

  • go test ./... passes
  • TestListUserPopulatesTagsAndTeams covers the inline path
  • TestListUserHydratesFirstPageInnerConnections covers the first-page hydration fix
  • All existing user/team/cache/actions/clientGQL test query strings updated and passing

@saditya370 saditya370 requested a review from andrewstillv15 May 4, 2026 11:21
@saditya370 saditya370 self-assigned this May 4, 2026
Comment thread user.go Outdated
}

func (user *User) Teams(client *Client, variables *PayloadVariables) (*TeamIdConnection, error) {
func (user *User) GetTeams(client *Client, variables *PayloadVariables) (*TeamIdConnection, error) {
Copy link
Copy Markdown
Contributor

@andrewstillv15 andrewstillv15 May 5, 2026

Choose a reason for hiding this comment

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

Can we do this without changing this method name? We'll have update the major version we change this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@andrewstillv15 Renamed the struct field to TeamsConnection with an explicit graphql:"teams" tag instead, which keeps User.Teams intact

Comment thread user.go
return &q.Account.User.Teams, nil
}

func (user *User) Hydrate(client *Client) error {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit

Suggested change
func (user *User) Hydrate(client *Client) error {
func (user *User) hydrate(client *Client) error {

@saditya370 saditya370 merged commit 6b82611 into main May 5, 2026
4 checks passed
@saditya370 saditya370 deleted the feat/user-tags-teams branch May 5, 2026 20:10
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.

2 participants