From 0e758e60984e7592d6929a0a2b67c64b06af12fe Mon Sep 17 00:00:00 2001 From: Chet Nichols III Date: Tue, 19 May 2026 17:38:15 -0700 Subject: [PATCH] refactor: Migrate Tenant Account API handler to WithTx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Continues the `WithTx` migration, this time covering the `Create` handler in `tenantaccount.go` — the only `BeginTx` site there. Some callouts are: - Uses `WithTx` with outer-scope `ta` + `ssd` since the response needs both. Two distinct outputs make `WithTxResult` a poor fit here. - The validation reads (org infra provider, tenant lookup, existing tenant accounts) were already outside the tx and stay there; only the two writes are inside. - Pulled the `sdDAO` constructor up to handler scope so it's only built once. - Errors switched to `NewAPIError`. Signed-off-by: Chet Nichols III --- api/pkg/api/handler/tenantaccount.go | 77 ++++++++++++---------------- 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/api/pkg/api/handler/tenantaccount.go b/api/pkg/api/handler/tenantaccount.go index a8aecfefc..9acd48d58 100644 --- a/api/pkg/api/handler/tenantaccount.go +++ b/api/pkg/api/handler/tenantaccount.go @@ -18,7 +18,6 @@ package handler import ( - "database/sql" "encoding/json" "fmt" "net/http" @@ -182,52 +181,44 @@ func (ctah CreateTenantAccountHandler) Handle(c echo.Context) error { // Generate a unique account number accountNumber := common.GenerateAccountNumber() - // Start a db tx - tx, err := cdb.BeginTx(ctx, ctah.dbSession, &sql.TxOptions{}) - if err != nil { - logger.Error().Err(err).Msg("unable to start transaction") - return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Error creating Tenant Account", nil) - } - txCommitted := false - defer common.RollbackTx(ctx, tx, &txCommitted) - - ta, err := taDAO.Create(ctx, tx, cdbm.TenantAccountCreateInput{ - AccountNumber: accountNumber, - TenantID: tenantID, - TenantOrg: *tenantOrg, - InfrastructureProviderID: ip.ID, - InfrastructureProviderOrg: ip.Org, - Status: cdbm.TenantAccountStatusInvited, - CreatedBy: dbUser.ID, - }) - if err != nil { - logger.Error().Err(err).Msg("error creating TenantAccount in DB") - // rollback transaction - return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to create tenant account", nil) - } - - // Create a status detail record for the tenantaccount sdDAO := cdbm.NewStatusDetailDAO(ctah.dbSession) - ssd, serr := sdDAO.CreateFromParams(ctx, tx, ta.ID.String(), *cdb.GetStrPtr(cdbm.TenantAccountStatusInvited), - cdb.GetStrPtr("received tenant account creation request, pending accept")) - if serr != nil { - logger.Error().Err(serr).Msg("error creating Status Detail DB entry") - // rollback transaction - return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to create Status Detail for TenantAccount", nil) - } - if ssd == nil { - logger.Error().Msg("Status Detail DB entry not returned from CreateFromParams") - return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to get new Status Detail for TenantAccount", nil) - } - // Commit transaction - err = tx.Commit() + var ta *cdbm.TenantAccount + var ssd *cdbm.StatusDetail + + err = cdb.WithTx(ctx, ctah.dbSession, func(tx *cdb.Tx) error { + var derr error + ta, derr = taDAO.Create(ctx, tx, cdbm.TenantAccountCreateInput{ + AccountNumber: accountNumber, + TenantID: tenantID, + TenantOrg: *tenantOrg, + InfrastructureProviderID: ip.ID, + InfrastructureProviderOrg: ip.Org, + Status: cdbm.TenantAccountStatusInvited, + CreatedBy: dbUser.ID, + }) + if derr != nil { + logger.Error().Err(derr).Msg("error creating TenantAccount in DB") + return cutil.NewAPIError(http.StatusInternalServerError, "Failed to create tenant account", nil) + } + + // Create a status detail record for the tenantaccount + ssd, derr = sdDAO.CreateFromParams(ctx, tx, ta.ID.String(), *cdb.GetStrPtr(cdbm.TenantAccountStatusInvited), + cdb.GetStrPtr("received tenant account creation request, pending accept")) + if derr != nil { + logger.Error().Err(derr).Msg("error creating Status Detail DB entry") + return cutil.NewAPIError(http.StatusInternalServerError, "Failed to create Status Detail for TenantAccount", nil) + } + if ssd == nil { + logger.Error().Msg("Status Detail DB entry not returned from CreateFromParams") + return cutil.NewAPIError(http.StatusInternalServerError, "Failed to get new Status Detail for TenantAccount", nil) + } + + return nil + }) if err != nil { - logger.Error().Err(err).Msg("error committing subnet transaction to DB") - return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to create Tenant Account", nil) + return common.HandleTxError(c, logger, err, "Failed to create Tenant Account, DB transaction error") } - // set committed so, deferred cleanup functions will do nothing - txCommitted = true // Create response apiInstance := model.NewAPITenantAccount(ta, []cdbm.StatusDetail{*ssd}, 0)