pg_sql_triggers 1.3.0 → 1.5.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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/.erb_lint.yml +0 -0
  3. data/.rspec +0 -0
  4. data/.rubocop.yml +6 -16
  5. data/AGENTS.md +8 -0
  6. data/CHANGELOG.md +354 -0
  7. data/COVERAGE.md +39 -41
  8. data/LICENSE +0 -0
  9. data/README.md +44 -26
  10. data/RELEASE.md +0 -0
  11. data/Rakefile +5 -0
  12. data/app/assets/javascripts/pg_sql_triggers/application.js +0 -0
  13. data/app/assets/javascripts/pg_sql_triggers/trigger_actions.js +0 -0
  14. data/app/assets/stylesheets/pg_sql_triggers/application.css +0 -0
  15. data/app/controllers/concerns/pg_sql_triggers/error_handling.rb +0 -0
  16. data/app/controllers/concerns/pg_sql_triggers/kill_switch_protection.rb +0 -0
  17. data/app/controllers/concerns/pg_sql_triggers/permission_checking.rb +6 -5
  18. data/app/controllers/pg_sql_triggers/application_controller.rb +0 -0
  19. data/app/controllers/pg_sql_triggers/audit_logs_controller.rb +81 -64
  20. data/app/controllers/pg_sql_triggers/dashboard_controller.rb +111 -34
  21. data/app/controllers/pg_sql_triggers/migrations_controller.rb +13 -14
  22. data/app/controllers/pg_sql_triggers/tables_controller.rb +8 -0
  23. data/app/controllers/pg_sql_triggers/triggers_controller.rb +1 -0
  24. data/app/helpers/pg_sql_triggers/dashboard_helper.rb +19 -0
  25. data/app/helpers/pg_sql_triggers/permissions_helper.rb +3 -2
  26. data/app/models/pg_sql_triggers/application_record.rb +0 -0
  27. data/app/models/pg_sql_triggers/audit_log.rb +29 -47
  28. data/app/models/pg_sql_triggers/trigger_registry.rb +137 -74
  29. data/app/views/layouts/pg_sql_triggers/application.html.erb +0 -1
  30. data/app/views/pg_sql_triggers/audit_logs/index.html.erb +9 -5
  31. data/app/views/pg_sql_triggers/dashboard/index.html.erb +107 -27
  32. data/app/views/pg_sql_triggers/shared/_confirmation_modal.html.erb +0 -0
  33. data/app/views/pg_sql_triggers/shared/_kill_switch_status.html.erb +0 -0
  34. data/app/views/pg_sql_triggers/tables/index.html.erb +27 -18
  35. data/app/views/pg_sql_triggers/tables/show.html.erb +0 -2
  36. data/app/views/pg_sql_triggers/triggers/_drop_modal.html.erb +0 -0
  37. data/app/views/pg_sql_triggers/triggers/_re_execute_modal.html.erb +0 -0
  38. data/app/views/pg_sql_triggers/triggers/show.html.erb +33 -0
  39. data/config/initializers/pg_sql_triggers.rb +0 -0
  40. data/config/routes.rb +0 -14
  41. data/db/migrate/{20251222000001_create_pg_sql_triggers_tables.rb → 20251222104327_create_pg_sql_triggers_tables.rb} +0 -0
  42. data/db/migrate/20251229071916_add_timing_to_pg_sql_triggers_registry.rb +0 -0
  43. data/db/migrate/{20260103000001_create_pg_sql_triggers_audit_log.rb → 20260103114508_create_pg_sql_triggers_audit_log.rb} +0 -0
  44. data/db/migrate/20260228162233_add_for_each_to_pg_sql_triggers_registry.rb +8 -0
  45. data/db/migrate/20260412185841_add_constraint_deferral_to_pg_sql_triggers_registry.rb +9 -0
  46. data/docs/README.md +3 -0
  47. data/docs/api-reference.md +176 -152
  48. data/docs/audit-trail.md +1 -1
  49. data/docs/configuration.md +196 -3
  50. data/docs/getting-started.md +31 -16
  51. data/docs/kill-switch.md +0 -0
  52. data/docs/permissions.md +6 -9
  53. data/docs/troubleshooting.md +0 -0
  54. data/docs/ui-guide.md +0 -0
  55. data/docs/usage-guide.md +112 -67
  56. data/docs/web-ui.md +3 -103
  57. data/lib/generators/pg_sql_triggers/install_generator.rb +0 -0
  58. data/lib/generators/pg_sql_triggers/templates/README +0 -0
  59. data/lib/generators/pg_sql_triggers/templates/create_pg_sql_triggers_tables.rb +0 -0
  60. data/lib/generators/pg_sql_triggers/templates/initializer.rb +14 -0
  61. data/lib/generators/pg_sql_triggers/templates/trigger_dsl.rb.tt +11 -0
  62. data/lib/generators/pg_sql_triggers/templates/trigger_migration.rb.erb +0 -0
  63. data/lib/generators/pg_sql_triggers/templates/trigger_migration_full.rb.tt +29 -0
  64. data/lib/generators/pg_sql_triggers/trigger_generator.rb +83 -0
  65. data/lib/generators/pg_sql_triggers/trigger_migration_generator.rb +0 -0
  66. data/lib/pg_sql_triggers/alerting.rb +77 -0
  67. data/lib/pg_sql_triggers/database_introspection.rb +0 -0
  68. data/lib/pg_sql_triggers/deferral_checksum.rb +54 -0
  69. data/lib/pg_sql_triggers/drift/db_queries.rb +26 -13
  70. data/lib/pg_sql_triggers/drift/detector.rb +59 -38
  71. data/lib/pg_sql_triggers/drift/reporter.rb +0 -0
  72. data/lib/pg_sql_triggers/drift.rb +5 -0
  73. data/lib/pg_sql_triggers/dsl/trigger_definition.rb +68 -20
  74. data/lib/pg_sql_triggers/dsl.rb +0 -0
  75. data/lib/pg_sql_triggers/engine.rb +49 -0
  76. data/lib/pg_sql_triggers/errors.rb +0 -0
  77. data/lib/pg_sql_triggers/events_checksum.rb +114 -0
  78. data/lib/pg_sql_triggers/migration.rb +5 -6
  79. data/lib/pg_sql_triggers/migrator/pre_apply_comparator.rb +85 -82
  80. data/lib/pg_sql_triggers/migrator/pre_apply_diff_reporter.rb +0 -0
  81. data/lib/pg_sql_triggers/migrator/safety_validator.rb +34 -12
  82. data/lib/pg_sql_triggers/migrator.rb +137 -94
  83. data/lib/pg_sql_triggers/permissions/checker.rb +12 -15
  84. data/lib/pg_sql_triggers/permissions.rb +1 -0
  85. data/lib/pg_sql_triggers/rake_development_boot.rb +65 -0
  86. data/lib/pg_sql_triggers/registry/manager.rb +60 -21
  87. data/lib/pg_sql_triggers/registry/validator.rb +287 -6
  88. data/lib/pg_sql_triggers/registry.rb +0 -0
  89. data/lib/pg_sql_triggers/schema_dumper_extension.rb +32 -0
  90. data/lib/pg_sql_triggers/sql/kill_switch.rb +154 -275
  91. data/lib/pg_sql_triggers/sql.rb +0 -6
  92. data/lib/pg_sql_triggers/testing/dry_run.rb +0 -0
  93. data/lib/pg_sql_triggers/testing/function_tester.rb +97 -107
  94. data/lib/pg_sql_triggers/testing/safe_executor.rb +0 -0
  95. data/lib/pg_sql_triggers/testing/syntax_validator.rb +0 -0
  96. data/lib/pg_sql_triggers/testing.rb +0 -0
  97. data/lib/pg_sql_triggers/trigger_structure_dumper.rb +111 -0
  98. data/lib/pg_sql_triggers/version.rb +1 -1
  99. data/lib/pg_sql_triggers.rb +21 -1
  100. data/lib/tasks/trigger_migrations.rake +235 -152
  101. data/rakelib/pg_sql_triggers_environment.rake +9 -0
  102. data/scripts/generate_coverage_report.rb +4 -1
  103. data/sig/pg_sql_triggers.rbs +0 -0
  104. metadata +68 -22
  105. data/Goal.md +0 -742
  106. data/app/controllers/pg_sql_triggers/generator_controller.rb +0 -213
  107. data/app/controllers/pg_sql_triggers/sql_capsules_controller.rb +0 -161
  108. data/app/views/pg_sql_triggers/generator/new.html.erb +0 -388
  109. data/app/views/pg_sql_triggers/generator/preview.html.erb +0 -305
  110. data/app/views/pg_sql_triggers/sql_capsules/new.html.erb +0 -81
  111. data/app/views/pg_sql_triggers/sql_capsules/show.html.erb +0 -85
  112. data/lib/generators/trigger/migration_generator.rb +0 -60
  113. data/lib/pg_sql_triggers/generator/form.rb +0 -80
  114. data/lib/pg_sql_triggers/generator/service.rb +0 -339
  115. data/lib/pg_sql_triggers/generator.rb +0 -8
  116. data/lib/pg_sql_triggers/sql/capsule.rb +0 -79
  117. data/lib/pg_sql_triggers/sql/executor.rb +0 -200
