validateAIProviderKeyMutations checks each entry has exactly one of ID or APIKey set, that plaintexts are non-empty after trimming, and that no ID is referenced twice in the same request. An empty slice itself is permitted (it clears all keys).
(muts []AIProviderKeyMutation)
| 372 | // that no ID is referenced twice in the same request. An empty slice |
| 373 | // itself is permitted (it clears all keys). |
| 374 | func validateAIProviderKeyMutations(muts []AIProviderKeyMutation) []ValidationError { |
| 375 | var validations []ValidationError |
| 376 | seen := make(map[uuid.UUID]int, len(muts)) |
| 377 | for i, m := range muts { |
| 378 | hasID := m.ID != nil |
| 379 | hasKey := m.APIKey != nil |
| 380 | switch { |
| 381 | case hasID == hasKey: |
| 382 | validations = append(validations, ValidationError{ |
| 383 | Field: fmt.Sprintf("api_keys[%d]", i), |
| 384 | Detail: "exactly one of id or api_key must be set", |
| 385 | }) |
| 386 | case hasKey && *m.APIKey == "": |
| 387 | validations = append(validations, ValidationError{ |
| 388 | Field: fmt.Sprintf("api_keys[%d].api_key", i), |
| 389 | Detail: "api_key must not be empty", |
| 390 | }) |
| 391 | case hasKey && strings.TrimSpace(*m.APIKey) != *m.APIKey: |
| 392 | validations = append(validations, ValidationError{ |
| 393 | Field: fmt.Sprintf("api_keys[%d].api_key", i), |
| 394 | Detail: "api_key must not contain leading or trailing whitespace", |
| 395 | }) |
| 396 | } |
| 397 | if hasID && !hasKey { |
| 398 | if prev, ok := seen[*m.ID]; ok { |
| 399 | validations = append(validations, ValidationError{ |
| 400 | Field: fmt.Sprintf("api_keys[%d].id", i), |
| 401 | Detail: fmt.Sprintf("id %s already referenced at api_keys[%d]", *m.ID, prev), |
| 402 | }) |
| 403 | } else { |
| 404 | seen[*m.ID] = i |
| 405 | } |
| 406 | } |
| 407 | } |
| 408 | return validations |
| 409 | } |
| 410 | |
| 411 | // AIProviders lists all (non-deleted) AI providers. |
| 412 | func (c *Client) AIProviders(ctx context.Context) ([]AIProvider, error) { |