pg_sql_triggers 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +144 -0
  3. data/COVERAGE.md +26 -19
  4. data/Goal.md +276 -155
  5. data/README.md +27 -1
  6. data/app/assets/javascripts/pg_sql_triggers/trigger_actions.js +50 -0
  7. data/app/controllers/concerns/pg_sql_triggers/error_handling.rb +56 -0
  8. data/app/controllers/concerns/pg_sql_triggers/kill_switch_protection.rb +66 -0
  9. data/app/controllers/concerns/pg_sql_triggers/permission_checking.rb +117 -0
  10. data/app/controllers/pg_sql_triggers/application_controller.rb +10 -62
  11. data/app/controllers/pg_sql_triggers/audit_logs_controller.rb +102 -0
  12. data/app/controllers/pg_sql_triggers/dashboard_controller.rb +4 -9
  13. data/app/controllers/pg_sql_triggers/tables_controller.rb +30 -4
  14. data/app/controllers/pg_sql_triggers/triggers_controller.rb +3 -21
  15. data/app/helpers/pg_sql_triggers/permissions_helper.rb +43 -0
  16. data/app/models/pg_sql_triggers/audit_log.rb +106 -0
  17. data/app/models/pg_sql_triggers/trigger_registry.rb +178 -9
  18. data/app/views/layouts/pg_sql_triggers/application.html.erb +26 -6
  19. data/app/views/pg_sql_triggers/audit_logs/index.html.erb +177 -0
  20. data/app/views/pg_sql_triggers/dashboard/index.html.erb +33 -8
  21. data/app/views/pg_sql_triggers/tables/index.html.erb +76 -3
  22. data/app/views/pg_sql_triggers/tables/show.html.erb +17 -4
  23. data/app/views/pg_sql_triggers/triggers/_drop_modal.html.erb +16 -7
  24. data/app/views/pg_sql_triggers/triggers/_re_execute_modal.html.erb +16 -7
  25. data/app/views/pg_sql_triggers/triggers/show.html.erb +26 -6
  26. data/config/routes.rb +2 -0
  27. data/db/migrate/20260103000001_create_pg_sql_triggers_audit_log.rb +28 -0
  28. data/docs/README.md +15 -5
  29. data/docs/api-reference.md +191 -0
  30. data/docs/audit-trail.md +413 -0
  31. data/docs/configuration.md +6 -6
  32. data/docs/permissions.md +369 -0
  33. data/docs/troubleshooting.md +486 -0
  34. data/docs/ui-guide.md +211 -0
  35. data/docs/web-ui.md +257 -34
  36. data/lib/pg_sql_triggers/errors.rb +245 -0
  37. data/lib/pg_sql_triggers/generator/service.rb +32 -0
  38. data/lib/pg_sql_triggers/permissions/checker.rb +9 -2
  39. data/lib/pg_sql_triggers/registry.rb +141 -8
  40. data/lib/pg_sql_triggers/sql/kill_switch.rb +33 -5
  41. data/lib/pg_sql_triggers/testing/function_tester.rb +2 -0
  42. data/lib/pg_sql_triggers/version.rb +1 -1
  43. data/lib/pg_sql_triggers.rb +3 -6
  44. metadata +29 -6
  45. data/docs/screenshots/.gitkeep +0 -1
  46. data/docs/screenshots/Generate Trigger.png +0 -0
  47. data/docs/screenshots/Triggers Page.png +0 -0
  48. data/docs/screenshots/kill error.png +0 -0
  49. data/docs/screenshots/kill modal for migration down.png +0 -0
