From 443d0492336a52be4ebd3d24ee09268fb27df5bc Mon Sep 17 00:00:00 2001 From: Bjorn Tipling Date: Sat, 25 Jul 2026 21:13:29 +0000 Subject: [PATCH] fix: migrate trait profile/status to resource-level APIs Resolve golangci-lint SA1019 failures on main by using WithResourceProfile/WithResourceStatus and GetProfile/GetStatus instead of deprecated trait-level profile and status APIs. Co-authored-by: c1-squire-dev[bot] --- pkg/bcel/bcel.go | 18 +++---- pkg/bsql/nhi_test.go | 10 +++- pkg/bsql/resources.go | 113 ++++++++++++++++++++++++++---------------- 3 files changed, 85 insertions(+), 56 deletions(-) diff --git a/pkg/bcel/bcel.go b/pkg/bcel/bcel.go index ba11817c..c1aa4d91 100644 --- a/pkg/bcel/bcel.go +++ b/pkg/bcel/bcel.go @@ -133,18 +133,12 @@ func resourceToCELMap(resource *v2.Resource) map[string]any { "DisplayName": resource.DisplayName, } - if t, err := sdkResource.GetGroupTrait(resource); err == nil && t.GetProfile() != nil { - out["profile"] = t.GetProfile().AsMap() - } else if t, err := sdkResource.GetUserTrait(resource); err == nil && t.GetProfile() != nil { - out["profile"] = t.GetProfile().AsMap() - } else if t, err := sdkResource.GetRoleTrait(resource); err == nil && t.GetProfile() != nil { - out["profile"] = t.GetProfile().AsMap() - } else if t, err := sdkResource.GetAppTrait(resource); err == nil && t.GetProfile() != nil { - out["profile"] = t.GetProfile().AsMap() - } - - // Empty default so `has(resource.profile.X)` is well-defined for optional fields. - if _, exists := out["profile"]; !exists { + // Profile lives on Resource (trait-level profile is deprecated SA1019). + // GetProfile reads resource-level first and falls back to legacy trait fields. + if profile := sdkResource.GetProfile(resource); profile != nil { + out["profile"] = profile.AsMap() + } else { + // Empty default so `has(resource.profile.X)` is well-defined for optional fields. out["profile"] = map[string]any{} } diff --git a/pkg/bsql/nhi_test.go b/pkg/bsql/nhi_test.go index f5cc70a6..6d3688ed 100644 --- a/pkg/bsql/nhi_test.go +++ b/pkg/bsql/nhi_test.go @@ -171,11 +171,17 @@ func TestMapResource_AgentTrait(t *testing.T) { at, err := sdkResource.GetAgentTrait(r) require.NoError(t, err) - require.Equal(t, v2.AgentTrait_AGENT_STATUS_READY, at.GetStatus()) + // Status and profile live on Resource (trait-level getters are deprecated SA1019). + // Agent READY maps to RESOURCE_STATUS_ENABLED (identical enum values). + st := sdkResource.GetStatus(r) + require.NotNil(t, st) + require.Equal(t, v2.Status_RESOURCE_STATUS_ENABLED, st.GetStatus()) require.NotNil(t, at.GetIdentityResourceId()) require.Equal(t, "user", at.GetIdentityResourceId().GetResourceType()) require.Equal(t, "svc-acct-1", at.GetIdentityResourceId().GetResource()) - require.Equal(t, "claude-opus", at.GetProfile().GetFields()["model"].GetStringValue()) + profile := sdkResource.GetProfile(r) + require.NotNil(t, profile) + require.Equal(t, "claude-opus", profile.GetFields()["model"].GetStringValue()) } // Graceful degradation — a plain user resource emits only a UserTrait, no diff --git a/pkg/bsql/resources.go b/pkg/bsql/resources.go index c8df30e3..79a2c4be 100644 --- a/pkg/bsql/resources.go +++ b/pkg/bsql/resources.go @@ -101,45 +101,47 @@ func (s *SQLSyncer) mapUserTrait(ctx context.Context, r *v2.Resource, rowMap map opts = append(opts, sdkResource.WithEmail(v, primary)) } - // Status + // Status lives on Resource (trait-level status options are deprecated SA1019). + var resourceStatus *v2.Status_ResourceStatus + var statusDetails string if mappings.Status != "" { statusValue, err := s.env.EvaluateString(ctx, mappings.Status, inputs) if err != nil { return err } - var status v2.UserTrait_Status_Status + var status v2.Status_ResourceStatus switch strings.ToLower(statusValue) { case "active": - status = v2.UserTrait_Status_STATUS_ENABLED + status = v2.Status_RESOURCE_STATUS_ENABLED case "enabled": - status = v2.UserTrait_Status_STATUS_ENABLED + status = v2.Status_RESOURCE_STATUS_ENABLED case "disabled": - status = v2.UserTrait_Status_STATUS_DISABLED + status = v2.Status_RESOURCE_STATUS_DISABLED case "inactive": - status = v2.UserTrait_Status_STATUS_DISABLED + status = v2.Status_RESOURCE_STATUS_DISABLED case "suspended": - status = v2.UserTrait_Status_STATUS_DISABLED + status = v2.Status_RESOURCE_STATUS_DISABLED case "locked": - status = v2.UserTrait_Status_STATUS_DISABLED + status = v2.Status_RESOURCE_STATUS_DISABLED case "deleted": - status = v2.UserTrait_Status_STATUS_DELETED + status = v2.Status_RESOURCE_STATUS_DELETED default: l.Warn("unexpected status value in mapping", zap.String("status", statusValue)) - status = v2.UserTrait_Status_STATUS_UNSPECIFIED + status = v2.Status_RESOURCE_STATUS_UNSPECIFIED } + resourceStatus = &status if mappings.StatusDetails != "" { v, err := s.env.EvaluateString(ctx, mappings.StatusDetails, inputs) if err != nil { return err } - opts = append(opts, sdkResource.WithDetailedStatus(status, v)) - } else { - opts = append(opts, sdkResource.WithStatus(status)) + statusDetails = v } } + // Profile lives on Resource (trait-level profile options are deprecated SA1019). profile := make(map[string]interface{}) for profileKey, profileValue := range mappings.Profile { v, err := s.env.EvaluateString(ctx, profileValue, inputs) @@ -149,10 +151,6 @@ func (s *SQLSyncer) mapUserTrait(ctx context.Context, r *v2.Resource, rowMap map profile[profileKey] = v } - if len(profile) > 0 { - opts = append(opts, sdkResource.WithUserProfile(profile)) - } - // Last Login if mappings.LastLogin != "" { lastLoginValue, err := s.env.EvaluateString(ctx, mappings.LastLogin, inputs) @@ -271,6 +269,17 @@ func (s *SQLSyncer) mapUserTrait(ctx context.Context, r *v2.Resource, rowMap map annos.Update(t) r.Annotations = annos + if resourceStatus != nil { + if err := sdkResource.WithResourceStatus(*resourceStatus, statusDetails)(r); err != nil { + return err + } + } + if len(profile) > 0 { + if err := sdkResource.WithResourceProfile(profile)(r); err != nil { + return err + } + } + // Annotation applied return nil @@ -291,6 +300,7 @@ func (s *SQLSyncer) mapAppTrait(ctx context.Context, r *v2.Resource, rowMap map[ opts = append(opts, sdkResource.WithAppHelpURL(v)) } + // Profile lives on Resource (trait-level profile options are deprecated SA1019). profile := make(map[string]interface{}) for profileKey, profileValue := range mappings.Profile { v, err := s.env.EvaluateString(ctx, profileValue, inputs) @@ -300,10 +310,6 @@ func (s *SQLSyncer) mapAppTrait(ctx context.Context, r *v2.Resource, rowMap map[ profile[profileKey] = v } - if len(profile) > 0 { - opts = append(opts, sdkResource.WithAppProfile(profile)) - } - t, err := sdkResource.NewAppTrait(opts...) if err != nil { return err @@ -313,6 +319,12 @@ func (s *SQLSyncer) mapAppTrait(ctx context.Context, r *v2.Resource, rowMap map[ annos.Update(t) r.Annotations = annos + if len(profile) > 0 { + if err := sdkResource.WithResourceProfile(profile)(r); err != nil { + return err + } + } + return nil } @@ -321,8 +333,7 @@ func (s *SQLSyncer) mapGroupTrait(ctx context.Context, r *v2.Resource, rowMap ma mappings := s.config.List.Map.Traits.Group - var opts []sdkResource.GroupTraitOption - + // Profile lives on Resource (trait-level profile options are deprecated SA1019). profile := make(map[string]interface{}) for profileKey, profileValue := range mappings.Profile { v, err := s.env.EvaluateString(ctx, profileValue, inputs) @@ -331,11 +342,8 @@ func (s *SQLSyncer) mapGroupTrait(ctx context.Context, r *v2.Resource, rowMap ma } profile[profileKey] = v } - if len(profile) > 0 { - opts = append(opts, sdkResource.WithGroupProfile(profile)) - } - t, err := sdkResource.NewGroupTrait(opts...) + t, err := sdkResource.NewGroupTrait() if err != nil { return err } @@ -344,6 +352,12 @@ func (s *SQLSyncer) mapGroupTrait(ctx context.Context, r *v2.Resource, rowMap ma annos.Update(t) r.Annotations = annos + if len(profile) > 0 { + if err := sdkResource.WithResourceProfile(profile)(r); err != nil { + return err + } + } + return nil } @@ -352,8 +366,7 @@ func (s *SQLSyncer) mapRoleTrait(ctx context.Context, r *v2.Resource, rowMap map mappings := s.config.List.Map.Traits.Role - var opts []sdkResource.RoleTraitOption - + // Profile lives on Resource (trait-level profile options are deprecated SA1019). profile := make(map[string]interface{}) for profileKey, profileValue := range mappings.Profile { v, err := s.env.EvaluateString(ctx, profileValue, inputs) @@ -362,11 +375,8 @@ func (s *SQLSyncer) mapRoleTrait(ctx context.Context, r *v2.Resource, rowMap map } profile[profileKey] = v } - if len(profile) > 0 { - opts = append(opts, sdkResource.WithRoleProfile(profile)) - } - t, err := sdkResource.NewRoleTrait(opts...) + t, err := sdkResource.NewRoleTrait() if err != nil { return err } @@ -375,6 +385,12 @@ func (s *SQLSyncer) mapRoleTrait(ctx context.Context, r *v2.Resource, rowMap map annos.Update(t) r.Annotations = annos + if len(profile) > 0 { + if err := sdkResource.WithResourceProfile(profile)(r); err != nil { + return err + } + } + return nil } @@ -467,25 +483,29 @@ func (s *SQLSyncer) mapAgentTrait(ctx context.Context, r *v2.Resource, rowMap ma var opts []sdkResource.AgentTraitOption + // Status lives on Resource (trait-level status options are deprecated SA1019). + // AgentTrait_AgentStatus and Status_ResourceStatus share the same numeric values + // (READY maps to ENABLED). + var resourceStatus *v2.Status_ResourceStatus if mappings.Status != "" { v, err := s.env.EvaluateString(ctx, mappings.Status, inputs) if err != nil { return err } - var status v2.AgentTrait_AgentStatus + var status v2.Status_ResourceStatus switch strings.ToLower(v) { case "ready", "active", "enabled": - status = v2.AgentTrait_AGENT_STATUS_READY + status = v2.Status_RESOURCE_STATUS_ENABLED case "disabled", "inactive": - status = v2.AgentTrait_AGENT_STATUS_DISABLED + status = v2.Status_RESOURCE_STATUS_DISABLED case "deleted": - status = v2.AgentTrait_AGENT_STATUS_DELETED + status = v2.Status_RESOURCE_STATUS_DELETED default: l.Warn("unexpected agent status value in mapping", zap.String("status", v)) - status = v2.AgentTrait_AGENT_STATUS_UNSPECIFIED + status = v2.Status_RESOURCE_STATUS_UNSPECIFIED } - opts = append(opts, sdkResource.WithAgentStatus(status)) + resourceStatus = &status } if mappings.IdentityResourceID != "" { @@ -512,6 +532,7 @@ func (s *SQLSyncer) mapAgentTrait(ctx context.Context, r *v2.Resource, rowMap ma } } + // Profile lives on Resource (trait-level profile options are deprecated SA1019). profile := make(map[string]interface{}) for profileKey, profileValue := range mappings.Profile { v, err := s.env.EvaluateString(ctx, profileValue, inputs) @@ -520,9 +541,6 @@ func (s *SQLSyncer) mapAgentTrait(ctx context.Context, r *v2.Resource, rowMap ma } profile[profileKey] = v } - if len(profile) > 0 { - opts = append(opts, sdkResource.WithAgentProfile(profile)) - } t, err := sdkResource.NewAgentTrait(opts...) if err != nil { @@ -533,6 +551,17 @@ func (s *SQLSyncer) mapAgentTrait(ctx context.Context, r *v2.Resource, rowMap ma annos.Update(t) r.Annotations = annos + if resourceStatus != nil { + if err := sdkResource.WithResourceStatus(*resourceStatus, "")(r); err != nil { + return err + } + } + if len(profile) > 0 { + if err := sdkResource.WithResourceProfile(profile)(r); err != nil { + return err + } + } + return nil }