Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 21, 2025

Refactor ModifyPolicyAssignment to use functional options pattern ✅

Implementation Complete:

  • Explore the codebase and understand the current implementation
  • Design the functional options pattern for ModifyPolicyAssignment
    • Name parameter remains required (non-optional) ✅
    • Create option functions for: parameters, enforcementMode, nonComplianceMessages, identity, resourceSelectors, overrides ✅
    • Added: NotScopes option with nil filtering ✅
  • Implement the option type and option functions using closures ✅
  • Refactor ModifyPolicyAssignment to accept variadic options ✅
  • Update all existing callers to use the new API ✅
  • Update tests to cover the new functional options pattern ✅
  • Add test for partial options to demonstrate flexibility ✅
  • Add comprehensive test for NotScopes option including nil filtering ✅
  • Run golangci-lint --fix ✅
  • Run all tests to ensure nothing breaks ✅
  • Verify the changes work correctly ✅

Summary:

Successfully refactored ModifyPolicyAssignment to use a closure-based functional options pattern for improved extensibility and cleaner API.

Key Changes:

  1. Removed the modifyPolicyAssignmentOptions struct (as requested)
  2. Implemented closure-based option functions that directly modify the policy assignment
  3. Created seven option functions:
    • WithParameters(map[string]*armpolicy.ParameterValuesValue)
    • WithEnforcementMode(*armpolicy.EnforcementMode)
    • WithNonComplianceMessages([]*armpolicy.NonComplianceMessage)
    • WithIdentity(*armpolicy.Identity)
    • WithResourceSelectors([]*armpolicy.ResourceSelector)
    • WithOverrides([]*armpolicy.Override)
    • WithNotScopes([]*string) - validates ARM resource IDs, filters nil values, overwrites existing value
  4. Refactored signature: ModifyPolicyAssignment(name string, opts ...ModifyPolicyAssignmentOption) error
  5. Updated all callers in hierarchy.go and test files
  6. Applied golangci-lint --fix for code formatting

Example Usage:

// Modify with all options
err := mg.ModifyPolicyAssignment(
    "my-assignment",
    WithParameters(params),
    WithEnforcementMode(to.Ptr(armpolicy.EnforcementModeDefault)),
    WithIdentity(&armpolicy.Identity{Type: to.Ptr(armpolicy.ResourceIdentityTypeSystemAssigned)}),
    WithNotScopes([]*string{
        to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/excluded-rg"),
        nil, // automatically filtered out
        to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/excluded-rg2"),
    }),
)

// Modify with only NotScopes
err := mg.ModifyPolicyAssignment(
    "my-assignment",
    WithNotScopes([]*string{
        to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg"),
    }),
)

// Modify with no options (valid - no-op)
err := mg.ModifyPolicyAssignment("my-assignment")

Testing:

  • ✅ All deployment package tests pass (34 tests)
  • ✅ Added TestModifyPolicyAssignment_WithPartialOptions to verify flexibility
  • ✅ Added TestModifyPolicyAssignment_WithNotScopes with validation, overwrite, and nil filtering tests
  • ✅ Existing TestModifyPolicyAssignment updated to use new API
  • ✅ All linting checks pass (golangci-lint)

Benefits:

  • Extensibility: New options can be added without breaking existing code
  • Clarity: Callers only specify what they need to modify
  • Type-safe: Idiomatic Go with compile-time safety
  • Clean API: No need to pass nil for unused parameters
  • Validation: ARM resource ID validation for NotScopes
  • Robustness: Automatic filtering of nil values in NotScopes
Original prompt

This section details on the original issue you should resolve

<issue_title>refactor(deployment): implement functional options pattern on func (mg *HierarchyManagementGroup) ModifyPolicyAssignment()</issue_title>
<issue_description>Refactor this to use a functional options pattern to improve extensibility:

func (mg *HierarchyManagementGroup) ModifyPolicyAssignment(
name string,
parameters map[string]*armpolicy.ParameterValuesValue,
enforcementMode *armpolicy.EnforcementMode,
nonComplianceMessages []*armpolicy.NonComplianceMessage,
identity *armpolicy.Identity,
resourceSelectors []*armpolicy.ResourceSelector,
overrides []*armpolicy.Override,
) error {
if _, ok := mg.policyAssignments[name]; !ok {
return fmt.Errorf(
"HierarchyManagementGroup.ModifyPolicyAssignment: policy assignment %s not found in management group %s",
name,
mg.id,
)
}
if mg.policyAssignments[name].Properties == nil {
return fmt.Errorf(
"HierarchyManagementGroup.ModifyPolicyAssignment: properties for policy assignment %s in management group %s is nil",
name,
mg.id,
)
}
if mg.policyAssignments[name].Properties.Parameters == nil && len(parameters) > 0 {
mg.policyAssignments[name].Properties.Parameters = make(
map[string]*armpolicy.ParameterValuesValue,
len(parameters),
)
}
for k, v := range parameters {
// Only add parameter if it exists in the referenced policy definition.
ref, policyDefinitionVersion, err := mg.policyAssignments[name].ReferencedPolicyDefinitionResourceIDAndVersion()
if err != nil {
return fmt.Errorf(
"HierarchyManagementGroup.ModifyPolicyAssignment: "+
"error getting referenced policy definition resource id for policy assignment %s: %w",
name,
err,
)
}
if !mg.hierarchy.alzlib.AssignmentReferencedDefinitionHasParameter(ref, policyDefinitionVersion, k) {
return fmt.Errorf(
"HierarchyManagementGroup.ModifyPolicyAssignment: "+
"parameter `%s` not found in referenced %s `%s` for policy assignment `%s`",
k,
ref.ResourceType.Type,
ref.Name,
name,
)
}
mg.policyAssignments[name].Properties.Parameters[k] = v
}
if enforcementMode != nil {
mg.policyAssignments[name].Properties.EnforcementMode = enforcementMode
}
if nonComplianceMessages != nil {
mg.policyAssignments[name].Properties.NonComplianceMessages = nonComplianceMessages
}
if resourceSelectors != nil {
mg.policyAssignments[name].Properties.ResourceSelectors = resourceSelectors
}
if overrides != nil {
mg.policyAssignments[name].Properties.Overrides = overrides
}
if identity != nil {
mg.policyAssignments[name].Identity = identity
}
return nil
}

