|
| 1 | +# Implement Automated Actions for Policy Violations |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +Replace the `LoggingActionService` with a full implementation that executes configured actions (create-issue, archive-repo) for policy violations, with proper duplicate prevention, error handling, and audit logging. |
| 6 | + |
| 7 | +## Implementation Steps |
| 8 | + |
| 9 | +### 1. Create IssueDetails Model |
| 10 | + |
| 11 | +**File**: `10xGitHubPolicies.App/Services/Configuration/Models/IssueDetails.cs` |
| 12 | + |
| 13 | +Create a new model class to represent issue configuration: |
| 14 | + |
| 15 | +```csharp |
| 16 | +public class IssueDetails |
| 17 | +{ |
| 18 | + [YamlMember(Alias = "title")] |
| 19 | + public string Title { get; set; } |
| 20 | + |
| 21 | + [YamlMember(Alias = "body")] |
| 22 | + public string Body { get; set; } |
| 23 | + |
| 24 | + [YamlMember(Alias = "labels")] |
| 25 | + public List<string> Labels { get; set; } = new(); |
| 26 | +} |
| 27 | +``` |
| 28 | + |
| 29 | +### 2. Update PolicyConfig Model |
| 30 | + |
| 31 | +**File**: `10xGitHubPolicies.App/Services/Configuration/Models/PolicyConfig.cs` |
| 32 | + |
| 33 | +Add missing properties to match the documented configuration format: |
| 34 | + |
| 35 | +```csharp |
| 36 | +public class PolicyConfig |
| 37 | +{ |
| 38 | + [YamlMember(Alias = "name")] |
| 39 | + public string Name { get; set; } |
| 40 | + |
| 41 | + [YamlMember(Alias = "type")] |
| 42 | + public string Type { get; set; } |
| 43 | + |
| 44 | + [YamlMember(Alias = "action")] |
| 45 | + public string Action { get; set; } |
| 46 | + |
| 47 | + [YamlMember(Alias = "issue_details")] |
| 48 | + public IssueDetails IssueDetails { get; set; } |
| 49 | +} |
| 50 | +``` |
| 51 | + |
| 52 | +### 3. Implement ActionService |
| 53 | + |
| 54 | +**File**: `10xGitHubPolicies.App/Services/Action/ActionService.cs` |
| 55 | + |
| 56 | +Create a new implementation that replaces `LoggingActionService`: |
| 57 | + |
| 58 | +**Key responsibilities:** |
| 59 | + |
| 60 | +- Retrieve violations for the scan from database |
| 61 | +- Load policy configuration to determine actions |
| 62 | +- For each violation: |
| 63 | + - Match violation to policy configuration by PolicyType |
| 64 | + - Execute the configured action (create-issue or archive-repo) |
| 65 | + - Log the action to ActionLog table |
| 66 | + - Handle errors gracefully |
| 67 | + |
| 68 | +**Core logic:** |
| 69 | + |
| 70 | +```csharp |
| 71 | +public async Task ProcessActionsForScanAsync(int scanId) |
| 72 | +{ |
| 73 | + // 1. Load violations with related entities (Repository, Policy) |
| 74 | + var violations = await _dbContext.PolicyViolations |
| 75 | + .Include(v => v.Repository) |
| 76 | + .Include(v => v.Policy) |
| 77 | + .Where(v => v.ScanId == scanId) |
| 78 | + .ToListAsync(); |
| 79 | + |
| 80 | + // 2. Get configuration to determine actions |
| 81 | + var config = await _configurationService.GetConfigAsync(); |
| 82 | + |
| 83 | + // 3. Process each violation |
| 84 | + foreach (var violation in violations) |
| 85 | + { |
| 86 | + var policyConfig = config.Policies |
| 87 | + .FirstOrDefault(p => p.Type == violation.Policy.PolicyKey); |
| 88 | + |
| 89 | + if (policyConfig == null) continue; |
| 90 | + |
| 91 | + // 4. Execute action based on configuration |
| 92 | + if (policyConfig.Action == "create-issue") |
| 93 | + { |
| 94 | + await CreateIssueForViolationAsync(violation, policyConfig); |
| 95 | + } |
| 96 | + else if (policyConfig.Action == "archive-repo") |
| 97 | + { |
| 98 | + await ArchiveRepositoryForViolationAsync(violation, policyConfig); |
| 99 | + } |
| 100 | + } |
| 101 | +} |
| 102 | +``` |
| 103 | + |
| 104 | +**Duplicate Prevention for Issues (US-010):** |
| 105 | + |
| 106 | +- Before creating an issue, check for existing open issues with the same title and label |
| 107 | +- Use `IGitHubService` to search existing issues in the repository |
| 108 | +- Skip issue creation if duplicate found, log the skip action |
| 109 | + |
| 110 | +**Error Handling:** |
| 111 | + |
| 112 | +- Wrap each action in try-catch to prevent one failure from blocking others |
| 113 | +- Log failures with details to ActionLog table with Status = "Failed" |
| 114 | +- Continue processing remaining violations even if one fails |
| 115 | + |
| 116 | +### 4. Update Service Registration |
| 117 | + |
| 118 | +**File**: `10xGitHubPolicies.App/Program.cs` |
| 119 | + |
| 120 | +Replace the service registration: |
| 121 | + |
| 122 | +```csharp |
| 123 | +// Change from: |
| 124 | +builder.Services.AddScoped<IActionService, LoggingActionService>(); |
| 125 | + |
| 126 | +// To: |
| 127 | +builder.Services.AddScoped<IActionService, ActionService>(); |
| 128 | +``` |
| 129 | + |
| 130 | +### 5. Add GitHub Service Methods (if missing) |
| 131 | + |
| 132 | +**File**: `10xGitHubPolicies.App/Services/GitHub/IGitHubService.cs` and `GitHubService.cs` |
| 133 | + |
| 134 | +Verify these methods exist (they should based on documentation): |
| 135 | + |
| 136 | +- `Task<Issue> CreateIssueAsync(long repositoryId, string title, string body, IEnumerable<string> labels)` |
| 137 | +- `Task ArchiveRepositoryAsync(long repositoryId)` |
| 138 | + |
| 139 | +If a method to check for existing issues doesn't exist, add: |
| 140 | + |
| 141 | +- `Task<IReadOnlyList<Issue>> GetOpenIssuesAsync(long repositoryId, string label)` |
| 142 | + |
| 143 | +### 6. Update ActionLog Usage |
| 144 | + |
| 145 | +Ensure ActionLog entries are created for each action with: |
| 146 | + |
| 147 | +- `ActionType`: "create-issue" or "archive-repo" |
| 148 | +- `Status`: "Success", "Failed", or "Skipped" |
| 149 | +- `Details`: JSON or text with action details/error messages |
| 150 | +- `Timestamp`: DateTime.UtcNow |
| 151 | + |
| 152 | +### 7. Delete LoggingActionService |
| 153 | + |
| 154 | +**File**: `10xGitHubPolicies.App/Services/Action/LoggingActionService.cs` |
| 155 | + |
| 156 | +Remove this file after ActionService is implemented and registered. |
| 157 | + |
| 158 | +### 8. Cleanup Test Endpoints |
| 159 | + |
| 160 | +**File**: `10xGitHubPolicies.App/Program.cs` |
| 161 | + |
| 162 | +Remove the test/verification endpoints that are no longer needed: |
| 163 | + |
| 164 | +- Remove `/verify-scan` endpoint (lines 78-90) - functionality exists in UI via "Scan Now" button |
| 165 | +- Remove `/log-job` endpoint (lines 93-97) - was only for testing Hangfire |
| 166 | + |
| 167 | +## Key Considerations |
| 168 | + |
| 169 | +**Dependencies:** |
| 170 | + |
| 171 | +- `IGitHubService`: For GitHub API operations |
| 172 | +- `IConfigurationService`: To retrieve policy configurations |
| 173 | +- `ApplicationDbContext`: For database operations |
| 174 | +- `ILogger<ActionService>`: For logging |
| 175 | + |
| 176 | +**Service Lifetime:** Scoped (matches current registration) |
| 177 | + |
| 178 | +**Thread Safety:** Not required - Hangfire processes jobs sequentially per worker |
| 179 | + |
| 180 | +**Idempotency:** Actions should be safe to retry (duplicate prevention for issues, archive is idempotent) |
| 181 | + |
| 182 | +**Testing:** After implementation, test via: |
| 183 | + |
| 184 | +1. Use the "Scan Now" button in the UI dashboard to trigger a scan |
| 185 | +2. Monitor Hangfire dashboard at `/hangfire` for job execution |
| 186 | +3. Check ActionLog table for action records |
| 187 | +4. Verify GitHub issues created and repositories archived |
| 188 | + |
| 189 | +### To-dos |
| 190 | + |
| 191 | +- [ ] Create IssueDetails model class for YAML configuration |
| 192 | +- [ ] Add Name and IssueDetails properties to PolicyConfig model |
| 193 | +- [ ] Create ActionService with create-issue and archive-repo logic |
| 194 | +- [ ] Implement duplicate issue detection logic in ActionService |
| 195 | +- [ ] Replace LoggingActionService with ActionService in Program.cs |
| 196 | +- [ ] Verify and add any missing GitHub service methods for issue checking |
| 197 | +- [ ] Delete LoggingActionService.cs file |
| 198 | +- [ ] Remove test endpoints (/verify-scan and /log-job) from Program.cs |
0 commit comments