@Summary Update an AI provider @ID update-an-ai-provider @Security CoderSessionToken @Accept json @Produce json @Tags AI Providers @Param idOrName path string true "Provider ID or name" @Param request body codersdk.UpdateAIProviderRequest true "Update AI provider request" @Success 200 {object} coder
(rw http.ResponseWriter, r *http.Request)
| 261 | // @Success 200 {object} codersdk.AIProvider |
| 262 | // @Router /api/v2/ai/providers/{idOrName} [patch] |
| 263 | func (api *API) aiProvidersUpdate(rw http.ResponseWriter, r *http.Request) { |
| 264 | // keyOpsAudit attaches per-key add/remove/keep counts to the audit |
| 265 | // entry. Keys live in a separate table, so a key-only PATCH would |
| 266 | // otherwise produce an empty diff and hide rotation from the log. |
| 267 | keyOpsAudit := &aiProviderKeyOpsAudit{} |
| 268 | var ( |
| 269 | ctx = r.Context() |
| 270 | auditor = api.Auditor.Load() |
| 271 | aReq, commitAudit = audit.InitRequest[database.AIProvider](rw, &audit.RequestParams{ |
| 272 | Audit: *auditor, |
| 273 | Log: api.Logger, |
| 274 | Request: r, |
| 275 | Action: database.AuditActionWrite, |
| 276 | AdditionalFields: keyOpsAudit, |
| 277 | }) |
| 278 | ) |
| 279 | defer commitAudit() |
| 280 | |
| 281 | var req codersdk.UpdateAIProviderRequest |
| 282 | if !httpapi.Read(ctx, rw, r, &req) { |
| 283 | return |
| 284 | } |
| 285 | |
| 286 | if req.IsEmpty() { |
| 287 | httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ |
| 288 | Message: "At least one field must be provided.", |
| 289 | }) |
| 290 | return |
| 291 | } |
| 292 | if validations := req.Validate(); len(validations) > 0 { |
| 293 | httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ |
| 294 | Message: "Invalid AI provider request.", |
| 295 | Validations: validations, |
| 296 | }) |
| 297 | return |
| 298 | } |
| 299 | |
| 300 | idOrName := chi.URLParam(r, "idOrName") |
| 301 | |
| 302 | var ( |
| 303 | updated database.AIProvider |
| 304 | keys []database.AIProviderKey |
| 305 | keyChanges aiProviderKeyChanges |
| 306 | ) |
| 307 | err := api.Database.InTx(func(tx database.Store) error { |
| 308 | old, err := lookupAIProvider(ctx, tx, idOrName) |
| 309 | if err != nil { |
| 310 | return err |
| 311 | } |
| 312 | aReq.Old = old |
| 313 | |
| 314 | // Decode the existing settings to merge with the patch. The dbcrypt |
| 315 | // wrapper has already decrypted the blob for us. |
| 316 | existing, err := db2sdk.AIProviderSettings(old.Settings) |
| 317 | if err != nil { |
| 318 | return xerrors.Errorf("decode existing settings: %w", err) |
| 319 | } |
| 320 | if req.Settings != nil { |
nothing calls this directly
no test coverage detected