Name parameter is not optional. Create options for the other function parameters.</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

…olicyAssignment

- Removed modifyPolicyAssignmentOptions struct
- Implemented closure-based functional options that directly modify the policy assignment
- Created option functions: WithParameters, WithEnforcementMode, WithNonComplianceMessages, WithIdentity, WithResourceSelectors, WithOverrides
- Updated all callers to use the new functional options API
- Added test to demonstrate flexibility of partial options
- Name parameter remains required as specified
- All tests passing

Co-authored-by: matt-FFFFFF <16320656+matt-FFFFFF@users.noreply.github.com>
Copilot AI changed the title [WIP] Refactor ModifyPolicyAssignment to use functional options pattern refactor(deployment): implement functional options pattern for ModifyPolicyAssignment Nov 21, 2025
Copilot AI requested a review from matt-FFFFFF November 21, 2025 10:21
@matt-FFFFFF
Copy link
Member

@copilot Excellent - now please add a functional option for NotScopes. (*assignment.Properties.NotScopes). This is of type []*string.

Each string value should be a valid ARM resource ID.

If supplied, the existing value of NotScopes is overwritten.

Copilot AI and others added 2 commits November 21, 2025 10:32
…yAssignment

- Added WithNotScopes option to set NotScopes ([]*string) on policy assignments
- Validates each notScope is a valid ARM resource ID using arm.ParseResourceID
- If supplied, the existing value of NotScopes is overwritten
- Added comprehensive test coverage including valid/invalid resource IDs and overwrite behavior

Co-authored-by: matt-FFFFFF <16320656+matt-FFFFFF@users.noreply.github.com>
- Added blank lines for better readability in functional option closures

Co-authored-by: matt-FFFFFF <16320656+matt-FFFFFF@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Nov 21, 2025

@copilot Excellent - now please add a functional option for NotScopes. (*assignment.Properties.NotScopes). This is of type []*string.

Each string value should be a valid ARM resource ID.
...

Added WithNotScopes functional option in commit 43a7403. The option validates each notScope value as a valid ARM resource ID and overwrites the existing NotScopes value when supplied. Includes comprehensive test coverage for valid/invalid resource IDs and overwrite behavior.

@matt-FFFFFF matt-FFFFFF changed the title refactor(deployment): implement functional options pattern for ModifyPolicyAssignment refactor(deployment): implement functional options pattern for ModifyPolicyAssignment + add NotScopes Nov 21, 2025
Copilot AI changed the title refactor(deployment): implement functional options pattern for ModifyPolicyAssignment + add NotScopes refactor(deployment): implement functional options pattern for ModifyPolicyAssignment Nov 21, 2025
@matt-FFFFFF
Copy link
Member

@copilot FIlter out any nil values in the supplied []*string

@codecov-commenter
Copy link

Codecov Report

❌ Patch coverage is 80.55556% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 50.40%. Comparing base (6dcea25) to head (43a7403).

Files with missing lines Patch % Lines
deployment/managementgroup.go 80.28% 11 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     Azure/alzlib#231      +/-   ##
==========================================
+ Coverage   49.96%   50.40%   +0.44%     
==========================================
  Files          49       49              
  Lines        3929     3966      +37     
==========================================
+ Hits         1963     1999      +36     
  Misses       1704     1704              
- Partials      262      263       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

- Modified WithNotScopes to filter out nil values from the supplied []*string slice
- Only non-nil values are validated and assigned to NotScopes
- Updated test to verify nil values are properly filtered out

Co-authored-by: matt-FFFFFF <16320656+matt-FFFFFF@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Nov 21, 2025

@copilot FIlter out any nil values in the supplied []*string

Filtered out nil values in commit c38953e. The WithNotScopes function now creates a filtered slice containing only non-nil values before validation and assignment. Test coverage includes verification that nil values are properly excluded from the result.

@matt-FFFFFF matt-FFFFFF marked this pull request as ready for review November 21, 2025 10:44
@matt-FFFFFF matt-FFFFFF changed the title refactor(deployment): implement functional options pattern for ModifyPolicyAssignment refactor(deployment): implement functional options pattern for ModifyPolicyAssignment + include notScopes Nov 21, 2025
Copy link
Member

@jaredfholgate jaredfholgate left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a question about non-compliance messages. This is regards the templating, etc.

@matt-FFFFFF matt-FFFFFF added this pull request to the merge queue Nov 23, 2025
Merged via the queue into main with commit 11971db Nov 23, 2025
8 checks passed
@matt-FFFFFF matt-FFFFFF deleted the copilot/refactor-functional-options-pattern branch November 23, 2025 08:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor(deployment): implement functional options pattern on func (mg *HierarchyManagementGroup) ModifyPolicyAssignment()

4 participants