data/Goal.md DELETED
@@ -1,742 +0,0 @@
1
- ## 1. Problem Statement
2
-
3
- Rails teams use PostgreSQL triggers for:
4
- - data integrity
5
- - performance
6
- - billing logic
7
-
8
- But triggers today are:
9
- - managed manually
10
- - invisible to Rails
11
- - unsafe to deploy
12
- - easy to drift
13
-
14
- This gem brings triggers into the Rails ecosystem with:
15
- - lifecycle management
16
- - safe deploys
17
- - versioning
18
- - UI control
19
- - emergency SQL escape hatches
20
-
21
- ---
22
-
23
- ## 2. Core Philosophy
24
-
25
- - Rails-native
26
- - Explicit over magic
27
- - Safe by default
28
- - Power with guardrails
29
-
30
- This gem **manages lifecycle**, not business logic.
31
-
32
- ---
33
-
34
- ## 3. Supported Capabilities (MUST IMPLEMENT)
35
-
36
- ### A. Trigger Declaration (DSL) ✅
37
-
38
- Developers declare triggers using Ruby DSL:
39
-
40
- ```ruby
41
- pg_sql_trigger "users_email_validation" do
42
- table :users
43
- on :insert, :update
44
- function :validate_user_email
45
-
46
- version 3
47
- enabled true
48
-
49
- when_env :production
50
- end
51
- ```
52
-
53
- Rules:
54
- - ~~DSL generates metadata, NOT raw SQL~~
55
- - ~~Every trigger has a version~~
56
- - ~~Triggers are environment-aware~~
57
- - ~~Triggers can be enabled or disabled~~
58
-
59
- ---
60
-
61
- ### B. Trigger Generation ✅
62
-
63
- The gem must generate triggers safely.
64
-
65
- Generators create:
66
- 1. ~~Trigger DSL file~~
67
- 2. ~~Function stub (PL/pgSQL)~~
68
- 3. ~~Manifest metadata~~
69
-
70
- Rules:
71
- - ~~Generated triggers are **disabled by default**~~
72
- - ~~Nothing executes automatically~~
73
- - ~~Developers must explicitly apply~~
74
-
75
- ---
76
-
77
- ### C. Trigger Registry (Source of Truth) ✅
78
-
79
- All triggers must be tracked in a registry table.
80
-
81
- Registry tracks:
82
- - ~~trigger_name~~
83
- - ~~table_name~~
84
- - ~~version~~
85
- - ~~enabled~~
86
- - ~~checksum~~ (✅ fully implemented - consistent field-concatenation algorithm)
87
- - ~~source (dsl / generated / manual_sql)~~
88
- - ~~environment~~
89
- - ~~installed_at~~
90
- - ~~last_verified_at~~
91
-
92
- Rails must always know:
93
- - ~~what exists~~
94
- - ~~how it was created~~
95
- - ✅ whether it drifted (drift detection fully implemented)
96
-
97
- ---
98
-
99
- ### D. Safe Apply & Deploy ✅ (fully implemented via migrations)
100
-
101
- Applying triggers must:
102
- - ✅ Run in a transaction (migrations run in transactions)
103
- - ✅ Diff expected vs actual (fully implemented - pre-apply comparison before migration execution)
104
- - ✅ Never blindly DROP + CREATE (fully implemented - safety validator blocks unsafe DROP + CREATE patterns)
105
- - ✅ Support rollback on failure (migration rollback exists)
106
- - ✅ Update registry atomically (registry updated during migration execution)
107
-
108
- **Status:** Core functionality fully implemented through migration system. Pre-apply comparison shows diff between expected (from migration) and actual (from database) state before applying migrations. Safety validator explicitly blocks unsafe DROP + CREATE operations, preventing migrations from blindly dropping and recreating existing database objects without validation.
109
-
110
- ---
111
-
112
- ### E. Drift Detection ✅ (fully implemented)
113
-
114
- System must detect:
115
- - ✅ Missing triggers (implemented via DROPPED state)
116
- - ✅ Version mismatch (implemented via checksum comparison)
117
- - ✅ Function body drift (implemented via checksum comparison)
118
- - ✅ Manual SQL overrides (implemented via MANUAL_OVERRIDE state)
119
- - ✅ Unknown external triggers (implemented via UNKNOWN state)
120
-
121
- Drift states:
122
- 1. ✅ Managed & In Sync (fully implemented)
123
- 2. ✅ Managed & Drifted (fully implemented)
124
- 3. ✅ Manual Override (fully implemented)
125
- 4. ✅ Disabled (fully implemented)
126
- 5. ✅ Dropped (Recorded) (fully implemented)
127
- 6. ✅ Unknown (External) (fully implemented)
128
-
129
- ---
130
-
131
- ### F. Rails Console Introspection ✅
132
-
133
- Provide console APIs:
134
-
135
- ✅ `PgSqlTriggers::Registry.list` - Returns all registered triggers
136
- ✅ `PgSqlTriggers::Registry.enabled` - Returns enabled triggers
137
- ✅ `PgSqlTriggers::Registry.disabled` - Returns disabled triggers
138
- ✅ `PgSqlTriggers::Registry.for_table(:users)` - Returns triggers for a specific table
139
- ✅ `PgSqlTriggers::Registry.diff` - Checks for drift (fully working with drift detection)
140
- ✅ `PgSqlTriggers::Registry.validate!` - Validates all triggers
141
- ✅ `PgSqlTriggers::Registry.drifted` - Returns all drifted triggers
142
- ✅ `PgSqlTriggers::Registry.in_sync` - Returns all in-sync triggers
143
- ✅ `PgSqlTriggers::Registry.unknown_triggers` - Returns all unknown (external) triggers
144
- ✅ `PgSqlTriggers::Registry.dropped` - Returns all dropped triggers
145
- ✅ `PgSqlTriggers::Registry.enable(trigger_name, actor:, confirmation:)` - Enable a trigger
146
- ✅ `PgSqlTriggers::Registry.disable(trigger_name, actor:, confirmation:)` - Disable a trigger
147
- ✅ `PgSqlTriggers::Registry.drop(trigger_name, actor:, reason:, confirmation:)` - Drop a trigger
148
- ✅ `PgSqlTriggers::Registry.re_execute(trigger_name, actor:, reason:, confirmation:)` - Re-execute a trigger
149
-
150
- ✅ No raw SQL required by users for basic operations.
151
- ✅ All console APIs are fully documented with YARD documentation.
152
-
153
- ---
154
-
155
- ## 4. Free-Form SQL Execution (MANDATORY) ✅ (fully implemented in v1.2.0)
156
-
157
- The gem MUST support free-form SQL execution.
158
-
159
- This is required for:
160
- - emergency fixes
161
- - complex migrations
162
- - DB-owner workflows
163
-
164
- ### SQL Capsules
165
-
166
- Free-form SQL is wrapped in **named SQL capsules**:
167
-
168
- - ✅ Must be named (fully implemented - `PgSqlTriggers::SQL::Capsule` class)
169
- - ✅ Must declare environment (fully implemented)
170
- - ✅ Must declare purpose (fully implemented)
171
- - ✅ Must be applied explicitly (fully implemented - web UI and console API)
172
-
173
- Rules:
174
- - ✅ Runs in a transaction (fully implemented - `PgSqlTriggers::SQL::Executor` executes in transaction)
175
- - ✅ Checksum verified (fully implemented - checksum calculated and stored)
176
- - ✅ Registry updated (fully implemented - registry updated with `source = manual_sql`)
177
- - ✅ Marked as `source = manual_sql` (fully implemented)
178
-
179
- **Status:** ✅ Fully implemented in v1.2.0. Includes:
180
- - `PgSqlTriggers::SQL::Capsule` class for defining SQL capsules
181
- - `PgSqlTriggers::SQL::Executor.execute` method for safe execution
182
- - Web UI controller (`SqlCapsulesController`) with create, show, and execute actions
183
- - Permission checks (Admin role required for execution)
184
- - Kill switch protection
185
- - Comprehensive audit logging
186
- - Console API: `PgSqlTriggers::SQL::Executor.execute(capsule, actor:, confirmation:)`
187
-
188
- ---
189
-
190
- ## 5. Permissions Model v1 ✅ (fully implemented and enforced in v1.3.0)
191
-
192
- Three permission levels:
193
-
194
- ### Viewer
195
- - ✅ Read-only (fully enforced)
196
- - ✅ View triggers (fully enforced)
197
- - ✅ View diffs (fully enforced)
198
-
199
- ### Operator
200
- - ✅ Enable / Disable triggers (fully enforced)
201
- - ✅ Apply generated triggers (fully enforced)
202
- - ✅ Re-execute triggers in non-prod (fully enforced)
203
- - ✅ Dry-run SQL (fully enforced)
204
-
205
- ### Admin
206
- - ✅ Drop triggers (fully enforced - Admin only)
207
- - ✅ Execute free-form SQL (fully enforced - Admin only)
208
- - ✅ Re-execute triggers in any env (fully enforced - Admin only)
209
- - ✅ Override drift (fully enforced)
210
-
211
- Permissions enforced in:
212
- - ✅ UI (fully enforced - buttons show/hide based on permissions, controllers check permissions)
213
- - ✅ CLI (kill switch provides protection - permissions can be added if needed)
214
- - ✅ Console (fully enforced - all console APIs check permissions via `permission_checker` configuration)
215
-
216
- ---
217
-
218
- ## 6. Kill Switch for Production SQL (MANDATORY) ✅
219
-
220
- Production mutations must be gated.
221
-
222
- ### Levels:
223
- 1. ~~Global disable (default)~~ ✅ (fully implemented)
224
- 2. ~~Runtime ENV override~~ ✅ (implemented via KILL_SWITCH_OVERRIDE and CONFIRMATION_TEXT)
225
- 3. ~~Explicit confirmation text~~ ✅ (implemented with customizable patterns)
226
- 4. ❌ Optional time-window auto-lock (not implemented - optional feature)
227
-
228
- Kill switch must:
229
- - ~~Block UI~~ ✅ (implemented in MigrationsController and GeneratorController)
230
- - ~~Block CLI~~ ✅ (implemented in all rake tasks)
231
- - ~~Block console~~ ✅ (implemented in TriggerRegistry and Migrator)
232
- - ~~Always log attempts~~ ✅ (comprehensive logging with operation, environment, actor, and status)
233
-
234
- ### Implementation Details:
235
-
236
- **Core Module**: `lib/pg_sql_triggers/sql/kill_switch.rb`
237
- - Thread-safe override mechanism using thread-local storage
238
- - Configuration-driven with sensible defaults
239
- - Operation-specific confirmation patterns
240
- - Comprehensive logging and audit trail
241
-
242
- **Protected Operations**:
243
- - CLI: All trigger migration tasks (migrate, rollback, up, down, redo)
244
- - CLI: Combined db:migrate:with_triggers tasks
245
- - Console: TriggerRegistry#enable!, TriggerRegistry#disable!
246
- - Console: Migrator.run_up, Migrator.run_down
247
- - UI: Migration up/down/redo actions
248
- - UI: Trigger generation
249
-
250
- **Configuration**: `config/initializers/pg_sql_triggers.rb`
251
- - kill_switch_enabled: Global enable/disable (default: true)
252
- - kill_switch_environments: Protected environments (default: [:production, :staging])
253
- - kill_switch_confirmation_required: Require confirmation text (default: true)
254
- - kill_switch_confirmation_pattern: Custom confirmation pattern lambda
255
- - kill_switch_logger: Logger for events (default: Rails.logger)
256
-
257
- **Usage Examples**:
258
- ```bash
259
- # CLI with confirmation
260
- KILL_SWITCH_OVERRIDE=true CONFIRMATION_TEXT="EXECUTE TRIGGER_MIGRATE" rake trigger:migrate
261
- ```
262
-
263
- ```ruby
264
- # Console with override block
265
- PgSqlTriggers::SQL::KillSwitch.override(confirmation: "EXECUTE TRIGGER_ENABLE") do
266
- trigger.enable!
267
- end
268
-
269
- # Console with direct confirmation
270
- trigger.enable!(confirmation: "EXECUTE TRIGGER_ENABLE")
271
- ```
272
-
273
- **Tests**: Comprehensive test suite at `spec/pg_sql_triggers/sql/kill_switch_spec.rb` covering:
274
- - Environment detection
275
- - Confirmation validation
276
- - Override mechanisms (thread-local, ENV, explicit)
277
- - Thread safety
278
- - Logging
279
- - Custom configuration
280
-
281
- ---
282
-
283
- ## 8. UI (Mountable Rails Engine)
284
-
285
- UI is operational, not decorative.
286
-
287
- ### Dashboard ✅ (fully implemented in v1.3.0)
288
- - ✅ Trigger name
289
- - ✅ Table
290
- - ✅ Version
291
- - ✅ Status (enabled/disabled)
292
- - ✅ Source
293
- - ✅ Drift state (drift detection fully implemented - shows drift count and states)
294
- - ✅ Environment
295
- - ✅ Last applied (installed_at displayed with human-readable formatting and tooltips in v1.3.0)
296
-
297
- ### Trigger Detail Page ✅ (fully implemented in v1.3.0)
298
- - ✅ Summary panel (dedicated trigger detail route and page with comprehensive metadata)
299
- - ✅ SQL diff (expected vs actual comparison with syntax highlighting)
300
- - ✅ Registry state (comprehensive state display including checksum, drift detection, manual override status)
301
- - ✅ Breadcrumb navigation (Dashboard → Tables → Table → Trigger)
302
- - ✅ Enhanced timestamp display (installed_at and last_verified_at with relative time formatting)
303
-
304
- ### Actions (State-Based) ✅ (fully implemented in v1.2.0 and v1.3.0)
305
- - ✅ Enable (UI buttons in dashboard, table view, and trigger detail page with kill switch protection)
306
- - ✅ Disable (UI buttons in dashboard, table view, and trigger detail page with kill switch protection)
307
- - ✅ Drop (fully implemented in v1.2.0 - UI buttons with confirmation modal in v1.3.0)
308
- - ✅ Re-execute (fully implemented in v1.2.0 - UI buttons with drift diff display in v1.3.0)
309
- - ✅ Execute SQL capsule (fully implemented in v1.2.0 - UI buttons in v1.3.0)
310
-
311
- Buttons must:
312
- - ✅ Be permission-aware (fully enforced - buttons show/hide based on user permissions in v1.3.0)
313
- - ✅ Be env-aware (fully implemented - warning colors for production, environment-aware styling)
314
- - ✅ Respect kill switch (kill switch fully implemented - see Section 6)
315
-
316
- ---
317
-
318
- ## 9. Drop & Re-Execute Flow (CRITICAL) ✅ (fully implemented in v1.2.0, UI added in v1.3.0)
319
-
320
- Re-execute must:
321
- 1. ✅ Show diff (fully implemented - drift diff displayed before re-execution)
322
- 2. ✅ Require reason (fully implemented - reason field required and logged)
323
- 3. ✅ Require typed confirmation (fully implemented - confirmation text required in protected environments)
324
- 4. ✅ Execute transactionally (fully implemented - all operations run in transactions)
325
- 5. ✅ Update registry (fully implemented - registry updated atomically with operations)
326
-
327
- Drop must:
328
- 1. ✅ Require reason (fully implemented - reason field required and logged)
329
- 2. ✅ Require typed confirmation (fully implemented - confirmation text required in protected environments)
330
- 3. ✅ Execute transactionally (fully implemented - drop runs in transaction)
331
- 4. ✅ Update registry (fully implemented - trigger removed from registry after drop)
332
- 5. ✅ Require Admin permission (fully enforced)
333
-
334
- No silent operations allowed. ✅ All operations are logged to audit trail with actor, reason, and state changes.
335
-
336
- ---
337
-
338
- ## 10. What This Gem Is NOT
339
-
340
- - Not a raw SQL editor
341
- - Not a trigger playground
342
- - Not auto-executing
343
- - Not unsafe
344
- - Not magic
345
-
346
- ---
347
-
348
- ## 11. Non-Negotiable Constraints
349
-
350
- - No silent prod changes
351
- - No hidden state
352
- - No bypassing registry
353
- - No bypassing permissions
354
-
355
- ---
356
-
357
- ## 12. Final Framing (VERY IMPORTANT)
358
-
359
- This gem must be described as:
360
-
361
- > **A PostgreSQL Trigger Control Plane for Rails**
362
-
363
- ---
364
-
365
- ## 13. Implementation Status & Improvements Needed
366
-
367
- ### 📊 Quick Status Summary
368
-
369
- **Fully Implemented:**
370
- - ✅ Trigger Declaration DSL (Section 3.A)
371
- - ✅ Trigger Generation (Section 3.B)
372
- - ✅ Trigger Registry (Section 3.C) - with consistent field-concatenation checksum algorithm
373
- - ✅ Safe Apply & Deploy (Section 3.D) - fully implemented with safety validation
374
- - ✅ Drift Detection (Section 3.E) - fully implemented with all 6 drift states
375
- - ✅ Rails Console Introspection (Section 3.F) - including working diff method
376
- - ✅ Kill Switch for Production Safety (Section 6) - fully implemented
377
- - ✅ SQL Capsules (Section 4) - fully implemented in v1.2.0
378
- - ✅ Permissions Model (Section 5) - fully enforced in v1.3.0
379
- - ✅ Drop & Re-Execute Flow (Section 9) - fully implemented in v1.2.0, UI in v1.3.0
380
- - ✅ Complete UI (Section 8) - dashboard, trigger detail page, all action buttons implemented in v1.3.0
381
- - ✅ Audit Logging System (Section 13) - fully implemented in v1.3.0 with UI
382
-
383
- **Partially Implemented:**
384
- - None - all critical features are fully implemented
385
-
386
- **Not Implemented (Optional/Low Priority):**
387
- - ❌ Time-window auto-lock for kill switch (Section 6 - optional feature)
388
-
389
- ### ✅ Achieved Features
390
-
391
- **Core Infrastructure:**
392
- - ✅ Trigger Declaration DSL (`PgSqlTriggers::DSL.pg_sql_trigger`) - Section 3.A
393
- - ✅ Trigger Registry model and table with all required fields - Section 3.C
394
- - ✅ Trigger Generation (form-based wizard, DSL + migration files) - Section 3.B
395
- - ✅ Database Introspection (tables, triggers, columns) - Supporting infrastructure
396
- - ✅ Trigger Migrations system (rake tasks + UI) - Supporting infrastructure
397
- - ✅ Drift Detection (all 6 states, detector, reporter, console APIs) - Section 3.E
398
- - ✅ Rails Console Introspection APIs (`PgSqlTriggers::Registry.*`) - Section 3.F
399
- - ✅ Enable/Disable trigger methods on TriggerRegistry model - Basic functionality
400
- - ✅ Kill Switch for Production Safety (fully implemented) - Section 6
401
- - ✅ Mountable Rails Engine with routes - Supporting infrastructure
402
- - ✅ Complete UI (Dashboard, Tables view, Generator, Trigger Detail Page, all action buttons) - Section 8 (fully implemented in v1.3.0)
403
-
404
- **From Section 3.A (Trigger Declaration DSL):**
405
- - ✅ DSL generates metadata
406
- - ✅ Every trigger has a version
407
- - ✅ Triggers are environment-aware
408
- - ✅ Triggers can be enabled or disabled
409
-
410
- **From Section 3.B (Trigger Generation):**
411
- - ✅ Generator creates trigger DSL file
412
- - ✅ Generator creates function stub (PL/pgSQL)
413
- - ✅ Generator creates manifest metadata
414
- - ✅ Generated triggers are disabled by default
415
-
416
- **From Section 3.C (Trigger Registry):**
417
- - ✅ Registry tracks: trigger_name, table_name, version, enabled, source, environment, installed_at, last_verified_at
418
- - ✅ Registry tracks checksum (✅ consistent field-concatenation algorithm across all creation paths)
419
- - ✅ Rails knows what exists and how it was created
420
-
421
- **From Section 3.E (Drift Detection):**
422
- - ✅ Drift::Detector class with all 6 drift states
423
- - ✅ Drift::Reporter class for formatting drift reports
424
- - ✅ Drift::DbQueries helper for PostgreSQL system catalog queries
425
- - ✅ Detection of missing triggers (DROPPED state)
426
- - ✅ Detection of version/function body drift (DRIFTED state via checksum)
427
- - ✅ Detection of manual SQL overrides (MANUAL_OVERRIDE state)
428
- - ✅ Detection of unknown external triggers (UNKNOWN state)
429
- - ✅ Detection of disabled triggers (DISABLED state)
430
- - ✅ Detection of in-sync triggers (IN_SYNC state)
431
- - ✅ Registry convenience methods (drifted, in_sync, unknown_triggers, dropped)
432
- - ✅ TriggerRegistry instance methods (drift_state, drift_result, drifted?, in_sync?, dropped?)
433
- - ✅ Comprehensive test coverage for Detector and Reporter
434
-
435
- **From Section 3.F (Rails Console Introspection):**
436
- - ✅ `PgSqlTriggers::Registry.list` (note: namespace differs slightly from goal)
437
- - ✅ `PgSqlTriggers::Registry.enabled`
438
- - ✅ `PgSqlTriggers::Registry.disabled`
439
- - ✅ `PgSqlTriggers::Registry.for_table(:users)`
440
- - ✅ `PgSqlTriggers::Registry.validate!`
441
- - ✅ `PgSqlTriggers::Registry.diff` (fully working with drift detection)
442
- - ✅ `PgSqlTriggers::Registry.drifted` (returns all drifted triggers)
443
- - ✅ `PgSqlTriggers::Registry.in_sync` (returns all in-sync triggers)
444
- - ✅ `PgSqlTriggers::Registry.unknown_triggers` (returns all external triggers)
445
- - ✅ `PgSqlTriggers::Registry.dropped` (returns all dropped triggers)
446
- - ✅ No raw SQL required by users for basic operations (enable/disable via console methods)
447
-
448
- **From Section 5 (Permissions Model):**
449
- - ✅ Permission structure exists (Viewer, Operator, Admin roles defined)
450
- - ✅ Permission model classes exist
451
-
452
- **From Section 6 (Kill Switch):**
453
- - ✅ Fully implemented - see Section 6 for details
454
- - ✅ Global disable configuration (default: true)
455
- - ✅ Runtime ENV override support (KILL_SWITCH_OVERRIDE)
456
- - ✅ Explicit confirmation text requirement
457
- - ✅ Comprehensive logging and audit trail
458
- - ✅ UI, CLI, and Console enforcement
459
- - ✅ Thread-safe override mechanism
460
-
461
- **From Section 4 (SQL Capsules):**
462
- - ✅ `PgSqlTriggers::SQL::Capsule` class for defining SQL capsules
463
- - ✅ `PgSqlTriggers::SQL::Executor.execute` method for safe execution
464
- - ✅ Web UI controller (`SqlCapsulesController`) with create, show, and execute actions
465
- - ✅ Permission checks (Admin role required)
466
- - ✅ Kill switch protection
467
- - ✅ Transactional execution
468
- - ✅ Checksum calculation and storage
469
- - ✅ Registry update with `source = manual_sql`
470
- - ✅ Comprehensive audit logging
471
- - ✅ Console API: `PgSqlTriggers::SQL::Executor.execute(capsule, actor:, confirmation:)`
472
-
473
- **From Section 5 (Permissions Model):**
474
- - ✅ Permission structure (Viewer, Operator, Admin roles)
475
- - ✅ Permission enforcement in UI (controllers and views)
476
- - ✅ Permission enforcement in Console APIs (all Registry methods)
477
- - ✅ Permission enforcement in SQL Executor
478
- - ✅ `PermissionsHelper` module for view-level permission checks
479
- - ✅ Permission helper methods in `ApplicationController`
480
- - ✅ Configurable `permission_checker` via configuration
481
- - ✅ `PermissionError` exception class
482
- - ✅ Comprehensive test coverage
483
-
484
- **From Section 8 (UI):**
485
- - ✅ Dashboard with: Trigger name, Table, Version, Status (enabled/disabled), Source, Environment, Drift state, Last Applied
486
- - ✅ Dashboard displays drift count (fully working with drift detection)
487
- - ✅ Tables view with table listing and trigger details
488
- - ✅ Trigger detail page (dedicated route/page with comprehensive metadata, SQL diff, registry state)
489
- - ✅ Generator UI (form-based wizard for creating triggers)
490
- - ✅ Migration management UI (up/down/redo with kill switch protection)
491
- - ✅ All action buttons (enable/disable/drop/re-execute/execute SQL capsule)
492
- - ✅ Permission-aware button visibility
493
- - ✅ Environment-aware button styling
494
- - ✅ Breadcrumb navigation
495
- - ✅ Enhanced timestamp display
496
-
497
- **From Section 9 (Drop & Re-Execute Flow):**
498
- - ✅ `TriggerRegistry#drop!` method with permission checks, kill switch, reason, confirmation
499
- - ✅ `TriggerRegistry#re_execute!` method with drift diff, reason, confirmation
500
- - ✅ UI buttons for drop and re-execute in dashboard, table view, and trigger detail page
501
- - ✅ Confirmation modals with reason input and typed confirmation
502
- - ✅ Drift comparison shown before re-execution
503
- - ✅ Transactional execution
504
- - ✅ Registry updates
505
- - ✅ Comprehensive audit logging
506
-
507
- ---
508
-
509
- ### ✅ HIGH PRIORITY - All Critical Features Completed
510
-
511
- **Status:** All HIGH priority features have been fully implemented in v1.2.0 and v1.3.0.
512
-
513
- #### 1. SQL Capsules (MANDATORY - Section 4) - ✅ COMPLETED in v1.2.0
514
- **Priority:** HIGH - Mandatory feature for emergency operations
515
-
516
- **Status:** ✅ Fully implemented in v1.2.0
517
-
518
- **Implementation:**
519
- - ✅ `lib/pg_sql_triggers/sql/capsule.rb` - SQL capsule definition class
520
- - ✅ `lib/pg_sql_triggers/sql/executor.rb` - SQL execution with transaction, checksum, registry update
521
- - ✅ `app/controllers/pg_sql_triggers/sql_capsules_controller.rb` - UI controller
522
- - ✅ SQL capsule views (new, show, create, execute)
523
- - ✅ SQL capsule storage mechanism (registry table with `source = manual_sql`)
524
-
525
- **Functionality:**
526
- - ✅ Named SQL capsules with environment and purpose declaration
527
- - ✅ Explicit application workflow with confirmation
528
- - ✅ Transactional execution
529
- - ✅ Checksum verification
530
- - ✅ Registry update with `source = manual_sql`
531
- - ✅ Kill switch protection (blocks in production)
532
- - ✅ Permission checks (Admin only)
533
- - ✅ Comprehensive audit logging
534
-
535
- **Impact:** ✅ Emergency SQL execution fully operational with safety controls.
536
-
537
- #### 2. Drop & Re-Execute Flow (Section 9) - ✅ COMPLETED in v1.2.0, UI in v1.3.0
538
- **Priority:** HIGH - Operational requirements
539
-
540
- **Status:** ✅ Fully implemented in v1.2.0, UI added in v1.3.0
541
-
542
- **Implementation:**
543
- - ✅ Drop trigger functionality with permission checks, kill switch, reason, typed confirmation
544
- - ✅ Re-execute functionality with diff display, reason, typed confirmation
545
- - ✅ UI for drop/re-execute actions (buttons in dashboard, table view, trigger detail page)
546
- - ✅ Confirmation modals with reason input and typed confirmation text
547
- - ✅ Transactional execution and registry update
548
- - ✅ Comprehensive audit logging with state changes
549
-
550
- **Impact:** ✅ Safe drop and re-execute workflows fully operational with UI access.
551
-
552
- #### 3. Safe Apply & Deploy (Section 3.D) - ✅ FULLY IMPLEMENTED
553
- **Priority:** MEDIUM-HIGH - Deployment safety enhancement
554
-
555
- **Status:** Fully implemented - pre-apply comparison and safety validation added
556
-
557
- **What Works:**
558
- - ✅ Migrations run in transactions
559
- - ✅ Migration rollback supported
560
- - ✅ Registry updated during migrations
561
- - ✅ Pre-apply comparison (diff expected vs actual) before migration execution
562
- - ✅ Diff reporting shows what will change before applying
563
- - ✅ Safety validator blocks unsafe DROP + CREATE operations
564
- - ✅ Explicit validation prevents migrations from blindly dropping and recreating existing objects
565
-
566
- **Implementation Details:**
567
- - `Migrator::SafetyValidator` class detects unsafe DROP + CREATE patterns in migrations
568
- - Validator checks if migrations would drop existing database objects and recreate them
569
- - Blocks migration execution if unsafe patterns detected (unless explicitly allowed)
570
- - Configuration option `allow_unsafe_migrations` (default: false) for global override
571
- - Environment variable `ALLOW_UNSAFE_MIGRATIONS=true` for per-migration override
572
- - Provides clear error messages explaining unsafe operations and how to proceed
573
-
574
- ---
575
-
576
- ### ✅ MEDIUM PRIORITY - All User-Facing Features Completed
577
-
578
- **Status:** All MEDIUM priority features have been fully implemented in v1.3.0.
579
-
580
- #### 4. Trigger Detail Page (Section 8 - UI) - ✅ COMPLETED in v1.3.0
581
- **Priority:** MEDIUM - Usability
582
-
583
- **Status:** ✅ Fully implemented in v1.3.0
584
-
585
- **Implementation:**
586
- - ✅ Dedicated trigger detail route and controller action (`triggers#show`)
587
- - ✅ Summary panel with all trigger metadata (name, table, version, status, source, environment, timestamps)
588
- - ✅ SQL diff view (expected vs actual with syntax highlighting)
589
- - ✅ Registry state display (comprehensive state including checksum, drift detection, manual override)
590
- - ✅ Action buttons (Enable/Disable/Drop/Re-execute/Execute SQL capsule)
591
- - ✅ Permission-aware, environment-aware, kill switch-aware button visibility
592
- - ✅ Breadcrumb navigation
593
-
594
- #### 5. UI Actions (Section 8) - ✅ COMPLETED in v1.3.0
595
- **Priority:** MEDIUM - Usability
596
-
597
- **Status:** ✅ Fully implemented in v1.3.0
598
-
599
- **Implementation:**
600
- - ✅ Enable/Disable buttons in dashboard, tables/show, and trigger detail pages
601
- - ✅ Drop button with confirmation modal (Admin permission required)
602
- - ✅ Re-execute button with drift diff display (Admin permission required)
603
- - ✅ Execute SQL capsule button (Admin permission required)
604
-
605
- **What Works:**
606
- - ✅ Kill switch enforcement in UI (fully implemented - see Section 6)
607
- - ✅ Migration actions (up/down/redo) with kill switch protection
608
- - ✅ All action buttons with permission checks
609
- - ✅ AJAX-based actions to avoid full page reloads
610
-
611
- #### 6. Permissions Enforcement (Section 5) - ✅ COMPLETED in v1.3.0
612
- **Priority:** MEDIUM - Security
613
-
614
- **Status:** ✅ Fully enforced in v1.3.0
615
-
616
- **Implementation:**
617
- - ✅ Permission checking in controllers (all UI actions check permissions)
618
- - ✅ Permission checking in UI (buttons show/hide based on role via `PermissionsHelper`)
619
- - ✅ Permission checks in `TriggerRegistry#enable!` and `disable!` (Operator or Admin required)
620
- - ✅ Permission checks in console APIs (all Registry methods check permissions)
621
- - ✅ Permission checks in SQL Executor (Admin required)
622
- - ✅ Actor context passing through all operations (actor tracked in audit logs)
623
-
624
- **What Exists:**
625
- - ✅ Permission structure (Viewer, Operator, Admin roles defined)
626
- - ✅ Permission model classes (`PgSqlTriggers::Permissions::Checker`)
627
- - ✅ Configurable `permission_checker` via configuration
628
- - ✅ `PermissionError` exception class
629
-
630
- ---
631
-
632
- ### 🟢 LOW PRIORITY - Polish & Improvements
633
-
634
- #### 7. Enhanced Logging & Audit Trail - ✅ COMPLETED in v1.3.0
635
- **Priority:** LOW - Operational polish
636
-
637
- **Status:** ✅ Fully implemented in v1.3.0
638
-
639
- **Implementation:**
640
- - ✅ Kill switch activation attempts logging (fully implemented)
641
- - ✅ Kill switch overrides logging (fully implemented)
642
- - ✅ Comprehensive audit trail table (`pg_sql_triggers_audit_log`) for all operations
643
- - ✅ Audit logging for enable/disable/drop/re-execute/SQL capsule execution
644
- - ✅ Complete state capture (before/after) for all operations
645
- - ✅ Actor tracking for all operations
646
- - ✅ Error message logging for failed operations
647
- - ✅ Audit log UI with filtering, sorting, pagination, and CSV export
648
- - ✅ Console API: `PgSqlTriggers::AuditLog.for_trigger(name)`
649
-
650
- #### 8. Error Handling Consistency - ✅ COMPLETED in v1.3.0
651
- **Priority:** LOW - Code quality
652
-
653
- **Status:** ✅ Fully implemented in v1.3.0
654
-
655
- **Implementation:**
656
- - ✅ Comprehensive error hierarchy with base `Error` class and specialized error types
657
- - ✅ Error classes: `PermissionError`, `KillSwitchError`, `DriftError`, `ValidationError`, `ExecutionError`, `UnsafeMigrationError`, `NotFoundError`
658
- - ✅ Error codes for programmatic handling (e.g., `PERMISSION_DENIED`, `KILL_SWITCH_ACTIVE`, `DRIFT_DETECTED`)
659
- - ✅ Standardized error messages with recovery suggestions
660
- - ✅ Enhanced error display in UI with user-friendly formatting
661
- - ✅ Context information included in all errors for better debugging
662
- - ✅ Error handling helpers in `ApplicationController` for consistent error formatting
663
- - ✅ Kill switch violations raise `KillSwitchError` (fully implemented)
664
- - ✅ Permission violations raise `PermissionError` (fully implemented)
665
- - ✅ Drift detection implemented (can be used for error handling)
666
- - ✅ Consistent error handling across all operations
667
-
668
- #### 9. Testing Coverage - ✅ COMPREHENSIVE
669
- **Priority:** LOW - Quality assurance
670
-
671
- **Status:** ✅ Comprehensive test coverage achieved (93.45% in v1.3.0)
672
-
673
- **Implementation:**
674
- - ✅ SQL capsules have comprehensive tests
675
- - ✅ Kill switch has comprehensive tests (fully tested)
676
- - ✅ Drift detection has comprehensive tests (fully tested)
677
- - ✅ Permission enforcement has comprehensive tests
678
- - ✅ Drop/re-execute flow has comprehensive tests
679
- - ✅ UI controller tests (triggers, dashboard, SQL capsules, audit logs)
680
- - ✅ Integration tests (full workflows)
681
- - ✅ Error handling tests
682
-
683
- #### 10. Documentation Updates - ✅ COMPLETED in v1.3.0
684
- **Priority:** LOW - User experience
685
-
686
- **Status:** ✅ Comprehensive documentation completed in v1.3.0
687
-
688
- **Implementation:**
689
- - ✅ README updated with all v1.3.0 features
690
- - ✅ README includes SQL capsules documentation with examples
691
- - ✅ README includes kill switch documentation with enforcement details (fully documented)
692
- - ✅ Examples provided for SQL capsules
693
- - ✅ Examples provided for permission configuration
694
- - ✅ Drift detection fully documented
695
- - ✅ New comprehensive guides:
696
- - `docs/ui-guide.md` - Using the web UI
697
- - `docs/permissions.md` - Configuring permissions
698
- - `docs/audit-trail.md` - Viewing audit logs
699
- - `docs/troubleshooting.md` - Common issues and solutions
700
- - ✅ API reference updated with all new methods
701
-
702
- #### 11. Implementation Status Summary
703
- **Priority:** LOW - Status tracking
704
-
705
- **All Features Completed:**
706
- - ✅ **Permissions Model** - Fully enforced in UI/CLI/console (v1.3.0)
707
- - ✅ **Kill Switch** - Fully implemented (see Section 6 for details)
708
- - ✅ **Checksum** - Fully implemented with consistent field-concatenation algorithm across all creation paths
709
- - ✅ **Drift Detection** - Fully implemented with all 6 drift states, comprehensive tests, and console APIs
710
- - ✅ **Dashboard** - `installed_at` displayed with formatting in UI (v1.3.0)
711
- - ✅ **Trigger Detail Page** - Dedicated route/page fully implemented (v1.3.0)
712
- - ✅ **Enable/Disable UI** - UI buttons implemented with permission checks (v1.3.0)
713
- - ✅ **SQL Capsules** - Fully implemented (v1.2.0)
714
- - ✅ **Drop & Re-Execute Flow** - Fully implemented (v1.2.0, UI in v1.3.0)
715
- - ✅ **Audit Logging** - Comprehensive audit trail with UI (v1.3.0)
716
- - ✅ **Error Handling** - Consistent error hierarchy and handling (v1.3.0)
717
-
718
- ---
719
-
720
- ### 📝 Technical Notes
721
-
722
- 1. **Console API Naming:** ✅ Standardized - All console APIs follow consistent naming:
723
- - Query methods: `list`, `enabled`, `disabled`, `for_table`, `diff`, `drifted`, `in_sync`, `unknown_triggers`, `dropped`
724
- - Action methods: `enable`, `disable`, `drop`, `re_execute`
725
- - All methods are fully documented with YARD documentation
726
-
727
- 2. **Code Organization:** ✅ Improved - Common controller concerns extracted:
728
- - `KillSwitchProtection` - Handles kill switch checking and confirmation
729
- - `PermissionChecking` - Handles permission checks and actor management
730
- - `ErrorHandling` - Handles error formatting and flash messages
731
- - All controllers inherit from `ApplicationController` which includes these concerns
732
-
733
- 3. **Service Object Patterns:** ✅ Standardized - All service objects follow consistent patterns:
734
- - `Generator::Service` - Class methods for stateless operations, fully documented
735
- - `SQL::Executor` - Class methods for stateless operations, fully documented
736
- - All public methods have YARD documentation
737
-
738
- 4. **YARD Documentation:** ✅ Added - Comprehensive YARD documentation for:
739
- - `PgSqlTriggers::Registry` module and all public methods
740
- - `PgSqlTriggers::TriggerRegistry` model and all public methods
741
- - `PgSqlTriggers::Generator::Service` and all public methods
742
- - `PgSqlTriggers::SQL::Executor` and all public methods