pg_sql_triggers 1.0.0 → 1.1.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.
- checksums.yaml +4 -4
- data/.erb_lint.yml +47 -0
- data/.rubocop.yml +4 -1
- data/CHANGELOG.md +112 -1
- data/COVERAGE.md +58 -0
- data/Goal.md +450 -123
- data/README.md +53 -215
- data/app/controllers/pg_sql_triggers/application_controller.rb +46 -0
- data/app/controllers/pg_sql_triggers/dashboard_controller.rb +4 -1
- data/app/controllers/pg_sql_triggers/generator_controller.rb +76 -8
- data/app/controllers/pg_sql_triggers/migrations_controller.rb +18 -0
- data/app/models/pg_sql_triggers/trigger_registry.rb +93 -12
- data/app/views/layouts/pg_sql_triggers/application.html.erb +34 -1
- data/app/views/pg_sql_triggers/dashboard/index.html.erb +70 -30
- data/app/views/pg_sql_triggers/generator/new.html.erb +22 -4
- data/app/views/pg_sql_triggers/generator/preview.html.erb +244 -16
- data/app/views/pg_sql_triggers/shared/_confirmation_modal.html.erb +221 -0
- data/app/views/pg_sql_triggers/shared/_kill_switch_status.html.erb +40 -0
- data/app/views/pg_sql_triggers/tables/index.html.erb +0 -2
- data/app/views/pg_sql_triggers/tables/show.html.erb +3 -4
- data/config/initializers/pg_sql_triggers.rb +69 -0
- data/db/migrate/20251222000001_create_pg_sql_triggers_tables.rb +3 -1
- data/db/migrate/20251229071916_add_timing_to_pg_sql_triggers_registry.rb +8 -0
- data/docs/README.md +66 -0
- data/docs/api-reference.md +681 -0
- data/docs/configuration.md +541 -0
- data/docs/getting-started.md +135 -0
- data/docs/kill-switch.md +586 -0
- data/docs/screenshots/.gitkeep +1 -0
- data/docs/screenshots/Generate Trigger.png +0 -0
- data/docs/screenshots/Triggers Page.png +0 -0
- data/docs/screenshots/kill error.png +0 -0
- data/docs/screenshots/kill modal for migration down.png +0 -0
- data/docs/usage-guide.md +493 -0
- data/docs/web-ui.md +353 -0
- data/lib/generators/pg_sql_triggers/templates/create_pg_sql_triggers_tables.rb +3 -1
- data/lib/generators/pg_sql_triggers/templates/initializer.rb +44 -2
- data/lib/pg_sql_triggers/drift/db_queries.rb +116 -0
- data/lib/pg_sql_triggers/drift/detector.rb +187 -0
- data/lib/pg_sql_triggers/drift/reporter.rb +179 -0
- data/lib/pg_sql_triggers/drift.rb +14 -11
- data/lib/pg_sql_triggers/dsl/trigger_definition.rb +15 -1
- data/lib/pg_sql_triggers/generator/form.rb +3 -1
- data/lib/pg_sql_triggers/generator/service.rb +82 -26
- data/lib/pg_sql_triggers/migration.rb +1 -1
- data/lib/pg_sql_triggers/migrator/pre_apply_comparator.rb +344 -0
- data/lib/pg_sql_triggers/migrator/pre_apply_diff_reporter.rb +143 -0
- data/lib/pg_sql_triggers/migrator/safety_validator.rb +258 -0
- data/lib/pg_sql_triggers/migrator.rb +85 -3
- data/lib/pg_sql_triggers/registry/manager.rb +100 -13
- data/lib/pg_sql_triggers/sql/kill_switch.rb +300 -0
- data/lib/pg_sql_triggers/testing/dry_run.rb +5 -7
- data/lib/pg_sql_triggers/testing/function_tester.rb +66 -24
- data/lib/pg_sql_triggers/testing/safe_executor.rb +23 -11
- data/lib/pg_sql_triggers/testing/syntax_validator.rb +24 -1
- data/lib/pg_sql_triggers/version.rb +1 -1
- data/lib/pg_sql_triggers.rb +24 -0
- data/lib/tasks/trigger_migrations.rake +33 -0
- data/scripts/generate_coverage_report.rb +129 -0
- metadata +45 -5
data/Goal.md
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
You are building a **production-grade Ruby on Rails gem** named **pg_sql_triggers**.
|
|
2
|
-
|
|
3
|
-
This gem is **not a toy generator**.
|
|
4
|
-
It is a **PostgreSQL Trigger Control Plane for Rails**, designed for real teams running production systems.
|
|
5
|
-
|
|
6
|
-
You must follow everything below strictly.
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
1
|
## 1. Problem Statement
|
|
11
2
|
|
|
12
3
|
Rails teams use PostgreSQL triggers for:
|
|
@@ -42,7 +33,7 @@ This gem **manages lifecycle**, not business logic.
|
|
|
42
33
|
|
|
43
34
|
## 3. Supported Capabilities (MUST IMPLEMENT)
|
|
44
35
|
|
|
45
|
-
### A. Trigger Declaration (DSL)
|
|
36
|
+
### A. Trigger Declaration (DSL) ✅
|
|
46
37
|
|
|
47
38
|
Developers declare triggers using Ruby DSL:
|
|
48
39
|
|
|
@@ -60,97 +51,99 @@ end
|
|
|
60
51
|
```
|
|
61
52
|
|
|
62
53
|
Rules:
|
|
63
|
-
- DSL generates metadata, NOT raw SQL
|
|
64
|
-
- Every trigger has a version
|
|
65
|
-
- Triggers are environment-aware
|
|
66
|
-
- Triggers can be enabled or disabled
|
|
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~~
|
|
67
58
|
|
|
68
59
|
---
|
|
69
60
|
|
|
70
|
-
### B. Trigger Generation
|
|
61
|
+
### B. Trigger Generation ✅
|
|
71
62
|
|
|
72
63
|
The gem must generate triggers safely.
|
|
73
64
|
|
|
74
65
|
Generators create:
|
|
75
|
-
1. Trigger DSL file
|
|
76
|
-
2. Function stub (PL/pgSQL)
|
|
77
|
-
3. Manifest metadata
|
|
66
|
+
1. ~~Trigger DSL file~~
|
|
67
|
+
2. ~~Function stub (PL/pgSQL)~~
|
|
68
|
+
3. ~~Manifest metadata~~
|
|
78
69
|
|
|
79
70
|
Rules:
|
|
80
|
-
- Generated triggers are **disabled by default
|
|
81
|
-
- Nothing executes automatically
|
|
82
|
-
- Developers must explicitly apply
|
|
71
|
+
- ~~Generated triggers are **disabled by default**~~
|
|
72
|
+
- ~~Nothing executes automatically~~
|
|
73
|
+
- ~~Developers must explicitly apply~~
|
|
83
74
|
|
|
84
75
|
---
|
|
85
76
|
|
|
86
|
-
### C. Trigger Registry (Source of Truth)
|
|
77
|
+
### C. Trigger Registry (Source of Truth) ✅
|
|
87
78
|
|
|
88
79
|
All triggers must be tracked in a registry table.
|
|
89
80
|
|
|
90
81
|
Registry tracks:
|
|
91
|
-
- trigger_name
|
|
92
|
-
- table_name
|
|
93
|
-
- version
|
|
94
|
-
- enabled
|
|
95
|
-
- checksum
|
|
96
|
-
- source (dsl / generated / manual_sql)
|
|
97
|
-
- environment
|
|
98
|
-
- installed_at
|
|
99
|
-
- last_verified_at
|
|
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~~
|
|
100
91
|
|
|
101
92
|
Rails must always know:
|
|
102
|
-
- what exists
|
|
103
|
-
- how it was created
|
|
104
|
-
- whether it drifted
|
|
93
|
+
- ~~what exists~~
|
|
94
|
+
- ~~how it was created~~
|
|
95
|
+
- ✅ whether it drifted (drift detection fully implemented)
|
|
105
96
|
|
|
106
97
|
---
|
|
107
98
|
|
|
108
|
-
### D. Safe Apply & Deploy
|
|
99
|
+
### D. Safe Apply & Deploy ✅ (fully implemented via migrations)
|
|
109
100
|
|
|
110
101
|
Applying triggers must:
|
|
111
|
-
- Run in a transaction
|
|
112
|
-
- Diff expected vs actual
|
|
113
|
-
- Never blindly DROP + CREATE
|
|
114
|
-
- Support rollback on failure
|
|
115
|
-
- Update registry atomically
|
|
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.
|
|
116
109
|
|
|
117
110
|
---
|
|
118
111
|
|
|
119
|
-
### E. Drift Detection
|
|
112
|
+
### E. Drift Detection ✅ (fully implemented)
|
|
120
113
|
|
|
121
114
|
System must detect:
|
|
122
|
-
- Missing triggers
|
|
123
|
-
- Version mismatch
|
|
124
|
-
- Function body drift
|
|
125
|
-
- Manual SQL overrides
|
|
126
|
-
- Unknown external triggers
|
|
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)
|
|
127
120
|
|
|
128
121
|
Drift states:
|
|
129
|
-
1. Managed & In Sync
|
|
130
|
-
2. Managed & Drifted
|
|
131
|
-
3. Manual Override
|
|
132
|
-
4. Disabled
|
|
133
|
-
5. Dropped (Recorded)
|
|
134
|
-
6. Unknown (External)
|
|
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)
|
|
135
128
|
|
|
136
129
|
---
|
|
137
130
|
|
|
138
|
-
### F. Rails Console Introspection
|
|
131
|
+
### F. Rails Console Introspection ✅
|
|
139
132
|
|
|
140
133
|
Provide console APIs:
|
|
141
134
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
135
|
+
~~PgSqlTriggers::Registry.list~~ (note: namespace differs slightly from goal)
|
|
136
|
+
~~PgSqlTriggers::Registry.enabled~~
|
|
137
|
+
~~PgSqlTriggers::Registry.disabled~~
|
|
138
|
+
~~PgSqlTriggers::Registry.for_table(:users)~~
|
|
139
|
+
~~PgSqlTriggers::Registry.diff~~ (✅ fully working with drift detection)
|
|
140
|
+
~~PgSqlTriggers::Registry.validate!~~
|
|
148
141
|
|
|
149
|
-
No raw SQL required by users
|
|
142
|
+
~~No raw SQL required by users.~~
|
|
150
143
|
|
|
151
144
|
---
|
|
152
145
|
|
|
153
|
-
## 4. Free-Form SQL Execution (MANDATORY)
|
|
146
|
+
## 4. Free-Form SQL Execution (MANDATORY) ❌ (routes defined but no implementation)
|
|
154
147
|
|
|
155
148
|
The gem MUST support free-form SQL execution.
|
|
156
149
|
|
|
@@ -163,62 +156,111 @@ This is required for:
|
|
|
163
156
|
|
|
164
157
|
Free-form SQL is wrapped in **named SQL capsules**:
|
|
165
158
|
|
|
166
|
-
- Must be named
|
|
167
|
-
- Must declare environment
|
|
168
|
-
- Must declare purpose
|
|
169
|
-
- Must be applied explicitly
|
|
159
|
+
- ❌ Must be named (routes defined in `config/routes.rb`, no controller exists)
|
|
160
|
+
- ❌ Must declare environment (not implemented)
|
|
161
|
+
- ❌ Must declare purpose (not implemented)
|
|
162
|
+
- ❌ Must be applied explicitly (not implemented)
|
|
170
163
|
|
|
171
164
|
Rules:
|
|
172
|
-
- Runs in a transaction
|
|
173
|
-
- Checksum verified
|
|
174
|
-
- Registry updated
|
|
175
|
-
- Marked as `source = manual_sql`
|
|
165
|
+
- ❌ Runs in a transaction (not implemented)
|
|
166
|
+
- ❌ Checksum verified (not implemented)
|
|
167
|
+
- ❌ Registry updated (not implemented)
|
|
168
|
+
- ❌ Marked as `source = manual_sql` (not implemented)
|
|
169
|
+
|
|
170
|
+
**Status:** Routes exist for `sql_capsules#new`, `sql_capsules#create`, `sql_capsules#show`, and `sql_capsules#execute`, but no controller, views, or logic implemented. Autoload reference exists in `lib/pg_sql_triggers/sql.rb` but file does not exist.
|
|
176
171
|
|
|
177
172
|
---
|
|
178
173
|
|
|
179
|
-
## 5. Permissions Model v1
|
|
174
|
+
## 5. Permissions Model v1 ⚠️ (structure exists, not enforced)
|
|
180
175
|
|
|
181
176
|
Three permission levels:
|
|
182
177
|
|
|
183
178
|
### Viewer
|
|
184
|
-
- Read-only
|
|
185
|
-
- View triggers
|
|
186
|
-
- View diffs
|
|
179
|
+
- ~~Read-only~~ (structure exists)
|
|
180
|
+
- ~~View triggers~~
|
|
181
|
+
- ~~View diffs~~
|
|
187
182
|
|
|
188
183
|
### Operator
|
|
189
|
-
- Enable / Disable triggers
|
|
190
|
-
- Apply generated triggers
|
|
191
|
-
- Re-execute triggers in non-prod
|
|
192
|
-
- Dry-run SQL
|
|
184
|
+
- ~~Enable / Disable triggers~~ (structure exists)
|
|
185
|
+
- ~~Apply generated triggers~~
|
|
186
|
+
- ~~Re-execute triggers in non-prod~~
|
|
187
|
+
- ~~Dry-run SQL~~
|
|
193
188
|
|
|
194
189
|
### Admin
|
|
195
|
-
- Drop triggers
|
|
196
|
-
- Execute free-form SQL
|
|
197
|
-
- Re-execute triggers in any env
|
|
198
|
-
- Override drift
|
|
190
|
+
- ~~Drop triggers~~ (structure exists)
|
|
191
|
+
- ~~Execute free-form SQL~~
|
|
192
|
+
- ~~Re-execute triggers in any env~~
|
|
193
|
+
- ~~Override drift~~
|
|
199
194
|
|
|
200
195
|
Permissions enforced in:
|
|
201
|
-
- UI
|
|
202
|
-
- CLI
|
|
203
|
-
- Console
|
|
196
|
+
- ❌ UI (not enforced)
|
|
197
|
+
- ❌ CLI (not enforced)
|
|
198
|
+
- ❌ Console (not enforced)
|
|
204
199
|
|
|
205
200
|
---
|
|
206
201
|
|
|
207
|
-
## 6. Kill Switch for Production SQL (MANDATORY)
|
|
202
|
+
## 6. Kill Switch for Production SQL (MANDATORY) ✅
|
|
208
203
|
|
|
209
204
|
Production mutations must be gated.
|
|
210
205
|
|
|
211
206
|
### Levels:
|
|
212
|
-
1. Global disable (default)
|
|
213
|
-
2. Runtime ENV override
|
|
214
|
-
3. Explicit confirmation text
|
|
215
|
-
4. Optional time-window auto-lock
|
|
207
|
+
1. ~~Global disable (default)~~ ✅ (fully implemented)
|
|
208
|
+
2. ~~Runtime ENV override~~ ✅ (implemented via KILL_SWITCH_OVERRIDE and CONFIRMATION_TEXT)
|
|
209
|
+
3. ~~Explicit confirmation text~~ ✅ (implemented with customizable patterns)
|
|
210
|
+
4. ❌ Optional time-window auto-lock (not implemented - optional feature)
|
|
216
211
|
|
|
217
212
|
Kill switch must:
|
|
218
|
-
- Block UI
|
|
219
|
-
- Block CLI
|
|
220
|
-
- Block console
|
|
221
|
-
- Always log attempts
|
|
213
|
+
- ~~Block UI~~ ✅ (implemented in MigrationsController and GeneratorController)
|
|
214
|
+
- ~~Block CLI~~ ✅ (implemented in all rake tasks)
|
|
215
|
+
- ~~Block console~~ ✅ (implemented in TriggerRegistry and Migrator)
|
|
216
|
+
- ~~Always log attempts~~ ✅ (comprehensive logging with operation, environment, actor, and status)
|
|
217
|
+
|
|
218
|
+
### Implementation Details:
|
|
219
|
+
|
|
220
|
+
**Core Module**: `lib/pg_sql_triggers/sql/kill_switch.rb`
|
|
221
|
+
- Thread-safe override mechanism using thread-local storage
|
|
222
|
+
- Configuration-driven with sensible defaults
|
|
223
|
+
- Operation-specific confirmation patterns
|
|
224
|
+
- Comprehensive logging and audit trail
|
|
225
|
+
|
|
226
|
+
**Protected Operations**:
|
|
227
|
+
- CLI: All trigger migration tasks (migrate, rollback, up, down, redo)
|
|
228
|
+
- CLI: Combined db:migrate:with_triggers tasks
|
|
229
|
+
- Console: TriggerRegistry#enable!, TriggerRegistry#disable!
|
|
230
|
+
- Console: Migrator.run_up, Migrator.run_down
|
|
231
|
+
- UI: Migration up/down/redo actions
|
|
232
|
+
- UI: Trigger generation
|
|
233
|
+
|
|
234
|
+
**Configuration**: `config/initializers/pg_sql_triggers.rb`
|
|
235
|
+
- kill_switch_enabled: Global enable/disable (default: true)
|
|
236
|
+
- kill_switch_environments: Protected environments (default: [:production, :staging])
|
|
237
|
+
- kill_switch_confirmation_required: Require confirmation text (default: true)
|
|
238
|
+
- kill_switch_confirmation_pattern: Custom confirmation pattern lambda
|
|
239
|
+
- kill_switch_logger: Logger for events (default: Rails.logger)
|
|
240
|
+
|
|
241
|
+
**Usage Examples**:
|
|
242
|
+
```bash
|
|
243
|
+
# CLI with confirmation
|
|
244
|
+
KILL_SWITCH_OVERRIDE=true CONFIRMATION_TEXT="EXECUTE TRIGGER_MIGRATE" rake trigger:migrate
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
```ruby
|
|
248
|
+
# Console with override block
|
|
249
|
+
PgSqlTriggers::SQL::KillSwitch.override(confirmation: "EXECUTE TRIGGER_ENABLE") do
|
|
250
|
+
trigger.enable!
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# Console with direct confirmation
|
|
254
|
+
trigger.enable!(confirmation: "EXECUTE TRIGGER_ENABLE")
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Tests**: Comprehensive test suite at `spec/pg_sql_triggers/sql/kill_switch_spec.rb` covering:
|
|
258
|
+
- Environment detection
|
|
259
|
+
- Confirmation validation
|
|
260
|
+
- Override mechanisms (thread-local, ENV, explicit)
|
|
261
|
+
- Thread safety
|
|
262
|
+
- Logging
|
|
263
|
+
- Custom configuration
|
|
222
264
|
|
|
223
265
|
---
|
|
224
266
|
|
|
@@ -226,43 +268,43 @@ Kill switch must:
|
|
|
226
268
|
|
|
227
269
|
UI is operational, not decorative.
|
|
228
270
|
|
|
229
|
-
### Dashboard
|
|
230
|
-
- Trigger name
|
|
231
|
-
- Table
|
|
232
|
-
- Version
|
|
233
|
-
- Status
|
|
234
|
-
- Source
|
|
235
|
-
- Drift state
|
|
236
|
-
- Environment
|
|
237
|
-
- Last applied
|
|
238
|
-
|
|
239
|
-
### Trigger Detail Page
|
|
240
|
-
- Summary panel
|
|
241
|
-
- SQL diff
|
|
242
|
-
- Registry state
|
|
243
|
-
|
|
244
|
-
### Actions (State-Based)
|
|
245
|
-
- Enable
|
|
246
|
-
- Disable
|
|
247
|
-
- Drop
|
|
248
|
-
- Re-execute
|
|
249
|
-
- Execute SQL capsule
|
|
271
|
+
### Dashboard ✅ (implemented, drift display pending)
|
|
272
|
+
- ✅ Trigger name
|
|
273
|
+
- ✅ Table
|
|
274
|
+
- ✅ Version
|
|
275
|
+
- ✅ Status (enabled/disabled)
|
|
276
|
+
- ✅ Source
|
|
277
|
+
- ⚠️ Drift state (UI shows drift count but drift detection logic not implemented)
|
|
278
|
+
- ✅ Environment
|
|
279
|
+
- ❌ Last applied (installed_at exists in registry but not displayed in dashboard)
|
|
280
|
+
|
|
281
|
+
### Trigger Detail Page ⚠️ (partial - shown in tables/show but not dedicated)
|
|
282
|
+
- ⚠️ Summary panel (trigger info shown in tables/show view but no dedicated detail route/page)
|
|
283
|
+
- ❌ SQL diff (expected vs actual comparison)
|
|
284
|
+
- ⚠️ Registry state (basic info shown, but not comprehensive state display)
|
|
285
|
+
|
|
286
|
+
### Actions (State-Based) ⚠️ (backend methods exist, UI actions missing)
|
|
287
|
+
- ⚠️ Enable (console method `TriggerRegistry#enable!` exists with kill switch protection, but no UI buttons)
|
|
288
|
+
- ⚠️ Disable (console method `TriggerRegistry#disable!` exists with kill switch protection, but no UI buttons)
|
|
289
|
+
- ❌ Drop (not implemented - no method or UI)
|
|
290
|
+
- ❌ Re-execute (not implemented - no method or UI)
|
|
291
|
+
- ❌ Execute SQL capsule (not implemented - SQL capsules not implemented)
|
|
250
292
|
|
|
251
293
|
Buttons must:
|
|
252
|
-
- Be permission-aware
|
|
253
|
-
- Be env-aware
|
|
254
|
-
- Respect kill switch
|
|
294
|
+
- ❌ Be permission-aware (permissions defined but not enforced in UI)
|
|
295
|
+
- ❌ Be env-aware (not implemented)
|
|
296
|
+
- ✅ Respect kill switch (kill switch fully implemented - see Section 6)
|
|
255
297
|
|
|
256
298
|
---
|
|
257
299
|
|
|
258
|
-
## 9. Drop & Re-Execute Flow (CRITICAL)
|
|
300
|
+
## 9. Drop & Re-Execute Flow (CRITICAL) ❌ (not implemented)
|
|
259
301
|
|
|
260
302
|
Re-execute must:
|
|
261
|
-
1. Show diff
|
|
262
|
-
2. Require reason
|
|
263
|
-
3. Require typed confirmation
|
|
264
|
-
4. Execute transactionally
|
|
265
|
-
5. Update registry
|
|
303
|
+
1. ❌ Show diff (not implemented)
|
|
304
|
+
2. ❌ Require reason (not implemented)
|
|
305
|
+
3. ❌ Require typed confirmation (not implemented)
|
|
306
|
+
4. ❌ Execute transactionally (not implemented)
|
|
307
|
+
5. ❌ Update registry (not implemented)
|
|
266
308
|
|
|
267
309
|
No silent operations allowed.
|
|
268
310
|
|
|
@@ -292,3 +334,288 @@ No silent operations allowed.
|
|
|
292
334
|
This gem must be described as:
|
|
293
335
|
|
|
294
336
|
> **A PostgreSQL Trigger Control Plane for Rails**
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## 13. Implementation Status & Improvements Needed
|
|
341
|
+
|
|
342
|
+
### 📊 Quick Status Summary
|
|
343
|
+
|
|
344
|
+
**Fully Implemented:**
|
|
345
|
+
- ✅ Trigger Declaration DSL (Section 3.A)
|
|
346
|
+
- ✅ Trigger Generation (Section 3.B)
|
|
347
|
+
- ✅ Trigger Registry (Section 3.C) - with consistent field-concatenation checksum algorithm
|
|
348
|
+
- ✅ Safe Apply & Deploy (Section 3.D) - fully implemented with safety validation
|
|
349
|
+
- ✅ Drift Detection (Section 3.E) - fully implemented with all 6 drift states
|
|
350
|
+
- ✅ Rails Console Introspection (Section 3.F) - including working diff method
|
|
351
|
+
- ✅ Kill Switch for Production Safety (Section 6) - fully implemented
|
|
352
|
+
- ✅ Basic UI Dashboard (Section 8) - migration management, tables view, generator
|
|
353
|
+
|
|
354
|
+
**Partially Implemented:**
|
|
355
|
+
- ⚠️ UI (Section 8) - dashboard and tables view exist, but no dedicated trigger detail page, no enable/disable buttons
|
|
356
|
+
- ⚠️ Permissions Model (Section 5) - structure exists but not enforced
|
|
357
|
+
|
|
358
|
+
**Not Implemented (Critical):**
|
|
359
|
+
- ❌ SQL Capsules (Section 4) - MANDATORY feature, routes exist but no implementation
|
|
360
|
+
- ❌ Drop & Re-Execute Flow (Section 9) - CRITICAL operational requirement
|
|
361
|
+
|
|
362
|
+
### ✅ Achieved Features
|
|
363
|
+
|
|
364
|
+
**Core Infrastructure:**
|
|
365
|
+
- ✅ Trigger Declaration DSL (`PgSqlTriggers::DSL.pg_sql_trigger`) - Section 3.A
|
|
366
|
+
- ✅ Trigger Registry model and table with all required fields - Section 3.C
|
|
367
|
+
- ✅ Trigger Generation (form-based wizard, DSL + migration files) - Section 3.B
|
|
368
|
+
- ✅ Database Introspection (tables, triggers, columns) - Supporting infrastructure
|
|
369
|
+
- ✅ Trigger Migrations system (rake tasks + UI) - Supporting infrastructure
|
|
370
|
+
- ✅ Drift Detection (all 6 states, detector, reporter, console APIs) - Section 3.E
|
|
371
|
+
- ✅ Rails Console Introspection APIs (`PgSqlTriggers::Registry.*`) - Section 3.F
|
|
372
|
+
- ✅ Enable/Disable trigger methods on TriggerRegistry model - Basic functionality
|
|
373
|
+
- ✅ Kill Switch for Production Safety (fully implemented) - Section 6
|
|
374
|
+
- ✅ Mountable Rails Engine with routes - Supporting infrastructure
|
|
375
|
+
- ✅ Basic UI (Dashboard, Tables view, Generator) - Section 8 (Dashboard partial)
|
|
376
|
+
|
|
377
|
+
**From Section 3.A (Trigger Declaration DSL):**
|
|
378
|
+
- ✅ DSL generates metadata
|
|
379
|
+
- ✅ Every trigger has a version
|
|
380
|
+
- ✅ Triggers are environment-aware
|
|
381
|
+
- ✅ Triggers can be enabled or disabled
|
|
382
|
+
|
|
383
|
+
**From Section 3.B (Trigger Generation):**
|
|
384
|
+
- ✅ Generator creates trigger DSL file
|
|
385
|
+
- ✅ Generator creates function stub (PL/pgSQL)
|
|
386
|
+
- ✅ Generator creates manifest metadata
|
|
387
|
+
- ✅ Generated triggers are disabled by default
|
|
388
|
+
|
|
389
|
+
**From Section 3.C (Trigger Registry):**
|
|
390
|
+
- ✅ Registry tracks: trigger_name, table_name, version, enabled, source, environment, installed_at, last_verified_at
|
|
391
|
+
- ✅ Registry tracks checksum (✅ consistent field-concatenation algorithm across all creation paths)
|
|
392
|
+
- ✅ Rails knows what exists and how it was created
|
|
393
|
+
|
|
394
|
+
**From Section 3.E (Drift Detection):**
|
|
395
|
+
- ✅ Drift::Detector class with all 6 drift states
|
|
396
|
+
- ✅ Drift::Reporter class for formatting drift reports
|
|
397
|
+
- ✅ Drift::DbQueries helper for PostgreSQL system catalog queries
|
|
398
|
+
- ✅ Detection of missing triggers (DROPPED state)
|
|
399
|
+
- ✅ Detection of version/function body drift (DRIFTED state via checksum)
|
|
400
|
+
- ✅ Detection of manual SQL overrides (MANUAL_OVERRIDE state)
|
|
401
|
+
- ✅ Detection of unknown external triggers (UNKNOWN state)
|
|
402
|
+
- ✅ Detection of disabled triggers (DISABLED state)
|
|
403
|
+
- ✅ Detection of in-sync triggers (IN_SYNC state)
|
|
404
|
+
- ✅ Registry convenience methods (drifted, in_sync, unknown_triggers, dropped)
|
|
405
|
+
- ✅ TriggerRegistry instance methods (drift_state, drift_result, drifted?, in_sync?, dropped?)
|
|
406
|
+
- ✅ Comprehensive test coverage for Detector and Reporter
|
|
407
|
+
|
|
408
|
+
**From Section 3.F (Rails Console Introspection):**
|
|
409
|
+
- ✅ `PgSqlTriggers::Registry.list` (note: namespace differs slightly from goal)
|
|
410
|
+
- ✅ `PgSqlTriggers::Registry.enabled`
|
|
411
|
+
- ✅ `PgSqlTriggers::Registry.disabled`
|
|
412
|
+
- ✅ `PgSqlTriggers::Registry.for_table(:users)`
|
|
413
|
+
- ✅ `PgSqlTriggers::Registry.validate!`
|
|
414
|
+
- ✅ `PgSqlTriggers::Registry.diff` (fully working with drift detection)
|
|
415
|
+
- ✅ `PgSqlTriggers::Registry.drifted` (returns all drifted triggers)
|
|
416
|
+
- ✅ `PgSqlTriggers::Registry.in_sync` (returns all in-sync triggers)
|
|
417
|
+
- ✅ `PgSqlTriggers::Registry.unknown_triggers` (returns all external triggers)
|
|
418
|
+
- ✅ `PgSqlTriggers::Registry.dropped` (returns all dropped triggers)
|
|
419
|
+
- ✅ No raw SQL required by users for basic operations (enable/disable via console methods)
|
|
420
|
+
|
|
421
|
+
**From Section 5 (Permissions Model):**
|
|
422
|
+
- ✅ Permission structure exists (Viewer, Operator, Admin roles defined)
|
|
423
|
+
- ✅ Permission model classes exist
|
|
424
|
+
|
|
425
|
+
**From Section 6 (Kill Switch):**
|
|
426
|
+
- ✅ Fully implemented - see Section 6 for details
|
|
427
|
+
- ✅ Global disable configuration (default: true)
|
|
428
|
+
- ✅ Runtime ENV override support (KILL_SWITCH_OVERRIDE)
|
|
429
|
+
- ✅ Explicit confirmation text requirement
|
|
430
|
+
- ✅ Comprehensive logging and audit trail
|
|
431
|
+
- ✅ UI, CLI, and Console enforcement
|
|
432
|
+
- ✅ Thread-safe override mechanism
|
|
433
|
+
|
|
434
|
+
**From Section 8 (UI):**
|
|
435
|
+
- ✅ Dashboard with: Trigger name, Table, Version, Status (enabled/disabled), Source, Environment
|
|
436
|
+
- ⚠️ Dashboard displays drift count (UI shows drifted stat, but drift detection logic not implemented, so will be 0 or error)
|
|
437
|
+
- ✅ Tables view with table listing and trigger details
|
|
438
|
+
- ✅ Tables/show view shows trigger info for a specific table (not a dedicated trigger detail page)
|
|
439
|
+
- ✅ Generator UI (form-based wizard for creating triggers)
|
|
440
|
+
- ✅ Migration management UI (up/down/redo with kill switch protection)
|
|
441
|
+
- ❌ Trigger detail page (no dedicated route/page, only shown in tables/show)
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
### 🔴 HIGH PRIORITY - Critical Missing Features
|
|
446
|
+
|
|
447
|
+
**Note:** Priorities have been adjusted based on actual implementation status. SQL Capsules (marked MANDATORY in Section 4) moved from MEDIUM to HIGH priority as it's a critical missing feature.
|
|
448
|
+
|
|
449
|
+
#### 1. SQL Capsules (MANDATORY - Section 4) - CRITICAL
|
|
450
|
+
**Priority:** HIGH - Mandatory feature for emergency operations
|
|
451
|
+
|
|
452
|
+
**Status:** Routes defined, but no implementation
|
|
453
|
+
|
|
454
|
+
**Missing Files:**
|
|
455
|
+
- ❌ `lib/pg_sql_triggers/sql/capsule.rb` - SQL capsule definition class (autoloaded but file doesn't exist)
|
|
456
|
+
- ❌ `lib/pg_sql_triggers/sql/executor.rb` - SQL execution with transaction, checksum, registry update
|
|
457
|
+
- ❌ `app/controllers/pg_sql_triggers/sql_capsules_controller.rb` - UI controller (routes reference it but it doesn't exist)
|
|
458
|
+
- ❌ SQL capsule views (new, show, create, execute)
|
|
459
|
+
- ❌ SQL capsule storage mechanism (could use registry table with `source = manual_sql`)
|
|
460
|
+
|
|
461
|
+
**Missing Functionality:**
|
|
462
|
+
- ❌ Named SQL capsules with environment and purpose declaration
|
|
463
|
+
- ❌ Explicit application workflow with confirmation
|
|
464
|
+
- ❌ Transactional execution
|
|
465
|
+
- ❌ Checksum verification
|
|
466
|
+
- ❌ Registry update with `source = manual_sql`
|
|
467
|
+
- ❌ Kill switch protection (should block in production)
|
|
468
|
+
|
|
469
|
+
**Impact:** Critical feature marked as MANDATORY in goal but completely missing. Emergency SQL execution not possible.
|
|
470
|
+
|
|
471
|
+
#### 2. Drop & Re-Execute Flow (Section 9) - CRITICAL
|
|
472
|
+
**Priority:** HIGH - Operational requirements
|
|
473
|
+
|
|
474
|
+
**Status:** Not implemented
|
|
475
|
+
|
|
476
|
+
**Missing:**
|
|
477
|
+
- ❌ Drop trigger functionality with permission checks, kill switch, reason, typed confirmation
|
|
478
|
+
- ❌ Re-execute functionality with diff display, reason, typed confirmation
|
|
479
|
+
- ❌ UI for drop/re-execute actions
|
|
480
|
+
- ❌ Confirmation dialogs with typed confirmation text
|
|
481
|
+
- ❌ Transactional execution and registry update
|
|
482
|
+
|
|
483
|
+
**Impact:** Cannot safely drop or re-execute triggers. Operational workflows blocked.
|
|
484
|
+
|
|
485
|
+
#### 3. Safe Apply & Deploy (Section 3.D) - ✅ FULLY IMPLEMENTED
|
|
486
|
+
**Priority:** MEDIUM-HIGH - Deployment safety enhancement
|
|
487
|
+
|
|
488
|
+
**Status:** Fully implemented - pre-apply comparison and safety validation added
|
|
489
|
+
|
|
490
|
+
**What Works:**
|
|
491
|
+
- ✅ Migrations run in transactions
|
|
492
|
+
- ✅ Migration rollback supported
|
|
493
|
+
- ✅ Registry updated during migrations
|
|
494
|
+
- ✅ Pre-apply comparison (diff expected vs actual) before migration execution
|
|
495
|
+
- ✅ Diff reporting shows what will change before applying
|
|
496
|
+
- ✅ Safety validator blocks unsafe DROP + CREATE operations
|
|
497
|
+
- ✅ Explicit validation prevents migrations from blindly dropping and recreating existing objects
|
|
498
|
+
|
|
499
|
+
**Implementation Details:**
|
|
500
|
+
- `Migrator::SafetyValidator` class detects unsafe DROP + CREATE patterns in migrations
|
|
501
|
+
- Validator checks if migrations would drop existing database objects and recreate them
|
|
502
|
+
- Blocks migration execution if unsafe patterns detected (unless explicitly allowed)
|
|
503
|
+
- Configuration option `allow_unsafe_migrations` (default: false) for global override
|
|
504
|
+
- Environment variable `ALLOW_UNSAFE_MIGRATIONS=true` for per-migration override
|
|
505
|
+
- Provides clear error messages explaining unsafe operations and how to proceed
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
509
|
+
### 🟡 MEDIUM PRIORITY - User-Facing Features
|
|
510
|
+
|
|
511
|
+
#### 4. Trigger Detail Page (Section 8 - UI)
|
|
512
|
+
**Priority:** MEDIUM - Usability
|
|
513
|
+
|
|
514
|
+
**Status:** Partial (shown in tables/show but not dedicated page)
|
|
515
|
+
|
|
516
|
+
**Missing:**
|
|
517
|
+
- ❌ Dedicated trigger detail route and controller action
|
|
518
|
+
- ❌ Summary panel with all trigger metadata
|
|
519
|
+
- ❌ SQL diff view (expected vs actual)
|
|
520
|
+
- ❌ Registry state display
|
|
521
|
+
- ❌ Action buttons (Enable/Disable/Drop/Re-execute/Execute SQL capsule)
|
|
522
|
+
- ❌ Permission-aware, environment-aware, kill switch-aware button visibility
|
|
523
|
+
|
|
524
|
+
#### 5. UI Actions (Section 8)
|
|
525
|
+
**Priority:** MEDIUM - Usability
|
|
526
|
+
|
|
527
|
+
**Status:** Backend methods exist, UI buttons missing
|
|
528
|
+
|
|
529
|
+
**Missing:**
|
|
530
|
+
- ❌ Enable/Disable buttons in dashboard and tables/show pages (methods exist: `TriggerRegistry#enable!` and `#disable!`)
|
|
531
|
+
- ❌ Drop button (requires drop functionality from Section 9)
|
|
532
|
+
- ❌ Re-execute button (requires re-execute functionality from Section 9)
|
|
533
|
+
- ❌ Execute SQL capsule button (requires SQL capsules from Section 4)
|
|
534
|
+
|
|
535
|
+
**What Works:**
|
|
536
|
+
- ✅ Kill switch enforcement in UI (fully implemented - see Section 6)
|
|
537
|
+
- ✅ Migration actions (up/down/redo) with kill switch protection
|
|
538
|
+
|
|
539
|
+
#### 6. Permissions Enforcement (Section 5)
|
|
540
|
+
**Priority:** MEDIUM - Security
|
|
541
|
+
|
|
542
|
+
**Status:** Permission structure exists but not enforced
|
|
543
|
+
|
|
544
|
+
**Missing:**
|
|
545
|
+
- ❌ Permission checking in controllers (UI actions should check permissions)
|
|
546
|
+
- ❌ Permission checking in UI (hide/disable buttons based on role)
|
|
547
|
+
- ❌ Permission checks in `TriggerRegistry#enable!` and `disable!` (currently only kill switch checked)
|
|
548
|
+
- ❌ Permission checks in rake tasks
|
|
549
|
+
- ❌ Permission checks in console APIs
|
|
550
|
+
- ❌ Actor context passing through all operations
|
|
551
|
+
|
|
552
|
+
**What Exists:**
|
|
553
|
+
- ✅ Permission structure (Viewer, Operator, Admin roles defined)
|
|
554
|
+
- ✅ Permission model classes (`PgSqlTriggers::Permissions::Checker`)
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
### 🟢 LOW PRIORITY - Polish & Improvements
|
|
559
|
+
|
|
560
|
+
#### 7. Enhanced Logging & Audit Trail
|
|
561
|
+
**Priority:** LOW - Operational polish
|
|
562
|
+
|
|
563
|
+
**Status:** Kill switch logging is comprehensive; audit trail could be enhanced
|
|
564
|
+
|
|
565
|
+
**Missing:**
|
|
566
|
+
- ✅ Kill switch activation attempts logging (fully implemented)
|
|
567
|
+
- ✅ Kill switch overrides logging (fully implemented)
|
|
568
|
+
- ⚠️ Comprehensive audit trail table for production operation attempts (optional enhancement - logging exists but structured audit table would be better)
|
|
569
|
+
|
|
570
|
+
#### 8. Error Handling Consistency
|
|
571
|
+
**Priority:** LOW - Code quality
|
|
572
|
+
|
|
573
|
+
**Status:** Kill switch errors are properly implemented; other error types need consistency
|
|
574
|
+
|
|
575
|
+
**Missing:**
|
|
576
|
+
- ✅ Kill switch violations raise `KillSwitchError` (fully implemented)
|
|
577
|
+
- ❌ Permission violations should raise `PermissionError`
|
|
578
|
+
- ✅ Drift detection implemented (can be used for error handling)
|
|
579
|
+
- ❌ Consistent error handling across all operations
|
|
580
|
+
|
|
581
|
+
#### 9. Testing Coverage
|
|
582
|
+
**Priority:** LOW - Quality assurance
|
|
583
|
+
|
|
584
|
+
**Status:** Kill switch has comprehensive tests; other areas need coverage
|
|
585
|
+
|
|
586
|
+
**Missing:**
|
|
587
|
+
- ❌ SQL capsules need tests
|
|
588
|
+
- ✅ Kill switch has comprehensive tests (fully tested)
|
|
589
|
+
- ✅ Drift detection has comprehensive tests (fully tested)
|
|
590
|
+
- ❌ Permission enforcement needs tests
|
|
591
|
+
- ❌ Drop/re-execute flow needs tests
|
|
592
|
+
|
|
593
|
+
#### 10. Documentation Updates
|
|
594
|
+
**Priority:** LOW - User experience
|
|
595
|
+
|
|
596
|
+
**Status:** Kill switch is well documented; other areas need documentation
|
|
597
|
+
|
|
598
|
+
**Missing:**
|
|
599
|
+
- ❌ README mentions SQL capsules but no implementation details
|
|
600
|
+
- ✅ README includes kill switch documentation with enforcement details (fully documented)
|
|
601
|
+
- ❌ Need examples for SQL capsules
|
|
602
|
+
- ❌ Need examples for permission configuration
|
|
603
|
+
- ✅ Drift detection fully documented in implementation plan
|
|
604
|
+
|
|
605
|
+
#### 11. Partial Implementation Notes
|
|
606
|
+
**Priority:** LOW - Known issues and technical debt
|
|
607
|
+
|
|
608
|
+
**Known Issues:**
|
|
609
|
+
- ⚠️ **Permissions Model** - Structure exists but not enforced in UI/CLI/console
|
|
610
|
+
- ✅ **Kill Switch** - Fully implemented (see Section 6 for details)
|
|
611
|
+
- ✅ **Checksum** - Fully implemented with consistent field-concatenation algorithm across all creation paths
|
|
612
|
+
- ✅ **Drift Detection** - Fully implemented with all 6 drift states, comprehensive tests, and console APIs
|
|
613
|
+
- ⚠️ **Dashboard** - `installed_at` exists in registry table but not displayed in UI
|
|
614
|
+
- ⚠️ **Trigger Detail Page** - No dedicated route/page, info shown in tables/show view only
|
|
615
|
+
- ⚠️ **Enable/Disable UI** - Console methods exist with kill switch protection, but no UI buttons
|
|
616
|
+
|
|
617
|
+
---
|
|
618
|
+
|
|
619
|
+
### 📝 Technical Notes
|
|
620
|
+
|
|
621
|
+
1. **Console API Naming:** Goal shows `PgSqlTrigger.list` but implementation is `PgSqlTriggers::Registry.list` (current is better, just note the difference)
|