@Summary Get users @ID get-users @Security CoderSessionToken @Produce json @Tags Users @Param q query string false "Search query" @Param after_id query string false "After ID" format(uuid) @Param limit query int false "Page limit" @Param offset query int false "Page offset" @Success 200 {object} cod
(rw http.ResponseWriter, r *http.Request)
| 314 | // @Success 200 {object} codersdk.GetUsersResponse |
| 315 | // @Router /api/v2/users [get] |
| 316 | func (api *API) users(rw http.ResponseWriter, r *http.Request) { |
| 317 | ctx := r.Context() |
| 318 | users, userCount, ok := api.GetUsers(rw, r) |
| 319 | if !ok { |
| 320 | return |
| 321 | } |
| 322 | |
| 323 | userIDs := make([]uuid.UUID, 0, len(users)) |
| 324 | for _, user := range users { |
| 325 | userIDs = append(userIDs, user.ID) |
| 326 | } |
| 327 | organizationIDsByMemberIDsRows, err := api.Database.GetOrganizationIDsByMemberIDs(ctx, userIDs) |
| 328 | if xerrors.Is(err, sql.ErrNoRows) { |
| 329 | err = nil |
| 330 | } |
| 331 | if err != nil { |
| 332 | httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ |
| 333 | Message: "Internal error fetching user's organizations.", |
| 334 | Detail: err.Error(), |
| 335 | }) |
| 336 | return |
| 337 | } |
| 338 | organizationIDsByUserID := map[uuid.UUID][]uuid.UUID{} |
| 339 | for _, organizationIDsByMemberIDsRow := range organizationIDsByMemberIDsRows { |
| 340 | organizationIDsByUserID[organizationIDsByMemberIDsRow.UserID] = organizationIDsByMemberIDsRow.OrganizationIDs |
| 341 | } |
| 342 | |
| 343 | var aiSeatSet map[uuid.UUID]struct{} |
| 344 | if api.Entitlements.Enabled(codersdk.FeatureAIGovernanceUserLimit) { |
| 345 | var aiSeatUserIDs []uuid.UUID |
| 346 | //nolint:gocritic // AI seat state is a system-level read gated by entitlement. |
| 347 | aiSeatUserIDs, err = api.Database.GetUserAISeatStates(dbauthz.AsSystemRestricted(ctx), userIDs) |
| 348 | if err != nil { |
| 349 | if !xerrors.Is(err, sql.ErrNoRows) { |
| 350 | api.Logger.Warn( |
| 351 | ctx, |
| 352 | "failed to fetch AI seat states for users", |
| 353 | slog.F("user_count", len(userIDs)), |
| 354 | slog.Error(err), |
| 355 | ) |
| 356 | } |
| 357 | aiSeatUserIDs = nil |
| 358 | } |
| 359 | |
| 360 | aiSeatSet = make(map[uuid.UUID]struct{}, len(aiSeatUserIDs)) |
| 361 | for _, uid := range aiSeatUserIDs { |
| 362 | aiSeatSet[uid] = struct{}{} |
| 363 | } |
| 364 | } |
| 365 | |
| 366 | httpapi.Write(ctx, rw, http.StatusOK, codersdk.GetUsersResponse{ |
| 367 | Users: convertUsers(users, organizationIDsByUserID, aiSeatSet), |
| 368 | Count: int(userCount), |
| 369 | }) |
| 370 | } |
| 371 | |
| 372 | func (api *API) GetUsers(rw http.ResponseWriter, r *http.Request) ([]database.User, int64, bool) { |
| 373 | ctx := r.Context() |
nothing calls this directly
no test coverage detected