2026-08-26 06:41:07 +08:00
package opsconfig
import (
"context"
"encoding/json"
"errors"
"fmt"
"regexp"
"strings"
"time"
"data-terminal-cloud/internal/audit"
"data-terminal-cloud/internal/idempotency"
"data-terminal-cloud/internal/identity"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
)
var (
ErrInvalid = errors . New ( "invalid runtime configuration" )
ErrAccessDenied = errors . New ( "operations access denied" )
ErrNotFound = errors . New ( "runtime configuration not found" )
ErrStateConflict = errors . New ( "runtime configuration state conflict" )
ErrVersionConflict = errors . New ( "runtime configuration version conflict" )
ErrSelfApproval = errors . New ( "configuration creator cannot approve own change" )
)
var referencePattern = regexp . MustCompile ( `^[A-Za-z][A-Za-z0-9_.:/-]{2,199}$` )
type Service struct {
pool * pgxpool . Pool
audit * audit . Repository
idem * idempotency . Executor
}
type CreateInput struct {
TenantID string `json:"tenant_id"`
ConfigKey string `json:"config_key"`
DisplayName string `json:"display_name"`
ConfigKind string `json:"config_kind"`
Payload map [ string ] any `json:"payload"`
SecretRefs map [ string ] string `json:"secret_refs"`
ChangeSummary string `json:"change_summary"`
}
type Version struct {
ID string `json:"id"`
TenantID string `json:"tenant_id"`
ConfigKey string `json:"config_key"`
DisplayName string `json:"display_name"`
ConfigKind string `json:"config_kind"`
VersionNo int `json:"version_no"`
Status string `json:"status"`
Payload map [ string ] any `json:"payload"`
SecretRefs map [ string ] string `json:"secret_refs"`
ChangeSummary string `json:"change_summary"`
ValidationResult map [ string ] any `json:"validation_result"`
CreatedBy string `json:"created_by"`
SubmittedBy * string `json:"submitted_by,omitempty"`
SubmittedAt * time . Time `json:"submitted_at,omitempty"`
ApprovedBy * string `json:"approved_by,omitempty"`
ApprovedAt * time . Time `json:"approved_at,omitempty"`
PublishedBy * string `json:"published_by,omitempty"`
PublishedAt * time . Time `json:"published_at,omitempty"`
RetiredAt * time . Time `json:"retired_at,omitempty"`
SupersedesID * string `json:"supersedes_id,omitempty"`
RollbackOfID * string `json:"rollback_of_id,omitempty"`
RowVersion int64 `json:"row_version"`
CreatedAt time . Time `json:"created_at"`
UpdatedAt time . Time `json:"updated_at"`
}
type actor struct {
tenantID string
tenantPublicID string
userID string
userPublicID string
role string
}
func NewService ( pool * pgxpool . Pool , auditRepository * audit . Repository ) ( * Service , error ) {
if pool == nil || auditRepository == nil {
return nil , errors . New ( "operations configuration database and audit repository are required" )
}
return & Service { pool : pool , audit : auditRepository , idem : idempotency . NewExecutor ( pool )}, nil
}
func ( s * Service ) Create ( ctx context . Context , principal identity . Principal , key , requestID string , input CreateInput ) ( Version , bool , error ) {
input . ConfigKey = strings . TrimSpace ( strings . ToLower ( input . ConfigKey ))
input . DisplayName = strings . TrimSpace ( input . DisplayName )
input . ConfigKind = strings . TrimSpace ( strings . ToUpper ( input . ConfigKind ))
input . ChangeSummary = strings . TrimSpace ( input . ChangeSummary )
if err := validateInput ( input ); err != nil {
return Version {}, false , err
}
currentActor , err := s . resolveActor ( ctx , principal , input . TenantID , "ops_operator" , "ops_approver" , "ops_admin" )
if err != nil {
return Version {}, false , err
}
requestBody , _ := json . Marshal ( input )
result , err := s . idem . Execute ( ctx , idempotency . Request {
TenantID : & currentActor . tenantID , ActorKey : "ops:" + currentActor . userPublicID ,
Scope : "POST /api/v1/ops/config-versions" , Key : key ,
RequestHash : idempotency . Hash ( requestBody ), ExpiresAfter : 24 * time . Hour ,
}, func ( operationContext context . Context , tx pgx . Tx ) ( idempotency . Result , error ) {
if _ , lockErr := tx . Exec ( operationContext , `SELECT pg_advisory_xact_lock(hashtextextended($1,0))` , currentActor . tenantID + ":" + input . ConfigKey ); lockErr != nil {
return idempotency . Result {}, lockErr
}
var versionNo int
if queryErr := tx . QueryRow ( operationContext , `SELECT COALESCE(MAX(version_no),0)+1 FROM runtime_config_version WHERE tenant_id=$1::uuid AND config_key=$2` , currentActor . tenantID , input . ConfigKey ). Scan ( & versionNo ); queryErr != nil {
return idempotency . Result {}, queryErr
}
payloadJSON , _ := json . Marshal ( input . Payload )
refsJSON , _ := json . Marshal ( input . SecretRefs )
publicID := uuid . NewString ()
var internalID string
if queryErr := tx . QueryRow ( operationContext , `
INSERT INTO runtime_config_version(public_id,tenant_id,config_key,display_name,config_kind,version_no,payload,secret_refs,change_summary,created_by)
VALUES($1::uuid,$2::uuid,$3,$4,$5,$6,$7::jsonb,$8::jsonb,$9,$10::uuid) RETURNING id::text` ,
publicID , currentActor . tenantID , input . ConfigKey , input . DisplayName , input . ConfigKind , versionNo , payloadJSON , refsJSON , input . ChangeSummary , currentActor . userID ). Scan ( & internalID ); queryErr != nil {
return idempotency . Result {}, queryErr
}
resourceID := internalID
if auditErr := s . audit . Record ( operationContext , tx , audit . Event { TenantID : & currentActor . tenantID , ActorType : "user" , ActorPublicID : & currentActor . userPublicID , Action : "ops.config.create" , ResourceType : "runtime_config_version" , ResourceID : & resourceID , RequestID : requestID , Purpose : input . ChangeSummary , Result : "success" , Detail : map [ string ] any { "config_key" : input . ConfigKey , "version_no" : versionNo , "config_kind" : input . ConfigKind }}); auditErr != nil {
return idempotency . Result {}, auditErr
}
item , queryErr := s . getTx ( operationContext , tx , currentActor . tenantID , publicID )
if queryErr != nil {
return idempotency . Result {}, queryErr
}
body , _ := json . Marshal ( item )
return idempotency . Result { StatusCode : 201 , Body : body , ResourceID : & internalID }, nil
})
if err != nil {
return Version {}, false , err
}
var item Version
if err := json . Unmarshal ( result . Body , & item ); err != nil {
return Version {}, false , err
}
return item , ! result . Replayed , nil
}
func ( s * Service ) List ( ctx context . Context , principal identity . Principal , tenantPublicID , configKey string ) ([] Version , error ) {
currentActor , err := s . resolveActor ( ctx , principal , tenantPublicID , "ops_viewer" , "ops_operator" , "ops_approver" , "ops_admin" )
if err != nil {
return nil , err
}
configKey = strings . TrimSpace ( strings . ToLower ( configKey ))
rows , err := s . pool . Query ( ctx , versionSelect + ` WHERE rcv.tenant_id=$1::uuid AND (NULLIF($2,'') IS NULL OR rcv.config_key=$2) ORDER BY rcv.config_key,rcv.version_no DESC LIMIT 500` , currentActor . tenantID , configKey )
if err != nil {
return nil , err
}
defer rows . Close ()
items := [] Version {}
for rows . Next () {
item , scanErr := scanVersion ( rows )
if scanErr != nil {
return nil , scanErr
}
items = append ( items , item )
}
return items , rows . Err ()
}
func ( s * Service ) Submit ( ctx context . Context , principal identity . Principal , tenantPublicID , publicID , requestID string , expectedVersion int64 ) ( Version , error ) {
currentActor , err := s . resolveActor ( ctx , principal , tenantPublicID , "ops_operator" , "ops_approver" , "ops_admin" )
if err != nil {
return Version {}, err
}
tx , err := s . pool . BeginTx ( ctx , pgx . TxOptions {})
if err != nil {
return Version {}, err
}
defer func () { _ = tx . Rollback ( context . Background ()) }()
item , internalID , err := s . lockVersion ( ctx , tx , currentActor . tenantID , publicID )
if err != nil {
return Version {}, err
}
if item . RowVersion != expectedVersion {
return Version {}, ErrVersionConflict
}
if item . Status != "DRAFT" {
return Version {}, ErrStateConflict
}
validation , err := validateVersionTx ( ctx , tx , currentActor . tenantID , item )
if err != nil {
return Version {}, err
}
validationJSON , _ := json . Marshal ( validation )
_ , err = tx . Exec ( ctx , `UPDATE runtime_config_version SET status='PENDING_APPROVAL',validation_result=$2::jsonb,submitted_by=$3::uuid,submitted_at=now(),row_version=row_version+1,updated_at=now() WHERE id=$1::uuid` , internalID , validationJSON , currentActor . userID )
if err != nil {
return Version {}, err
}
if err = s . record ( ctx , tx , currentActor , "ops.config.submit" , internalID , requestID , item . ChangeSummary , map [ string ] any { "config_key" : item . ConfigKey , "version_no" : item . VersionNo , "checks" : validation }); err != nil {
return Version {}, err
}
updated , err := s . getTx ( ctx , tx , currentActor . tenantID , publicID )
if err == nil {
err = tx . Commit ( ctx )
}
return updated , err
}
func ( s * Service ) Publish ( ctx context . Context , principal identity . Principal , tenantPublicID , publicID , requestID string , input PublishInput , expectedVersion int64 ) ( Version , error ) {
input . Reason = strings . TrimSpace ( input . Reason )
if input . Reason == "" || len ([] rune ( input . Reason )) > 500 || validateRollout ( input . Rollout ) != nil {
return Version {}, ErrInvalid
}
currentActor , err := s . resolveActor ( ctx , principal , tenantPublicID , "ops_approver" , "ops_admin" )
if err != nil {
return Version {}, err
}
tx , err := s . pool . BeginTx ( ctx , pgx . TxOptions {})
if err != nil {
return Version {}, err
}
defer func () { _ = tx . Rollback ( context . Background ()) }()
item , internalID , err := s . lockVersion ( ctx , tx , currentActor . tenantID , publicID )
if err != nil {
return Version {}, err
}
if item . RowVersion != expectedVersion {
return Version {}, ErrVersionConflict
}
if item . Status != "PENDING_APPROVAL" {
return Version {}, ErrStateConflict
}
var creatorID string
if err = tx . QueryRow ( ctx , `SELECT created_by::text FROM runtime_config_version WHERE id=$1::uuid` , internalID ). Scan ( & creatorID ); err != nil {
return Version {}, err
}
if creatorID == currentActor . userID {
return Version {}, ErrSelfApproval
}
if err = s . checkPublishGatesTx ( ctx , tx , currentActor . tenantID , internalID , item ); err != nil {
return Version {}, err
}
if _ , err = tx . Exec ( ctx , `SELECT pg_advisory_xact_lock(hashtextextended($1,0))` , currentActor . tenantID + ":" + item . ConfigKey ); err != nil {
return Version {}, err
}
_ , err = tx . Exec ( ctx , `UPDATE runtime_config_version SET status='PUBLISHED',approved_by=$2::uuid,approved_at=now(),published_by=$2::uuid,published_at=now(),retired_at=NULL,row_version=row_version+1,updated_at=now() WHERE id=$1::uuid` , internalID , currentActor . userID )
if err != nil {
return Version {}, err
}
rolloutID , supersededID , err := s . activateRolloutTx ( ctx , tx , currentActor , internalID , item , input . Rollout , input . Reason )
if err != nil {
return Version {}, err
}
if supersededID != nil {
if _ , err = tx . Exec ( ctx , `UPDATE runtime_config_version SET supersedes_id=$2::uuid WHERE id=$1::uuid` , internalID , * supersededID ); err != nil {
return Version {}, err
}
}
if err = s . record ( ctx , tx , currentActor , "ops.config.publish" , internalID , requestID , input . Reason , map [ string ] any { "config_key" : item . ConfigKey , "version_no" : item . VersionNo , "rollout_id" : rolloutID , "rollout" : input . Rollout . String ()}); err != nil {
return Version {}, err
}
updated , err := s . getTx ( ctx , tx , currentActor . tenantID , publicID )
if err == nil {
err = tx . Commit ( ctx )
}
return updated , err
}
func ( s * Service ) Rollback ( ctx context . Context , principal identity . Principal , tenantPublicID , targetPublicID , key , requestID string , input PublishInput ) ( Version , bool , error ) {
input . Reason = strings . TrimSpace ( input . Reason )
if input . Reason == "" || len ([] rune ( input . Reason )) > 500 || validateRollout ( input . Rollout ) != nil {
return Version {}, false , ErrInvalid
}
currentActor , err := s . resolveActor ( ctx , principal , tenantPublicID , "ops_approver" , "ops_admin" )
if err != nil {
return Version {}, false , err
}
requestBody , _ := json . Marshal ( map [ string ] any { "target_id" : targetPublicID , "reason" : input . Reason , "rollout" : input . Rollout })
result , err := s . idem . Execute ( ctx , idempotency . Request {
TenantID : & currentActor . tenantID , ActorKey : "ops:" + currentActor . userPublicID ,
Scope : "POST /api/v1/ops/config-versions/rollback" , Key : key ,
RequestHash : idempotency . Hash ( requestBody ), ExpiresAfter : 24 * time . Hour ,
}, func ( operationContext context . Context , tx pgx . Tx ) ( idempotency . Result , error ) {
target , targetInternalID , rollbackErr := s . lockVersion ( operationContext , tx , currentActor . tenantID , targetPublicID )
if rollbackErr != nil {
return idempotency . Result {}, rollbackErr
}
if target . Status != "RETIRED" && target . Status != "PUBLISHED" {
return idempotency . Result {}, ErrStateConflict
}
if rollbackErr = s . checkPublishGatesTx ( operationContext , tx , currentActor . tenantID , targetInternalID , target ); rollbackErr != nil {
return idempotency . Result {}, rollbackErr
}
if _ , rollbackErr = tx . Exec ( operationContext , `SELECT pg_advisory_xact_lock(hashtextextended($1,0))` , currentActor . tenantID + ":" + target . ConfigKey ); rollbackErr != nil {
return idempotency . Result {}, rollbackErr
}
var nextVersion int
if rollbackErr = tx . QueryRow ( operationContext , `SELECT MAX(version_no)+1 FROM runtime_config_version WHERE tenant_id=$1::uuid AND config_key=$2` , currentActor . tenantID , target . ConfigKey ). Scan ( & nextVersion ); rollbackErr != nil {
return idempotency . Result {}, rollbackErr
}
newPublicID := uuid . NewString ()
var newInternalID string
if rollbackErr = tx . QueryRow ( operationContext , `INSERT INTO runtime_config_version(public_id,tenant_id,config_key,display_name,config_kind,version_no,status,payload,secret_refs,change_summary,validation_result,created_by,submitted_by,submitted_at,approved_by,approved_at,published_by,published_at,rollback_of_id) VALUES($1::uuid,$2::uuid,$3,$4,$5,$6,'PUBLISHED',$7::jsonb,$8::jsonb,$9,$10::jsonb,$11::uuid,$11::uuid,now(),$11::uuid,now(),$11::uuid,now(),$12::uuid) RETURNING id::text` , newPublicID , currentActor . tenantID , target . ConfigKey , target . DisplayName , target . ConfigKind , nextVersion , mustJSON ( target . Payload ), mustJSON ( target . SecretRefs ), input . Reason , mustJSON ( target . ValidationResult ), currentActor . userID , targetInternalID ). Scan ( & newInternalID ); rollbackErr != nil {
return idempotency . Result {}, rollbackErr
}
rolloutID , supersededID , rollbackErr := s . activateRolloutTx ( operationContext , tx , currentActor , newInternalID , target , input . Rollout , input . Reason )
if rollbackErr != nil {
return idempotency . Result {}, rollbackErr
}
if supersededID != nil {
if _ , rollbackErr = tx . Exec ( operationContext , `UPDATE runtime_config_version SET supersedes_id=$2::uuid WHERE id=$1::uuid` , newInternalID , * supersededID ); rollbackErr != nil {
return idempotency . Result {}, rollbackErr
}
}
if rollbackErr = s . record ( operationContext , tx , currentActor , "ops.config.rollback" , newInternalID , requestID , input . Reason , map [ string ] any { "config_key" : target . ConfigKey , "new_version_no" : nextVersion , "rollback_of" : targetPublicID , "rollout_id" : rolloutID , "rollout" : input . Rollout . String ()}); rollbackErr != nil {
return idempotency . Result {}, rollbackErr
}
updated , rollbackErr := s . getTx ( operationContext , tx , currentActor . tenantID , newPublicID )
if rollbackErr != nil {
return idempotency . Result {}, rollbackErr
}
body , _ := json . Marshal ( updated )
return idempotency . Result { StatusCode : 201 , Body : body , ResourceID : & newInternalID }, nil
})
if err != nil {
return Version {}, false , err
}
var item Version
if err := json . Unmarshal ( result . Body , & item ); err != nil {
return Version {}, false , err
}
return item , ! result . Replayed , nil
}
func ( s * Service ) resolveActor ( ctx context . Context , principal identity . Principal , tenantPublicID string , roles ... string ) ( actor , error ) {
if _ , err := uuid . Parse ( tenantPublicID ); err != nil {
return actor {}, ErrInvalid
}
var result actor
2026-08-29 21:56:51 +08:00
err := s . pool . QueryRow ( ctx , `SELECT t.id::text,t.public_id::text,ua.id::text,ua.public_id::text,ua.platform_role FROM user_account ua JOIN tenant t ON t.id=ua.tenant_id AND t.status='active' WHERE t.public_id=$1::uuid AND ua.oidc_issuer=$2 AND ua.oidc_subject=$3 AND ua.status='active' AND ua.account_kind='operator'` , tenantPublicID , principal . Issuer , principal . Subject ). Scan ( & result . tenantID , & result . tenantPublicID , & result . userID , & result . userPublicID , & result . role )
2026-08-26 06:41:07 +08:00
if errors . Is ( err , pgx . ErrNoRows ) {
return actor {}, ErrAccessDenied
}
if err != nil {
return actor {}, err
}
for _ , role := range roles {
if result . role == role {
return result , nil
}
}
return actor {}, ErrAccessDenied
}
func validateInput ( input CreateInput ) error {
if ! regexp . MustCompile ( `^[a-z][a-z0-9_.-]{2,99}$` ). MatchString ( input . ConfigKey ) || input . DisplayName == "" || len ([] rune ( input . DisplayName )) > 200 || input . ChangeSummary == "" || len ([] rune ( input . ChangeSummary )) > 500 || input . Payload == nil {
return ErrInvalid
}
switch input . ConfigKind {
case "PERSONA" , "VOICE" , "MODEL" , "ROUTING" , "AGENT_BUNDLE" :
default :
return ErrInvalid
}
if _ , err := validatePayload ( input . ConfigKind , input . Payload , input . SecretRefs ); err != nil {
return err
}
return nil
}
func validateVersion ( item Version ) ( map [ string ] any , error ) {
return validatePayload ( item . ConfigKind , item . Payload , item . SecretRefs )
}
func validateVersionTx ( ctx context . Context , tx pgx . Tx , tenantID string , item Version ) ( map [ string ] any , error ) {
validation , err := validateVersion ( item )
if err != nil {
return nil , err
}
if item . ConfigKind != "AGENT_BUNDLE" {
return validation , nil
}
components , err := validateAgentBundleComponentsTx ( ctx , tx , tenantID , item )
if err != nil {
return nil , ErrInvalid
}
checks , _ := validation [ "checks" ].([] string )
validation [ "checks" ] = append ( checks , "component_versions" )
validation [ "component_versions" ] = components
return validation , nil
}
func validateAgentBundleComponentsTx ( ctx context . Context , tx pgx . Tx , tenantID string , item Version ) ([] map [ string ] string , error ) {
expectedKinds := [] struct {
field string
kind string
}{
{ field : "persona_version_id" , kind : "PERSONA" },
{ field : "voice_version_id" , kind : "VOICE" },
{ field : "model_version_id" , kind : "MODEL" },
{ field : "routing_version_id" , kind : "ROUTING" },
}
components := make ([] map [ string ] string , 0 , len ( expectedKinds ))
for _ , expected := range expectedKinds {
publicID , ok := item . Payload [ expected . field ].( string )
if ! ok {
return nil , ErrInvalid
}
publicID = strings . TrimSpace ( publicID )
if _ , parseErr := uuid . Parse ( publicID ); parseErr != nil {
return nil , ErrInvalid
}
var kind , status string
err := tx . QueryRow ( ctx , `SELECT config_kind,status FROM runtime_config_version WHERE tenant_id=$1::uuid AND public_id=$2::uuid` , tenantID , publicID ). Scan ( & kind , & status )
if errors . Is ( err , pgx . ErrNoRows ) {
return nil , ErrInvalid
}
if err != nil {
return nil , err
}
if kind != expected . kind || ( status != "PUBLISHED" && status != "RETIRED" ) {
return nil , ErrInvalid
}
components = append ( components , map [ string ] string { "field" : expected . field , "version_id" : publicID , "config_kind" : kind , "status" : status })
}
return components , nil
}
func validatePayload ( kind string , payload map [ string ] any , refs map [ string ] string ) ( map [ string ] any , error ) {
encoded , err := json . Marshal ( payload )
if err != nil || len ( encoded ) == 0 || len ( encoded ) > 65536 || containsSensitiveKey ( payload ) {
return nil , ErrInvalid
}
for key , value := range refs {
if ! referencePattern . MatchString ( key ) || ! referencePattern . MatchString ( strings . TrimSpace ( value )) {
return nil , ErrInvalid
}
}
requireString := func ( key string ) bool { value , ok := payload [ key ].( string ); return ok && strings . TrimSpace ( value ) != "" }
switch kind {
case "PERSONA" :
if ! requireString ( "system_prompt" ) || ! requireString ( "tone" ) {
return nil , ErrInvalid
}
case "VOICE" :
if ! requireString ( "provider" ) || ! requireString ( "voice_resource_ref" ) || refs [ "credential_ref" ] == "" {
return nil , ErrInvalid
}
case "MODEL" :
if ! requireString ( "provider" ) || ! requireString ( "model_id" ) || refs [ "credential_ref" ] == "" {
return nil , ErrInvalid
}
case "ROUTING" :
mode , _ := payload [ "default_engine_mode" ].( string )
if mode != "s2s_pure" && mode != "s2s_hybrid" && mode != "cascade" {
return nil , ErrInvalid
}
if _ , exists := payload [ "routes" ]; ! exists {
return nil , ErrInvalid
}
case "AGENT_BUNDLE" :
for _ , key := range [] string { "persona_version_id" , "voice_version_id" , "model_version_id" , "routing_version_id" } {
value , _ := payload [ key ].( string )
if ! requireString ( key ) {
return nil , ErrInvalid
}
if _ , parseErr := uuid . Parse ( strings . TrimSpace ( value )); parseErr != nil {
return nil , ErrInvalid
}
}
}
return map [ string ] any { "valid" : true , "schema" : "runtime-config-v1" , "checked_at" : time . Now (). UTC (). Format ( time . RFC3339 ), "checks" : [] string { "structure" , "required_fields" , "secret_references" , "sensitive_key_rejection" }}, nil
}
func containsSensitiveKey ( value any ) bool {
switch typed := value .( type ) {
case map [ string ] any :
for key , child := range typed {
lower := strings . ToLower ( key )
for _ , forbidden := range [] string { "secret" , "password" , "token" , "private_key" , "api_key" , "credential" } {
if strings . Contains ( lower , forbidden ) {
return true
}
}
if containsSensitiveKey ( child ) {
return true
}
}
case [] any :
for _ , child := range typed {
if containsSensitiveKey ( child ) {
return true
}
}
}
return false
}
const versionSelect = `SELECT rcv.public_id::text,t.public_id::text,rcv.config_key,rcv.display_name,rcv.config_kind,rcv.version_no,rcv.status,rcv.payload,rcv.secret_refs,rcv.change_summary,rcv.validation_result,creator.public_id::text,submitter.public_id::text,rcv.submitted_at,approver.public_id::text,rcv.approved_at,publisher.public_id::text,rcv.published_at,rcv.retired_at,supersedes.public_id::text,rollback.public_id::text,rcv.row_version,rcv.created_at,rcv.updated_at FROM runtime_config_version rcv JOIN tenant t ON t.id=rcv.tenant_id JOIN user_account creator ON creator.id=rcv.created_by LEFT JOIN user_account submitter ON submitter.id=rcv.submitted_by LEFT JOIN user_account approver ON approver.id=rcv.approved_by LEFT JOIN user_account publisher ON publisher.id=rcv.published_by LEFT JOIN runtime_config_version supersedes ON supersedes.id=rcv.supersedes_id LEFT JOIN runtime_config_version rollback ON rollback.id=rcv.rollback_of_id`
type rowScanner interface { Scan ( ... any ) error }
func scanVersion ( row rowScanner ) ( Version , error ) {
var item Version
var payloadJSON , refsJSON , validationJSON [] byte
err := row . Scan ( & item . ID , & item . TenantID , & item . ConfigKey , & item . DisplayName , & item . ConfigKind , & item . VersionNo , & item . Status , & payloadJSON , & refsJSON , & item . ChangeSummary , & validationJSON , & item . CreatedBy , & item . SubmittedBy , & item . SubmittedAt , & item . ApprovedBy , & item . ApprovedAt , & item . PublishedBy , & item . PublishedAt , & item . RetiredAt , & item . SupersedesID , & item . RollbackOfID , & item . RowVersion , & item . CreatedAt , & item . UpdatedAt )
if err != nil {
return Version {}, err
}
if err = json . Unmarshal ( payloadJSON , & item . Payload ); err != nil {
return Version {}, err
}
if err = json . Unmarshal ( refsJSON , & item . SecretRefs ); err != nil {
return Version {}, err
}
if err = json . Unmarshal ( validationJSON , & item . ValidationResult ); err != nil {
return Version {}, err
}
return item , nil
}
func ( s * Service ) getTx ( ctx context . Context , tx pgx . Tx , tenantID , publicID string ) ( Version , error ) {
item , err := scanVersion ( tx . QueryRow ( ctx , versionSelect + ` WHERE rcv.tenant_id=$1::uuid AND rcv.public_id=$2::uuid` , tenantID , publicID ))
if errors . Is ( err , pgx . ErrNoRows ) {
return Version {}, ErrNotFound
}
return item , err
}
func ( s * Service ) lockVersion ( ctx context . Context , tx pgx . Tx , tenantID , publicID string ) ( Version , string , error ) {
var internalID string
err := tx . QueryRow ( ctx , `SELECT id::text FROM runtime_config_version WHERE tenant_id=$1::uuid AND public_id=$2::uuid FOR UPDATE` , tenantID , publicID ). Scan ( & internalID )
if errors . Is ( err , pgx . ErrNoRows ) {
return Version {}, "" , ErrNotFound
}
if err != nil {
return Version {}, "" , err
}
item , err := s . getTx ( ctx , tx , tenantID , publicID )
return item , internalID , err
}
func ( s * Service ) record ( ctx context . Context , tx pgx . Tx , currentActor actor , action , resourceID , requestID , purpose string , detail map [ string ] any ) error {
return s . audit . Record ( ctx , tx , audit . Event { TenantID : & currentActor . tenantID , ActorType : "user" , ActorPublicID : & currentActor . userPublicID , Action : action , ResourceType : "runtime_config_version" , ResourceID : & resourceID , RequestID : requestID , Purpose : purpose , Result : "success" , Detail : detail })
}
func mustJSON ( value any ) [] byte {
encoded , _ := json . Marshal ( value )
return encoded
}
func ( v Version ) String () string { return fmt . Sprintf ( "%s@%d" , v . ConfigKey , v . VersionNo ) }