applyAIProviderKeyOps reconciles a provider's keys against the supplied mutation list inside a transaction: kept-by-ID rows stay, rows whose ID is absent from the list are deleted, and entries carrying a plaintext APIKey are inserted as new rows. Caller is responsible for prior validation (XOR per e
(ctx context.Context, tx database.Store, providerID uuid.UUID, muts []codersdk.AIProviderKeyMutation)
| 677 | // responsible for prior validation (XOR per entry, no duplicate IDs). |
| 678 | // IDs that do not belong to this provider return errAIProviderKeyUnknown. |
| 679 | func applyAIProviderKeyOps(ctx context.Context, tx database.Store, providerID uuid.UUID, muts []codersdk.AIProviderKeyMutation) ([]database.AIProviderKey, aiProviderKeyOpsAudit, aiProviderKeyChanges, error) { |
| 680 | var ( |
| 681 | ops aiProviderKeyOpsAudit |
| 682 | changes aiProviderKeyChanges |
| 683 | ) |
| 684 | existing, err := tx.GetAIProviderKeysByProviderID(ctx, providerID) |
| 685 | if err != nil { |
| 686 | return nil, ops, changes, xerrors.Errorf("load existing ai provider keys: %w", err) |
| 687 | } |
| 688 | existingByID := make(map[uuid.UUID]struct{}, len(existing)) |
| 689 | for _, k := range existing { |
| 690 | existingByID[k.ID] = struct{}{} |
| 691 | } |
| 692 | |
| 693 | keep := make(map[uuid.UUID]struct{}, len(muts)) |
| 694 | var inserts []string |
| 695 | for _, m := range muts { |
| 696 | switch { |
| 697 | case m.ID != nil: |
| 698 | if _, ok := existingByID[*m.ID]; !ok { |
| 699 | return nil, ops, changes, xerrors.Errorf("%w: %s", errAIProviderKeyUnknown, *m.ID) |
| 700 | } |
| 701 | keep[*m.ID] = struct{}{} |
| 702 | case m.APIKey != nil: |
| 703 | inserts = append(inserts, *m.APIKey) |
| 704 | } |
| 705 | } |
| 706 | |
| 707 | for _, k := range existing { |
| 708 | if _, ok := keep[k.ID]; ok { |
| 709 | continue |
| 710 | } |
| 711 | if err := tx.DeleteAIProviderKey(ctx, k.ID); err != nil { |
| 712 | return nil, ops, changes, xerrors.Errorf("delete ai provider key %s: %w", k.ID, err) |
| 713 | } |
| 714 | ops.Removed = append(ops.Removed, aiProviderKeyOp{ID: k.ID, Masked: aibridgeutils.MaskSecret(k.APIKey)}) |
| 715 | changes.Removed = append(changes.Removed, k) |
| 716 | } |
| 717 | |
| 718 | added, err := insertAIProviderKeys(ctx, tx, providerID, inserts) |
| 719 | if err != nil { |
| 720 | return nil, ops, changes, err |
| 721 | } |
| 722 | for _, k := range added { |
| 723 | ops.Added = append(ops.Added, aiProviderKeyOp{ID: k.ID, Masked: aibridgeutils.MaskSecret(k.APIKey)}) |
| 724 | } |
| 725 | changes.Added = append(changes.Added, added...) |
| 726 | ops.Kept = len(keep) |
| 727 | |
| 728 | out, err := tx.GetAIProviderKeysByProviderID(ctx, providerID) |
| 729 | if err != nil { |
| 730 | return nil, ops, changes, xerrors.Errorf("reload ai provider keys: %w", err) |
| 731 | } |
| 732 | return out, ops, changes, nil |
| 733 | } |
| 734 | |
| 735 | // errAIProviderKeyUnknown is the sentinel returned by |
| 736 | // applyAIProviderKeyOps when a mutation references an ID that does not |
no test coverage detected