data/docs/web-ui.md CHANGED
@@ -9,6 +9,7 @@ The PgSqlTriggers web interface provides a visual dashboard for managing trigger
9
9
  - [Managing Triggers](#managing-triggers)
10
10
  - [Migration Management](#migration-management)
11
11
  - [SQL Capsules](#sql-capsules)
12
+ - [Audit Log](#audit-log)
12
13
  - [Permissions and Safety](#permissions-and-safety)
13
14
 
14
15
  ## Accessing the Web UI
@@ -34,11 +35,12 @@ The dashboard provides a comprehensive view of your trigger ecosystem.
34
35
 
35
36
  ### Main Features
36
37
 
37
- 1. **Trigger List**: View all triggers with their current status
38
+ 1. **Trigger List**: View all triggers with their current status and "Last Applied" timestamps
38
39
  2. **Drift Detection**: Visual indicators for drift states
39
40
  3. **Migration Status**: See pending and applied migrations
40
- 4. **Quick Actions**: Enable/disable triggers, run migrations
41
+ 4. **Quick Actions**: Enable/disable triggers, drop/re-execute triggers (based on permissions), run migrations
41
42
  5. **Kill Switch Status**: Production environment indicator
43
+ 6. **Audit Trail**: All operations are logged with actor information and viewable via Audit Log UI
42
44
 
43
45
  ![Dashboard Screenshot](screenshots/dashboard.png)
44
46
 
@@ -50,32 +52,116 @@ The dashboard provides a comprehensive view of your trigger ecosystem.
50
52
  - **○ Gray**: Disabled
51
53
  - **? Purple**: Unknown
52
54
 
55
+ ## Database Tables & Triggers
56
+
57
+ The Database Tables & Triggers page provides a comprehensive view of all tables in your database and their associated triggers. This page helps you understand which tables have triggers and which don't, making it easier to manage your trigger ecosystem.
58
+
59
+ ### Accessing the Tables Page
60
+
61
+ 1. Click "View Tables" from the dashboard
62
+ 2. Or navigate directly to `/pg_sql_triggers/tables`
63
+
64
+ ### Statistics Overview
65
+
66
+ The page displays three key statistics:
67
+ - **Tables with Triggers**: Count of tables that have at least one trigger
68
+ - **Tables without Triggers**: Count of tables that have no triggers
69
+ - **Total Tables**: Total count of all tables in the database
70
+
71
+ ### Filtering Tables
72
+
73
+ Use the filter controls to view different subsets of tables:
74
+ - **All Tables**: Shows all tables regardless of trigger status
75
+ - **With Triggers**: Shows only tables that have at least one trigger (default)
76
+ - **Without Triggers**: Shows only tables that have no triggers
77
+
78
+ The active filter is highlighted with a colored background. Click any filter button to switch views.
79
+
80
+ ### Pagination
81
+
82
+ When you have many tables, the list is paginated for better performance:
83
+ - **Default**: 20 tables per page
84
+ - **Configurable**: Choose 10, 20, 50, or 100 tables per page
85
+ - **Navigation**: Use Previous/Next buttons to move between pages
86
+ - **Filter Preservation**: Your selected filter is preserved when navigating pages
87
+
88
+ ### Table Information
89
+
90
+ Each table row displays:
91
+ - **Table Name**: The name of the database table
92
+ - **Trigger Count**: Number of triggers on the table (badge indicator)
93
+ - **Trigger Names & Functions**: List of all triggers with their function names
94
+ - Registry triggers (blue border) - Triggers managed by pg_sql_triggers
95
+ - Database-only triggers (yellow border) - Triggers not in the registry
96
+ - **Status**: Summary of enabled/disabled triggers
97
+ - **Actions**:
98
+ - "View Details" - Navigate to the table detail page
99
+ - "Create Trigger" - Generate a new trigger for this table
100
+
101
+ ### Table Detail Page
102
+
103
+ Click "View Details" on any table to see:
104
+ - **Table Columns**: Complete list of columns with data types and nullability
105
+ - **Registered Triggers**: All triggers managed by pg_sql_triggers with full details
106
+ - **Database Triggers**: Triggers that exist in the database but aren't in the registry
107
+
108
+ From the table detail page, you can:
109
+ - Enable/disable individual triggers (Operator+ permission)
110
+ - Drop triggers (Admin permission)
111
+ - Re-execute drifted triggers (Admin permission)
112
+ - Create new triggers for the table
113
+
53
114
  ## Managing Triggers
54
115
 
55
116
  ### Viewing Trigger Details
56
117
 
57
- Click on any trigger to view:
58
- - Current status and drift state
118
+ Click on any trigger name (from dashboard or table view) to access the trigger detail page. The detail page includes:
119
+
120
+ #### Navigation
121
+ - **Breadcrumb Navigation**: Dashboard → Tables → Table Name → Trigger Name
122
+ - **Quick Links**: Back to Dashboard, View Table
123
+
124
+ #### Summary Panel
125
+ - Current status and drift state with visual indicators
59
126
  - Table and function information
60
- - Version history
61
- - Enabled/disabled state
62
- - Environment configuration
127
+ - Version, source (DSL/generated/manual_sql), and environment
128
+ - **Last Applied**: Human-readable timestamp showing when trigger was last applied (e.g., "2 hours ago")
129
+ - **Last Verified**: Timestamp of last drift verification
130
+ - **Created At**: Original creation timestamp
131
+
132
+ #### SQL Information
133
+ - **Function Body**: Complete PL/pgSQL function code
134
+ - **Trigger Configuration**: Events, timing, conditions
135
+ - **SQL Diff View**: If drift detected, shows expected vs actual SQL side-by-side
136
+
137
+ #### Actions
138
+ All action buttons available based on permissions:
139
+ - Enable/Disable (Operator+)
140
+ - Re-Execute (Admin, shown only when drift detected)
141
+ - Drop (Admin)
63
142
 
64
143
  ### Enabling/Disabling Triggers
65
144
 
145
+ Triggers can be enabled or disabled from multiple locations:
146
+ - **Dashboard**: Quick action buttons in the trigger table (Operator+ permission)
147
+ - **Table Detail Page**: Action buttons for each trigger (Operator+ permission)
148
+ - **Trigger Detail Page**: Full action panel (Operator+ permission)
149
+
66
150
  #### Enable a Trigger
67
151
 
68
- 1. Navigate to the trigger in the dashboard
69
- 2. Click the "Enable" button
152
+ 1. Navigate to the trigger (dashboard, table view, or trigger detail page)
153
+ 2. Click the "Enable" button (green button)
70
154
  3. In production environments, enter the confirmation text when prompted
71
- 4. Confirm the action
155
+ 4. Confirm the action in the modal
156
+ 5. The trigger will be enabled and the operation logged to the audit trail
72
157
 
73
158
  #### Disable a Trigger
74
159
 
75
- 1. Navigate to the trigger in the dashboard
76
- 2. Click the "Disable" button
160
+ 1. Navigate to the trigger (dashboard, table view, or trigger detail page)
161
+ 2. Click the "Disable" button (red button)
77
162
  3. In production environments, enter the confirmation text when prompted
78
- 4. Confirm the action
163
+ 4. Confirm the action in the modal
164
+ 5. The trigger will be disabled and the operation logged to the audit trail
79
165
 
80
166
  ### Viewing Drift Status
81
167
 
@@ -99,14 +185,18 @@ Available actions depend on trigger state and your permissions:
99
185
 
100
186
  ### Drop Trigger
101
187
 
102
- The drop action permanently removes a trigger from the database and registry.
188
+ The drop action permanently removes a trigger from the database and registry. Available from:
189
+ - **Dashboard**: "Drop" button in trigger table (Admin only)
190
+ - **Table Detail Page**: "Drop Trigger" button (Admin only)
191
+ - **Trigger Detail Page**: "Drop Trigger" button (Admin only)
103
192
 
104
- 1. Navigate to the trigger detail page
105
- 2. Click the "Drop Trigger" button
193
+ **Steps**:
194
+ 1. Navigate to the trigger (any view with drop button)
195
+ 2. Click the "Drop Trigger" button (gray button with warning icon)
106
196
  3. A modal will appear requiring:
107
197
  - **Reason**: Explanation for dropping the trigger (required for audit trail)
108
198
  - **Confirmation**: In protected environments, type the exact confirmation text shown
109
- 4. Review the warning message
199
+ 4. Review the warning message carefully
110
200
  5. Click "Drop Trigger" to confirm
111
201
 
112
202
  **Important Notes**:
@@ -115,26 +205,34 @@ The drop action permanently removes a trigger from the database and registry.
115
205
  - Protected by kill switch in production environments
116
206
  - Reason is logged for compliance and audit purposes
117
207
  - The trigger is removed from both the database and the registry
208
+ - Operation is logged to audit trail with actor information and state changes
118
209
 
119
210
  ### Re-Execute Trigger
120
211
 
121
- The re-execute action fixes drifted triggers by dropping and recreating them from the registry definition.
212
+ The re-execute action fixes drifted triggers by dropping and recreating them from the registry definition. Available from:
213
+ - **Dashboard**: "Re-Execute" button in trigger table (Admin only, shown only when drift detected)
214
+ - **Table Detail Page**: "Re-Execute Trigger" button (Admin only, shown only when drift detected)
215
+ - **Trigger Detail Page**: "Re-Execute Trigger" button (Admin only, shown only when drift detected)
122
216
 
123
- 1. Navigate to the trigger detail page
124
- 2. If the trigger is drifted, you'll see a drift warning
125
- 3. Click the "Re-Execute" button
217
+ **Steps**:
218
+ 1. Navigate to the trigger (any view with re-execute button)
219
+ 2. If the trigger is drifted, you'll see a drift warning and the "Re-Execute" button will be visible
220
+ 3. Click the "Re-Execute" button (yellow/warning button)
126
221
  4. A modal will appear showing:
127
- - **Drift Comparison**: Differences between database state and registry definition
222
+ - **Drift Comparison**: Side-by-side differences between expected (registry) and actual (database) SQL
128
223
  - **Reason Field**: Explanation for re-executing (required for audit trail)
129
224
  - **Confirmation**: In protected environments, type the exact confirmation text shown
130
- 5. Review the drift differences to understand what will change
225
+ 5. Review the drift differences carefully to understand what will change
131
226
  6. Click "Re-Execute Trigger" to confirm
132
227
 
133
228
  **What Happens**:
134
229
  1. Current trigger is dropped from the database
135
230
  2. New trigger is created using the registry definition (function_body, events, timing, condition)
136
231
  3. Registry is updated with execution timestamp
137
- 4. Operation is logged with reason and actor information
232
+ 4. Operation is logged to audit trail with:
233
+ - Reason and actor information
234
+ - Before and after state
235
+ - SQL diff information
138
236
 
139
237
  **Important Notes**:
140
238
  - Requires **Admin** permission level
@@ -142,6 +240,7 @@ The re-execute action fixes drifted triggers by dropping and recreating them fro
142
240
  - Reason is logged for compliance and audit purposes
143
241
  - Executes in a database transaction (rolls back on error)
144
242
  - Best used to fix triggers that have drifted from their DSL definition
243
+ - Button only appears when drift is detected
145
244
 
146
245
  ## Migration Management
147
246
 
@@ -281,6 +380,92 @@ SELECT * FROM pg_sql_triggers_registry
281
380
  WHERE trigger_name = 'users_email_validation';
282
381
  ```
283
382
 
383
+ ## Audit Log
384
+
385
+ The Audit Log provides a comprehensive view of all trigger operations performed through the web UI, console APIs, and CLI. This feature is essential for compliance, debugging, and tracking changes to your trigger ecosystem.
386
+
387
+ ### Accessing the Audit Log
388
+
389
+ 1. Navigate to the "Audit Log" link in the main navigation menu
390
+ 2. Or visit `/pg_sql_triggers/audit_logs` directly
391
+
392
+ ### Viewing Audit Log Entries
393
+
394
+ The audit log displays all operations with the following information:
395
+
396
+ - **Time**: When the operation occurred (relative time with exact timestamp on hover)
397
+ - **Trigger**: The trigger name (clickable link to trigger detail page if available)
398
+ - **Operation**: The type of operation performed (e.g., `trigger_enable`, `trigger_drop`, `trigger_re_execute`)
399
+ - **Status**: Success or failure indicator
400
+ - **Environment**: The environment where the operation was performed
401
+ - **Actor**: Who performed the operation (e.g., `UI:user_id`, `Console:email`)
402
+ - **Reason**: Explanation for the operation (for drop/re-execute operations)
403
+ - **Error**: Error message if the operation failed
404
+
405
+ ### Filtering Audit Logs
406
+
407
+ The audit log supports multiple filters to help you find specific entries:
408
+
409
+ 1. **Trigger Name**: Filter by specific trigger name
410
+ 2. **Operation**: Filter by operation type (enable, disable, drop, re_execute, etc.)
411
+ 3. **Status**: Filter by success or failure
412
+ 4. **Environment**: Filter by environment (production, staging, development, etc.)
413
+ 5. **Sort Order**: Sort by date (newest first or oldest first)
414
+
415
+ Click "Apply Filters" to update the view, or "Clear" to remove all filters.
416
+
417
+ ### Exporting Audit Logs
418
+
419
+ To export audit log entries:
420
+
421
+ 1. Apply any desired filters
422
+ 2. Click the "Export CSV" button
423
+ 3. The CSV file will include all entries matching your filters (not just the current page)
424
+ 4. File is named with timestamp: `audit_logs_YYYYMMDD_HHMMSS.csv`
425
+
426
+ The CSV export includes:
427
+ - ID, Trigger Name, Operation, Status, Environment
428
+ - Actor Type and ID
429
+ - Reason and Error Message
430
+ - Created At timestamp
431
+
432
+ ### Pagination
433
+
434
+ The audit log uses pagination to handle large datasets:
435
+
436
+ - Default: 50 entries per page (adjustable via URL parameter)
437
+ - Maximum: 200 entries per page
438
+ - Navigate using "Previous" and "Next" buttons
439
+ - Page numbers and total count displayed
440
+
441
+ ### What Gets Logged
442
+
443
+ All of the following operations are logged to the audit log:
444
+
445
+ - **Enable Trigger**: Success/failure, before/after state
446
+ - **Disable Trigger**: Success/failure, before/after state
447
+ - **Drop Trigger**: Success/failure, reason, state changes
448
+ - **Re-execute Trigger**: Success/failure, reason, drift diff information
449
+ - **SQL Capsule Execution**: Success/failure, capsule details
450
+ - **Migration Operations**: Up, down, and redo operations (infrastructure ready)
451
+
452
+ Each log entry includes:
453
+ - Complete actor information (who performed the operation)
454
+ - Before and after state (for state-changing operations)
455
+ - Operation metadata (reason, confirmation text, environment)
456
+ - Error details (if the operation failed)
457
+ - Timestamp of the operation
458
+
459
+ ### Use Cases
460
+
461
+ Common use cases for the audit log:
462
+
463
+ - **Compliance**: Track all changes for audit requirements
464
+ - **Debugging**: Understand what operations were performed before an issue
465
+ - **Accountability**: See who performed specific operations
466
+ - **Troubleshooting**: Review failed operations and their error messages
467
+ - **Change History**: Track the evolution of your trigger ecosystem over time
468
+
284
469
  ## Permissions and Safety
285
470
 
286
471
  ### Permission Levels
@@ -322,7 +507,13 @@ In protected environments (production, staging), the Web UI enforces additional
322
507
  1. **Status Indicator**: Kill switch badge shows protection status
323
508
  2. **Confirmation Required**: Dangerous operations require typed confirmation
324
509
  3. **Warning Banners**: Visual alerts for production environment
325
- 4. **Audit Logging**: All protected operations are logged
510
+ 4. **Audit Logging**: All protected operations are logged with complete audit trail:
511
+ - Actor information (who performed the operation)
512
+ - Before and after state
513
+ - Operation details (reason, confirmation text)
514
+ - Success/failure status
515
+ - Error messages (if failed)
516
+ - Timestamp of operation
326
517
 
327
518
  ### Configuring Permissions
328
519
 
@@ -332,20 +523,26 @@ Set up custom permission checking in the initializer:
332
523
  # config/initializers/pg_sql_triggers.rb
333
524
  PgSqlTriggers.configure do |config|
334
525
  config.permission_checker = ->(actor, action, environment) {
335
- user = User.find(actor[:id])
526
+ user = User.find_by(id: actor[:id])
527
+ return false unless user
336
528
 
337
529
  case action
338
- when :view
339
- user.present?
340
- when :operate
341
- user.admin? || user.operator?
342
- when :admin
343
- user.admin?
530
+ when :view_triggers, :view_diffs
531
+ user.present? # Viewer level
532
+ when :enable_trigger, :disable_trigger, :apply_trigger, :generate_trigger, :test_trigger, :dry_run_sql
533
+ user.operator? || user.admin? # Operator level
534
+ when :drop_trigger, :execute_sql, :override_drift
535
+ user.admin? # Admin level
344
536
  else
345
537
  false
346
538
  end
347
539
  }
348
540
  end
541
+ ```else
542
+ false
543
+ end
544
+ }
545
+ end
349
546
  ```
350
547
 
351
548
  ## Screenshots
@@ -379,13 +576,39 @@ The preview page displays:
379
576
  ### SQL Capsules
380
577
  ![SQL Capsules](screenshots/sql-capsules.png)
381
578
 
579
+ ## Dashboard Enhancements (v1.3.0+)
580
+
581
+ ### Last Applied Column
582
+
583
+ The dashboard now includes a "Last Applied" column showing when each trigger was last applied to the database:
584
+ - **Human-readable format**: Displays relative time (e.g., "2 hours ago", "3 days ago")
585
+ - **Tooltip**: Hover over the timestamp to see exact date and time
586
+ - **Default sorting**: Dashboard sorted by most recently applied triggers first
587
+ - **Never applied**: Shows "Never" if trigger has never been applied
588
+
589
+ This helps you quickly identify:
590
+ - Which triggers are actively maintained
591
+ - How recently triggers were updated
592
+ - Triggers that may need attention
593
+
594
+ ### Quick Actions in Dashboard
595
+
596
+ The dashboard trigger table now includes quick action buttons:
597
+ - **Enable/Disable**: Toggle trigger state (Operator+ permission)
598
+ - **Drop**: Remove trigger permanently (Admin only)
599
+ - **Re-Execute**: Fix drifted triggers (Admin only, shown only when drift detected)
600
+
601
+ All actions respect permission levels and show/hide buttons based on your role.
602
+
382
603
  ## Tips and Best Practices
383
604
 
384
605
  1. **Check Status Regularly**: Monitor drift detection to catch unexpected changes
385
606
  2. **Use Confirmations**: Don't bypass production confirmations without understanding the impact
386
607
  3. **Test in Development**: Always test UI actions in development before production
387
- 4. **Review Logs**: Check application logs after important operations
388
- 5. **Document Changes**: Add comments when making manual changes via SQL Capsules
608
+ 4. **Review Logs**: Check application logs and audit trail after important operations
609
+ 5. **Document Changes**: Add detailed reasons when dropping or re-executing triggers
610
+ 6. **Monitor Last Applied**: Use the "Last Applied" column to track trigger maintenance activity
611
+ 7. **Breadcrumb Navigation**: Use breadcrumbs on trigger detail page for easy navigation
389
612
 
390
613
  ## Troubleshooting
391
614
 
@@ -0,0 +1,245 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgSqlTriggers
4
+ # Base error class for all PgSqlTriggers errors
5
+ #
6
+ # All errors in PgSqlTriggers inherit from this base class and include
7
+ # error codes for programmatic handling, standardized messages, and
8
+ # recovery suggestions.
9
+ class Error < StandardError
10
+ attr_reader :error_code, :recovery_suggestion, :context
11
+
12
+ def initialize(message = nil, error_code: nil, recovery_suggestion: nil, context: {})
13
+ @context = context || {}
14
+ @error_code = error_code || default_error_code
15
+ @recovery_suggestion = recovery_suggestion || default_recovery_suggestion
16
+ super(message || default_message)
17
+ end
18
+
19
+ # Returns a user-friendly error message suitable for UI display
20
+ def user_message
21
+ msg = message
22
+ msg += "\n\nRecovery: #{recovery_suggestion}" if recovery_suggestion
23
+ msg
24
+ end
25
+
26
+ # Returns error details as a hash for programmatic access
27
+ def to_h
28
+ {
29
+ error_class: self.class.name,
30
+ error_code: error_code,
31
+ message: message,
32
+ recovery_suggestion: recovery_suggestion,
33
+ context: context
34
+ }
35
+ end
36
+
37
+ protected
38
+
39
+ def default_error_code
40
+ # Convert class name to error code (e.g., "PermissionError" -> "PERMISSION_ERROR")
41
+ class_name = self.class.name.split("::").last
42
+ class_name.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
43
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
44
+ .upcase
45
+ end
46
+
47
+ def default_message
48
+ "An error occurred in PgSqlTriggers"
49
+ end
50
+
51
+ def default_recovery_suggestion
52
+ "Please check the logs for more details and contact support if the issue persists."
53
+ end
54
+ end
55
+
56
+ # Error raised when permission checks fail
57
+ #
58
+ # @example
59
+ # raise PgSqlTriggers::PermissionError.new(
60
+ # "Permission denied: enable_trigger requires Operator level access",
61
+ # error_code: "PERMISSION_DENIED",
62
+ # recovery_suggestion: "Contact your administrator to request Operator or Admin access",
63
+ # context: { action: :enable_trigger, required_role: "Operator" }
64
+ # )
65
+ class PermissionError < Error
66
+ def default_error_code
67
+ "PERMISSION_DENIED"
68
+ end
69
+
70
+ def default_message
71
+ "Permission denied for this operation"
72
+ end
73
+
74
+ def default_recovery_suggestion
75
+ if context[:required_role]
76
+ "This operation requires #{context[:required_role]} level access. " \
77
+ "Contact your administrator to request appropriate permissions."
78
+ else
79
+ "This operation requires elevated permissions. Contact your administrator."
80
+ end
81
+ end
82
+ end
83
+
84
+ # Error raised when kill switch blocks an operation
85
+ #
86
+ # @example
87
+ # raise PgSqlTriggers::KillSwitchError.new(
88
+ # "Kill switch is active for production environment",
89
+ # error_code: "KILL_SWITCH_ACTIVE",
90
+ # recovery_suggestion: "Provide confirmation text to override: EXECUTE OPERATION_NAME",
91
+ # context: { operation: :trigger_enable, environment: "production" }
92
+ # )
93
+ class KillSwitchError < Error
94
+ def default_error_code
95
+ "KILL_SWITCH_ACTIVE"
96
+ end
97
+
98
+ def default_message
99
+ "Kill switch is active for this environment"
100
+ end
101
+
102
+ def default_recovery_suggestion
103
+ ctx = @context || {}
104
+ ctx[:operation] || "this operation"
105
+ environment = ctx[:environment] || "this environment"
106
+ "Kill switch is active for #{environment}. " \
107
+ "To override, provide the required confirmation text. " \
108
+ "For CLI/rake tasks, use: KILL_SWITCH_OVERRIDE=true CONFIRMATION_TEXT=\"...\" rake your:task"
109
+ end
110
+ end
111
+
112
+ # Error raised when drift is detected
113
+ #
114
+ # @example
115
+ # raise PgSqlTriggers::DriftError.new(
116
+ # "Trigger 'users_email_validation' has drifted from definition",
117
+ # error_code: "DRIFT_DETECTED",
118
+ # recovery_suggestion: "Run migration to sync trigger, or re-execute trigger to apply current definition",
119
+ # context: { trigger_name: "users_email_validation", drift_type: "function_body" }
120
+ # )
121
+ class DriftError < Error
122
+ def default_error_code
123
+ "DRIFT_DETECTED"
124
+ end
125
+
126
+ def default_message
127
+ "Trigger has drifted from its definition"
128
+ end
129
+
130
+ def default_recovery_suggestion
131
+ trigger_name = context[:trigger_name] || "trigger"
132
+ "Trigger '#{trigger_name}' has drifted. " \
133
+ "Run 'rake trigger:migrate' to sync the trigger, or use the re-execute feature " \
134
+ "to apply the current definition."
135
+ end
136
+ end
137
+
138
+ # Error raised when validation fails
139
+ #
140
+ # @example
141
+ # raise PgSqlTriggers::ValidationError.new(
142
+ # "Invalid trigger definition: table name is required",
143
+ # error_code: "VALIDATION_FAILED",
144
+ # recovery_suggestion: "Ensure all required fields are provided in the trigger definition",
145
+ # context: { field: :table_name, errors: ["is required"] }
146
+ # )
147
+ class ValidationError < Error
148
+ def default_error_code
149
+ "VALIDATION_FAILED"
150
+ end
151
+
152
+ def default_message
153
+ "Validation failed"
154
+ end
155
+
156
+ def default_recovery_suggestion
157
+ if context[:field]
158
+ "Please fix the #{context[:field]} field and try again."
159
+ else
160
+ "Please review the input and ensure all required fields are provided."
161
+ end
162
+ end
163
+ end
164
+
165
+ # Error raised when SQL execution fails
166
+ #
167
+ # @example
168
+ # raise PgSqlTriggers::ExecutionError.new(
169
+ # "SQL execution failed: syntax error near 'INVALID'",
170
+ # error_code: "EXECUTION_FAILED",
171
+ # recovery_suggestion: "Review SQL syntax and ensure all references are valid",
172
+ # context: { sql: "SELECT * FROM...", database_error: "..." }
173
+ # )
174
+ class ExecutionError < Error
175
+ def default_error_code
176
+ "EXECUTION_FAILED"
177
+ end
178
+
179
+ def default_message
180
+ "SQL execution failed"
181
+ end
182
+
183
+ def default_recovery_suggestion
184
+ if context[:database_error]
185
+ "Review the SQL syntax and database error. Ensure all table and column names are correct."
186
+ else
187
+ "Review the SQL and ensure it is valid PostgreSQL syntax."
188
+ end
189
+ end
190
+ end
191
+
192
+ # Error raised when unsafe migrations are attempted
193
+ #
194
+ # @example
195
+ # raise PgSqlTriggers::UnsafeMigrationError.new(
196
+ # "Migration contains unsafe DROP + CREATE operations",
197
+ # error_code: "UNSAFE_MIGRATION",
198
+ # recovery_suggestion: "Review migration safety or set allow_unsafe_migrations=true",
199
+ # context: { violations: [...] }
200
+ # )
201
+ class UnsafeMigrationError < Error
202
+ def default_error_code
203
+ "UNSAFE_MIGRATION"
204
+ end
205
+
206
+ def default_message
207
+ "Migration contains unsafe operations"
208
+ end
209
+
210
+ def default_recovery_suggestion
211
+ "Review the migration for unsafe operations. " \
212
+ "If you are certain the migration is safe, you can set " \
213
+ "PgSqlTriggers.configure { |c| c.allow_unsafe_migrations = true } " \
214
+ "or use the kill switch override mechanism."
215
+ end
216
+ end
217
+
218
+ # Error raised when a trigger is not found
219
+ #
220
+ # @example
221
+ # raise PgSqlTriggers::NotFoundError.new(
222
+ # "Trigger 'users_email_validation' not found",
223
+ # error_code: "TRIGGER_NOT_FOUND",
224
+ # recovery_suggestion: "Verify trigger name or create the trigger first",
225
+ # context: { trigger_name: "users_email_validation" }
226
+ # )
227
+ class NotFoundError < Error
228
+ def default_error_code
229
+ "NOT_FOUND"
230
+ end
231
+
232
+ def default_message
233
+ "Resource not found"
234
+ end
235
+
236
+ def default_recovery_suggestion
237
+ if context[:trigger_name]
238
+ "Trigger '#{context[:trigger_name]}' not found. " \
239
+ "Verify the trigger name or create the trigger first using the generator or DSL."
240
+ else
241
+ "The requested resource was not found. Verify the identifier and try again."
242
+ end
243
+ end
244
+ end
245
+ end