activity_notification 2.3.3 → 2.4.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +7 -39
  3. data/CHANGELOG.md +18 -0
  4. data/Gemfile +1 -3
  5. data/README.md +9 -1
  6. data/activity_notification.gemspec +5 -5
  7. data/ai-curated-specs/issues/172/design.md +220 -0
  8. data/ai-curated-specs/issues/172/tasks.md +326 -0
  9. data/ai-curated-specs/issues/188/design.md +227 -0
  10. data/ai-curated-specs/issues/188/requirements.md +78 -0
  11. data/ai-curated-specs/issues/188/tasks.md +203 -0
  12. data/ai-curated-specs/issues/188/upstream-contributions.md +592 -0
  13. data/ai-curated-specs/issues/50/design.md +235 -0
  14. data/ai-curated-specs/issues/50/requirements.md +49 -0
  15. data/ai-curated-specs/issues/50/tasks.md +232 -0
  16. data/app/controllers/activity_notification/notifications_api_controller.rb +22 -0
  17. data/app/controllers/activity_notification/notifications_controller.rb +27 -1
  18. data/app/mailers/activity_notification/mailer.rb +2 -2
  19. data/app/views/activity_notification/notifications/default/_index.html.erb +6 -1
  20. data/app/views/activity_notification/notifications/default/destroy_all.js.erb +6 -0
  21. data/docs/Setup.md +43 -6
  22. data/gemfiles/Gemfile.rails-7.0 +2 -0
  23. data/gemfiles/Gemfile.rails-8.0 +0 -2
  24. data/lib/activity_notification/apis/notification_api.rb +51 -2
  25. data/lib/activity_notification/controllers/concerns/swagger/notifications_api.rb +59 -0
  26. data/lib/activity_notification/helpers/view_helpers.rb +28 -0
  27. data/lib/activity_notification/mailers/helpers.rb +14 -7
  28. data/lib/activity_notification/models/concerns/target.rb +16 -0
  29. data/lib/activity_notification/models.rb +1 -1
  30. data/lib/activity_notification/notification_resilience.rb +115 -0
  31. data/lib/activity_notification/orm/dynamoid/extension.rb +4 -87
  32. data/lib/activity_notification/orm/dynamoid/notification.rb +19 -2
  33. data/lib/activity_notification/orm/dynamoid.rb +42 -6
  34. data/lib/activity_notification/rails/routes.rb +1 -0
  35. data/lib/activity_notification/version.rb +1 -1
  36. data/lib/activity_notification.rb +1 -0
  37. data/lib/generators/templates/controllers/notifications_api_controller.rb +5 -0
  38. data/lib/generators/templates/controllers/notifications_api_with_devise_controller.rb +5 -0
  39. data/lib/generators/templates/controllers/notifications_controller.rb +5 -0
  40. data/lib/generators/templates/controllers/notifications_with_devise_controller.rb +5 -0
  41. data/spec/concerns/apis/notification_api_spec.rb +161 -5
  42. data/spec/concerns/models/target_spec.rb +7 -0
  43. data/spec/controllers/controller_spec_utility.rb +1 -1
  44. data/spec/controllers/notifications_api_controller_shared_examples.rb +113 -0
  45. data/spec/controllers/notifications_controller_shared_examples.rb +150 -0
  46. data/spec/helpers/view_helpers_spec.rb +14 -0
  47. data/spec/jobs/notification_resilience_job_spec.rb +167 -0
  48. data/spec/mailers/notification_resilience_spec.rb +263 -0
  49. data/spec/models/notification_spec.rb +1 -1
  50. data/spec/models/subscription_spec.rb +1 -1
  51. data/spec/rails_app/app/helpers/devise_helper.rb +2 -0
  52. data/spec/rails_app/config/application.rb +1 -0
  53. data/spec/rails_app/config/initializers/zeitwerk.rb +10 -0
  54. metadata +63 -50
@@ -0,0 +1,326 @@
1
+ # Implementation Plan
2
+
3
+ ## Problem 1: Mongoid ORM Compatibility Issue with ID Array Filtering
4
+
5
+ ### Issue Description
6
+ The bulk destroy functionality works correctly with ActiveRecord ORM but fails with Mongoid ORM. Specifically, the test case `context 'with ids options'` in `spec/concerns/apis/notification_api_spec.rb` is failing.
7
+
8
+ **Location**: `lib/activity_notification/apis/notification_api.rb` line 440
9
+ **Problematic Code**:
10
+ ```ruby
11
+ target_notifications = target_notifications.where(id: options[:ids])
12
+ ```
13
+
14
+ ### Root Cause Analysis
15
+ The issue stems from different query syntax requirements between ActiveRecord and Mongoid when filtering by an array of IDs:
16
+
17
+ 1. **ActiveRecord**: Uses `where(id: [1, 2, 3])` which automatically translates to SQL `WHERE id IN (1, 2, 3)`
18
+ 2. **Mongoid**: Requires explicit `$in` operator syntax for array matching: `where(id: { '$in' => [1, 2, 3] })`
19
+
20
+ ### Current Implementation Problem
21
+ The current implementation uses ActiveRecord syntax:
22
+ ```ruby
23
+ target_notifications = target_notifications.where(id: options[:ids])
24
+ ```
25
+
26
+ This works for ActiveRecord but fails for Mongoid because Mongoid doesn't automatically interpret an array as an `$in` operation.
27
+
28
+ ### Expected Behavior
29
+ - When `options[:ids]` contains `[notification1.id, notification2.id]`, only notifications with those specific IDs should be destroyed
30
+ - The filtering should work consistently across both ActiveRecord and Mongoid ORMs
31
+ - Other filter options should still be applied in combination with ID filtering
32
+
33
+ ### Test Case Analysis
34
+ The failing test:
35
+ ```ruby
36
+ it "destroys notifications with specified IDs only" do
37
+ notification_to_destroy = @user_1.notifications.first
38
+ described_class.destroy_all_of(@user_1, { ids: [notification_to_destroy.id] })
39
+ expect(@user_1.notifications.count).to eq(1)
40
+ expect(@user_1.notifications.first).not_to eq(notification_to_destroy)
41
+ end
42
+ ```
43
+
44
+ This test expects that when an array of IDs is provided, only those specific notifications are destroyed.
45
+
46
+ ### Solution Strategy
47
+ Implement ORM-specific ID filtering logic that:
48
+
49
+ 1. **Detection**: Check the current ORM configuration using `ActivityNotification.config.orm`
50
+ 2. **ActiveRecord Path**: Use existing `where(id: options[:ids])` syntax
51
+ 3. **Mongoid Path**: Use `where(id: { '$in' => options[:ids] })` syntax
52
+ 4. **Dynamoid Path**: Use `where(‘id.in‘: options[:ids])` syntax
53
+
54
+ ### Implementation Plan
55
+ 1. **Conditional Logic**: Add ORM detection in the `destroy_all_of` method
56
+ 2. **Mongoid Syntax**: Use `{ '$in' => options[:ids] }` for Mongoid
57
+ 3. **Backward Compatibility**: Ensure ActiveRecord continues to work as before
58
+ 4. **Testing**: Verify both ORMs work correctly with the new implementation
59
+
60
+ ### Code Changes Required
61
+ **File**: `lib/activity_notification/apis/notification_api.rb`
62
+ **Method**: `destroy_all_of` (around line 440)
63
+
64
+ Replace:
65
+ ```ruby
66
+ if options[:ids].present?
67
+ target_notifications = target_notifications.where(id: options[:ids])
68
+ end
69
+ ```
70
+
71
+ With ORM-specific logic:
72
+ ```ruby
73
+ if options[:ids].present?
74
+ case ActivityNotification.config.orm
75
+ when :mongoid
76
+ target_notifications = target_notifications.where(id: { '$in' => options[:ids] })
77
+ when :dynamoid
78
+ target_notifications = target_notifications.where('id.in': options[:ids])
79
+ else # :active_record
80
+ target_notifications = target_notifications.where(id: options[:ids])
81
+ end
82
+ end
83
+ ```
84
+
85
+ ### Testing Requirements
86
+ 1. **Unit Tests**: Ensure the method works with both ActiveRecord and Mongoid
87
+ 2. **Integration Tests**: Verify the complete destroy_all functionality
88
+ 3. **Regression Tests**: Ensure existing functionality remains intact
89
+
90
+ ### Risk Assessment
91
+ - **Low Risk**: The change is isolated to the ID filtering logic
92
+ - **Backward Compatible**: ActiveRecord behavior remains unchanged
93
+ - **Well-Tested**: Existing test suite will catch any regressions
94
+
95
+ ### Future Considerations
96
+ - Consider extracting ORM-specific query logic into a helper method if more similar cases arise
97
+ - Document the ORM differences for future developers
98
+ - Consider adding similar logic to other methods that might have the same issue
99
+
100
+ ---
101
+
102
+ ## Problem 2: Add IDs Parameter to open_all API
103
+
104
+ ### Issue Description
105
+ Enhance the `open_all_of` method to support the `ids` parameter functionality, similar to the implementation in `destroy_all_of`. This will allow users to open specific notifications by providing an array of notification IDs.
106
+
107
+ ### Current State Analysis
108
+
109
+ #### Existing open_all_of Method
110
+ **Location**: `lib/activity_notification/apis/notification_api.rb` (around line 415)
111
+
112
+ **Current Implementation**:
113
+ ```ruby
114
+ def open_all_of(target, options = {})
115
+ opened_at = options[:opened_at] || Time.current
116
+ target_unopened_notifications = target.notifications.unopened_only.filtered_by_options(options)
117
+ opened_notifications = target_unopened_notifications.to_a.map { |n| n.opened_at = opened_at; n }
118
+ target_unopened_notifications.update_all(opened_at: opened_at)
119
+ opened_notifications
120
+ end
121
+ ```
122
+
123
+ **Current Parameters**:
124
+ - `opened_at`: Time to set to opened_at of the notification record
125
+ - `filtered_by_type`: Notifiable type for filter
126
+ - `filtered_by_group`: Group instance for filter
127
+ - `filtered_by_group_type`: Group type for filter, valid with :filtered_by_group_id
128
+ - `filtered_by_group_id`: Group instance id for filter, valid with :filtered_by_group_type
129
+ - `filtered_by_key`: Key of the notification for filter
130
+ - `later_than`: ISO 8601 format time to filter notification index later than specified time
131
+ - `earlier_than`: ISO 8601 format time to filter notification index earlier than specified time
132
+
133
+ ### Proposed Enhancement
134
+
135
+ #### Add IDs Parameter Support
136
+ Add support for the `ids` parameter to allow opening specific notifications by their IDs, following the same pattern as `destroy_all_of`.
137
+
138
+ #### Updated Method Signature
139
+ ```ruby
140
+ # @option options [Array] :ids (nil) Array of specific notification IDs to open
141
+ def open_all_of(target, options = {})
142
+ ```
143
+
144
+ ### Implementation Strategy
145
+ 1. **Reuse existing pattern**: Follow the same ORM-specific ID filtering logic implemented in `destroy_all_of`
146
+ 2. **Maintain backward compatibility**: Ensure existing functionality remains unchanged
147
+ 3. **Consistent behavior**: Apply ID filtering after other filters, similar to destroy_all_of
148
+
149
+ ### Code Changes Required
150
+
151
+ #### 1. Update Method Documentation
152
+ **File**: `lib/activity_notification/apis/notification_api.rb`
153
+
154
+ Add the `ids` parameter to the method documentation:
155
+ ```ruby
156
+ # @option options [Array] :ids (nil) Array of specific notification IDs to open
157
+ ```
158
+
159
+ #### 2. Add ID Filtering Logic
160
+ Insert the same ORM-specific ID filtering logic used in `destroy_all_of`:
161
+
162
+ ```ruby
163
+ def open_all_of(target, options = {})
164
+ opened_at = options[:opened_at] || Time.current
165
+ target_unopened_notifications = target.notifications.unopened_only.filtered_by_options(options)
166
+
167
+ # Add ID filtering logic (same as destroy_all_of)
168
+ if options[:ids].present?
169
+ # :nocov:
170
+ case ActivityNotification.config.orm
171
+ when :mongoid
172
+ target_unopened_notifications = target_unopened_notifications.where(id: { '$in' => options[:ids] })
173
+ when :dynamoid
174
+ target_unopened_notifications = target_unopened_notifications.where('id.in': options[:ids])
175
+ else # :active_record
176
+ target_unopened_notifications = target_unopened_notifications.where(id: options[:ids])
177
+ end
178
+ # :nocov:
179
+ end
180
+
181
+ opened_notifications = target_unopened_notifications.to_a.map { |n| n.opened_at = opened_at; n }
182
+ target_unopened_notifications.update_all(opened_at: opened_at)
183
+ opened_notifications
184
+ end
185
+ ```
186
+
187
+ #### 3. Update Controller Actions
188
+ The controller actions that use `open_all_of` should be updated to accept and pass through the `ids` parameter:
189
+
190
+ **Files to Update**:
191
+ - `app/controllers/activity_notification/notifications_controller.rb`
192
+ - `app/controllers/activity_notification/notifications_api_controller.rb`
193
+
194
+ **Parameter Addition**:
195
+ ```ruby
196
+ # Add :ids to permitted parameters
197
+ params.permit(:ids => [])
198
+ ```
199
+
200
+ #### 4. Update API Documentation
201
+ **File**: `lib/activity_notification/controllers/concerns/swagger/notifications_api.rb`
202
+
203
+ Add `ids` parameter to the Swagger documentation for the open_all endpoint:
204
+ ```ruby
205
+ parameter do
206
+ key :name, :ids
207
+ key :in, :query
208
+ key :description, 'Array of specific notification IDs to open'
209
+ key :required, false
210
+ key :type, :array
211
+ items do
212
+ key :type, :string
213
+ end
214
+ end
215
+ ```
216
+
217
+ ### Testing Requirements
218
+
219
+ #### 1. Add Test Cases
220
+ **File**: `spec/concerns/apis/notification_api_spec.rb`
221
+
222
+ Add test cases similar to the `destroy_all_of` tests:
223
+
224
+ ```ruby
225
+ context 'with ids options' do
226
+ it "opens notifications with specified IDs only" do
227
+ notification_to_open = @user_1.notifications.first
228
+ described_class.open_all_of(@user_1, { ids: [notification_to_open.id] })
229
+ expect(@user_1.notifications.unopened_only.count).to eq(1)
230
+ expect(@user_1.notifications.opened_only!.count).to eq(1)
231
+ expect(@user_1.notifications.opened_only!.first).to eq(notification_to_open)
232
+ end
233
+
234
+ it "applies other filter options when ids are specified" do
235
+ notification_to_open = @user_1.notifications.first
236
+ described_class.open_all_of(@user_1, {
237
+ ids: [notification_to_open.id],
238
+ filtered_by_key: 'non_existent_key'
239
+ })
240
+ expect(@user_1.notifications.unopened_only.count).to eq(2)
241
+ expect(@user_1.notifications.opened_only!.count).to eq(0)
242
+ end
243
+
244
+ it "only opens unopened notifications even when opened notification IDs are provided" do
245
+ # First open one notification
246
+ notification_to_open = @user_1.notifications.first
247
+ notification_to_open.open!
248
+
249
+ # Try to open it again using ids parameter
250
+ described_class.open_all_of(@user_1, { ids: [notification_to_open.id] })
251
+
252
+ # Should not affect the count since it was already opened
253
+ expect(@user_1.notifications.unopened_only.count).to eq(1)
254
+ expect(@user_1.notifications.opened_only!.count).to eq(1)
255
+ end
256
+ end
257
+ ```
258
+
259
+ #### 2. Update Controller Tests
260
+ **File**: `spec/controllers/notifications_api_controller_shared_examples.rb`
261
+
262
+ Add test cases for the API controller to ensure the `ids` parameter is properly handled:
263
+
264
+ ```ruby
265
+ context 'with ids parameter' do
266
+ it "opens only specified notifications" do
267
+ notification_to_open = @user.notifications.first
268
+ post open_all_notification_path(@user), params: { ids: [notification_to_open.id] }
269
+ expect(response).to have_http_status(200)
270
+ expect(@user.notifications.unopened_only.count).to eq(1)
271
+ expect(@user.notifications.opened_only!.count).to eq(1)
272
+ end
273
+ end
274
+ ```
275
+
276
+ ### Benefits
277
+
278
+ #### 1. Consistency
279
+ - Provides consistent API between `open_all_of` and `destroy_all_of` methods
280
+ - Both methods now support the same filtering options including `ids`
281
+
282
+ #### 2. Flexibility
283
+ - Allows precise control over which notifications to open
284
+ - Enables batch operations on specific notifications
285
+ - Supports complex filtering combinations
286
+
287
+ #### 3. Performance
288
+ - Efficient database operations using bulk updates
289
+ - Reduces the need for multiple individual open operations
290
+
291
+ #### 4. User Experience
292
+ - Provides the functionality requested in the original issue
293
+ - Enables building more sophisticated notification management UIs
294
+
295
+ ### Implementation Considerations
296
+
297
+ #### 1. Backward Compatibility
298
+ - All existing functionality remains unchanged
299
+ - New `ids` parameter is optional
300
+ - Existing tests should continue to pass
301
+
302
+ #### 2. ORM Compatibility
303
+ - Uses the same ORM-specific logic as `destroy_all_of`
304
+ - Tested across ActiveRecord, Mongoid, and Dynamoid
305
+
306
+ #### 3. Security
307
+ - ID filtering is applied after target validation
308
+ - Only notifications belonging to the specified target can be opened
309
+ - Follows existing security patterns
310
+
311
+ #### 4. Error Handling
312
+ - Invalid IDs are silently ignored (consistent with existing behavior)
313
+ - Non-existent notifications don't cause errors
314
+ - Maintains existing error handling patterns
315
+
316
+ ### Risk Assessment
317
+ - **Low Risk**: Follows established patterns from `destroy_all_of`
318
+ - **Backward Compatible**: ActiveRecord behavior remains unchanged
319
+ - **Well-Tested**: Existing test suite will catch any regressions
320
+
321
+ ### Implementation Timeline
322
+ 1. **Phase 1**: Update `open_all_of` method with ID filtering logic
323
+ 2. **Phase 2**: Add comprehensive test cases
324
+ 3. **Phase 3**: Update controller actions and API documentation
325
+ 4. **Phase 4**: Update controller tests and integration tests
326
+ 5. **Phase 5**: Documentation updates and final testing
@@ -0,0 +1,227 @@
1
+ # Design Document
2
+
3
+ ## Overview
4
+
5
+ This design outlines the approach for upgrading activity_notification's Dynamoid dependency from v3.1.0 to v3.11.0. The upgrade involves updating namespace references, method signatures, and class hierarchies that have changed between these versions, while maintaining backward compatibility and preparing useful enhancements for upstream contribution to Dynamoid.
6
+
7
+ ## Architecture
8
+
9
+ ### Current Architecture
10
+ - **Dynamoid Extension**: `lib/activity_notification/orm/dynamoid/extension.rb` contains monkey patches extending Dynamoid v3.1.0
11
+ - **ORM Integration**: `lib/activity_notification/orm/dynamoid.rb` provides ActivityNotification-specific query methods
12
+ - **Dependency Management**: Gemspec pins Dynamoid to exactly v3.1.0
13
+
14
+ ### Target Architecture
15
+ - **Updated Extension**: Refactored extension file compatible with Dynamoid v3.11.0 namespaces
16
+ - **Backward Compatibility**: Maintained API surface for existing applications
17
+ - **Upstream Preparation**: Clean, well-documented code ready for Dynamoid contribution
18
+ - **Flexible Dependency**: Version range allowing v3.11.0+ while preventing breaking v4.0 changes
19
+
20
+ ## Components and Interfaces
21
+
22
+ ### 1. Gemspec Update
23
+ **File**: `activity_notification.gemspec`
24
+ **Changes**:
25
+ - Update Dynamoid dependency from development dependency `'3.1.0'` to runtime dependency `'>= 3.11.0', '< 4.0'`
26
+ - Change dependency type from `add_development_dependency` to `add_dependency` for production use
27
+ - Ensure compatibility with Rails version constraints
28
+
29
+ ### 2. Extension Module Refactoring
30
+ **File**: `lib/activity_notification/orm/dynamoid/extension.rb`
31
+
32
+ #### 2.1 Critical Namespace Changes
33
+ Based on analysis of Dynamoid v3.1.0 vs v3.11.0:
34
+
35
+ **Query and Scan Class Locations**:
36
+ - **v3.1.0**: `Dynamoid::AdapterPlugin::Query` and `Dynamoid::AdapterPlugin::Scan`
37
+ - **v3.11.0**: `Dynamoid::AdapterPlugin::AwsSdkV3::Query` and `Dynamoid::AdapterPlugin::AwsSdkV3::Scan`
38
+
39
+ **Method Signature Changes**:
40
+ - **v3.1.0**: `Query.new(client, table, opts = {})`
41
+ - **v3.11.0**: `Query.new(client, table, key_conditions, non_key_conditions, options)`
42
+
43
+ #### 2.2 Removed Constants and Methods
44
+ **Constants removed in v3.11.0**:
45
+ - `FIELD_MAP` - Used for condition mapping
46
+ - `RANGE_MAP` - Used for range condition mapping
47
+ - `attribute_value_list()` method - No longer exists
48
+
49
+ **New Architecture in v3.11.0**:
50
+ - Uses `FilterExpressionConvertor` for building filter expressions
51
+ - Uses expression attribute names and values instead of legacy condition format
52
+ - Middleware pattern for handling backoff, limits, and pagination
53
+
54
+ #### 2.3 Class Hierarchy Updates Required
55
+ - Update inheritance from `::Dynamoid::AdapterPlugin::Query` to `::Dynamoid::AdapterPlugin::AwsSdkV3::Query`
56
+ - Update inheritance from `::Dynamoid::AdapterPlugin::Scan` to `::Dynamoid::AdapterPlugin::AwsSdkV3::Scan`
57
+ - Remove references to `FIELD_MAP`, `RANGE_MAP`, and `attribute_value_list()`
58
+ - Adapt to new filter expression format
59
+
60
+ ### 3. Core Functionality Preservation
61
+ **Methods to Maintain**:
62
+ - `none()` - Returns empty result set
63
+ - `limit()` - Aliases to `record_limit()`
64
+ - `exists?()` - Checks if records exist
65
+ - `update_all()` - Batch update operations
66
+ - `serializable_hash()` - Array serialization
67
+ - Null/not_null operators for query filtering
68
+ - Uniqueness validation support
69
+
70
+ ### 4. Upstream Contribution Preparation
71
+ **Target Methods for Contribution**:
72
+ - `Chain#none` - Useful empty result pattern
73
+ - `Chain#limit` - More intuitive alias for `record_limit`
74
+ - `Chain#exists?` - Common query pattern
75
+ - `Chain#update_all` - Batch operations
76
+ - Null operator extensions - Enhanced query capabilities
77
+ - Uniqueness validator - Common validation need
78
+
79
+ ## Data Models
80
+
81
+ ### Extension Points
82
+ ```ruby
83
+ # Current structure (v3.1.0)
84
+ module Dynamoid
85
+ module Criteria
86
+ class Chain
87
+ # Extension methods work with @query hash
88
+ end
89
+ end
90
+
91
+ module AdapterPlugin
92
+ class AwsSdkV3
93
+ class Query < ::Dynamoid::AdapterPlugin::Query
94
+ # Uses FIELD_MAP, RANGE_MAP, attribute_value_list
95
+ def query_filter
96
+ # Legacy condition format
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ # Target structure (v3.11.0) - CONFIRMED
104
+ module Dynamoid
105
+ module Criteria
106
+ class Chain
107
+ # Extension methods work with @where_conditions object
108
+ # Uses KeyFieldsDetector and WhereConditions classes
109
+ end
110
+ end
111
+
112
+ module AdapterPlugin
113
+ class AwsSdkV3
114
+ class Query < ::Dynamoid::AdapterPlugin::AwsSdkV3::Query # CHANGED NAMESPACE
115
+ # Uses FilterExpressionConvertor instead of FIELD_MAP
116
+ # No more query_filter method - uses filter_expression
117
+ def initialize(client, table, key_conditions, non_key_conditions, options)
118
+ # CHANGED SIGNATURE
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ ```
125
+
126
+ ### Critical Breaking Changes
127
+ 1. **Query/Scan inheritance path changed**: `::Dynamoid::AdapterPlugin::Query` → `::Dynamoid::AdapterPlugin::AwsSdkV3::Query`
128
+ 2. **Constructor signature changed**: Single options hash → separate key/non-key conditions + options
129
+ 3. **Filter building changed**: `FIELD_MAP`/`RANGE_MAP` → `FilterExpressionConvertor`
130
+ 4. **Method removal**: `attribute_value_list()`, `query_filter()`, `scan_filter()` methods removed
131
+
132
+ ### Configuration Changes
133
+ - No breaking changes to ActivityNotification configuration
134
+ - Maintain existing API for `acts_as_notification_target`, `acts_as_notifiable`, etc.
135
+ - Preserve all existing query method signatures
136
+
137
+ ## Error Handling
138
+
139
+ ### Migration Strategy
140
+ 1. **Gradual Rollout**: Support version range to allow gradual adoption
141
+ 2. **Fallback Mechanisms**: Detect Dynamoid version and use appropriate code paths if needed
142
+ 3. **Clear Error Messages**: Provide helpful errors if incompatible versions are used
143
+
144
+ ### Exception Handling
145
+ - **NameError**: Handle missing classes/modules gracefully
146
+ - **NoMethodError**: Provide fallbacks for changed method signatures
147
+ - **ArgumentError**: Handle parameter changes in Dynamoid methods
148
+
149
+ ### Validation
150
+ - Runtime checks for critical Dynamoid functionality
151
+ - Test coverage for all supported Dynamoid versions
152
+ - Integration tests with real DynamoDB operations
153
+
154
+ ## Testing Strategy
155
+
156
+ ### Unit Tests
157
+ - Test all extension methods with Dynamoid v3.11.0
158
+ - Verify namespace resolution works correctly
159
+ - Test error handling for edge cases
160
+
161
+ ### Integration Tests
162
+ - Full ActivityNotification workflow with DynamoDB
163
+ - Performance regression testing
164
+ - Memory usage validation
165
+
166
+ ### Compatibility Tests
167
+ - Test with multiple Dynamoid versions in range
168
+ - Verify no breaking changes for existing applications
169
+ - Test upgrade path from v3.1.0 to v3.11.0
170
+
171
+ ### Upstream Preparation Tests
172
+ - Isolated tests for each method proposed for contribution
173
+ - Documentation examples that work standalone
174
+ - Performance benchmarks for contributed methods
175
+
176
+ ## Implementation Phases
177
+
178
+ ### Phase 1: Research and Analysis ✅ COMPLETED
179
+ - ✅ Compare Dynamoid v3.1.0 vs v3.11.0 source code
180
+ - ✅ Identify all namespace and method signature changes
181
+ - ✅ Create compatibility matrix
182
+
183
+ **Key Findings**:
184
+ - Query/Scan classes moved from `AdapterPlugin::` to `AdapterPlugin::AwsSdkV3::`
185
+ - Constructor signatures completely changed
186
+ - FIELD_MAP/RANGE_MAP constants removed
187
+ - Filter building now uses FilterExpressionConvertor
188
+ - Legacy query_filter/scan_filter methods removed
189
+
190
+ ### Phase 2: Core Updates
191
+ - Update gemspec dependency
192
+ - Refactor extension.rb for new namespaces
193
+ - Maintain existing functionality
194
+
195
+ ### Phase 3: Testing and Validation
196
+ - Update test suite for new Dynamoid version
197
+ - Run comprehensive integration tests using `AN_ORM=dynamoid bundle exec rspec`
198
+ - Fix failing tests to ensure all Dynamoid-related functionality works
199
+ - Performance validation
200
+ - Verify all existing test scenarios pass with new Dynamoid version
201
+
202
+ ### Phase 4: Upstream Preparation
203
+ - Extract reusable methods into separate modules
204
+ - Create documentation and examples
205
+ - Prepare pull requests for Dynamoid project
206
+
207
+ ### Phase 5: Documentation and Release
208
+ - Update CHANGELOG with breaking changes
209
+ - Update README with version requirements
210
+ - Release new version with proper semantic versioning
211
+
212
+ ## Risk Mitigation
213
+
214
+ ### Breaking Changes
215
+ - Use version range to prevent automatic v4.0 adoption
216
+ - Provide clear upgrade documentation
217
+ - Maintain backward compatibility where possible
218
+
219
+ ### Performance Impact
220
+ - Benchmark critical query operations
221
+ - Monitor memory usage changes
222
+ - Test with large datasets
223
+
224
+ ### Upstream Contribution Risks
225
+ - Prepare contributions as optional enhancements
226
+ - Ensure activity_notification works without upstream acceptance
227
+ - Maintain local implementations as fallbacks
@@ -0,0 +1,78 @@
1
+ # Requirements Document
2
+
3
+ ## Introduction
4
+
5
+ This feature involves upgrading the activity_notification gem's Dynamoid dependency from the outdated v3.1.0 to the latest v3.11.0. The upgrade requires updating namespace references and method calls that have changed between these versions, while maintaining backward compatibility and ensuring all existing functionality continues to work correctly.
6
+
7
+ ## Requirements
8
+
9
+ ### Requirement 1
10
+
11
+ **User Story:** As a developer using activity_notification with DynamoDB, I want the gem to support the latest Dynamoid version so that I can benefit from bug fixes, performance improvements, and security updates.
12
+
13
+ #### Acceptance Criteria
14
+
15
+ 1. WHEN the gemspec is updated THEN the Dynamoid dependency SHALL be changed from development dependency '3.1.0' to runtime dependency '>= 3.11.0', '< 4.0'
16
+ 2. WHEN the gem is installed THEN it SHALL successfully resolve dependencies with Dynamoid v3.11.0
17
+ 3. WHEN existing applications upgrade THEN they SHALL continue to work without breaking changes
18
+
19
+ ### Requirement 2
20
+
21
+ **User Story:** As a maintainer of activity_notification, I want to update the Dynamoid extension code to use the correct namespaces and method signatures so that the gem works with the latest Dynamoid version.
22
+
23
+ #### Acceptance Criteria
24
+
25
+ 1. WHEN the extension file is updated THEN all namespace references SHALL match Dynamoid v3.11.0 structure
26
+ 2. WHEN Dynamoid classes are referenced THEN they SHALL use the correct module paths from v3.11.0
27
+ 3. WHEN adapter plugin classes are extended THEN they SHALL use the updated class hierarchy from v3.11.0
28
+ 4. WHEN the code is executed THEN it SHALL not raise any NameError or NoMethodError exceptions
29
+
30
+ ### Requirement 3
31
+
32
+ **User Story:** As a developer using activity_notification with DynamoDB, I want all existing functionality to continue working after the Dynamoid upgrade so that my application remains stable.
33
+
34
+ #### Acceptance Criteria
35
+
36
+ 1. WHEN the none() method is called THEN it SHALL return an empty result set as before
37
+ 2. WHEN the limit() method is called THEN it SHALL properly limit query results
38
+ 3. WHEN the exists?() method is called THEN it SHALL correctly determine if records exist
39
+ 4. WHEN the update_all() method is called THEN it SHALL update all matching records
40
+ 5. WHEN null and not_null operators are used THEN they SHALL filter records correctly
41
+ 6. WHEN uniqueness validation is performed THEN it SHALL prevent duplicate records
42
+
43
+ ### Requirement 4
44
+
45
+ **User Story:** As a developer running tests for activity_notification, I want all existing tests to pass with the new Dynamoid version so that I can be confident the upgrade doesn't break functionality.
46
+
47
+ #### Acceptance Criteria
48
+
49
+ 1. WHEN the test suite is run THEN all Dynamoid-related tests SHALL pass
50
+ 2. WHEN integration tests are executed THEN they SHALL work with the new Dynamoid version
51
+ 3. WHEN edge cases are tested THEN they SHALL behave consistently with the previous version
52
+ 4. WHEN performance tests are run THEN they SHALL show no significant regression
53
+
54
+ ### Requirement 5
55
+
56
+ **User Story:** As a maintainer of activity_notification, I want to contribute useful enhancements back to the Dynamoid upstream project so that the broader community can benefit from the improvements we've developed.
57
+
58
+ #### Acceptance Criteria
59
+
60
+ 1. WHEN extension methods are identified as generally useful THEN they SHALL be prepared for upstream contribution
61
+ 2. WHEN the none() method implementation is stable THEN it SHALL be proposed as a pull request to Dynamoid
62
+ 3. WHEN the limit() method enhancement is validated THEN it SHALL be contributed to Dynamoid upstream
63
+ 4. WHEN the exists?() method is proven useful THEN it SHALL be submitted to Dynamoid for inclusion
64
+ 5. WHEN the update_all() method is optimized THEN it SHALL be offered as a contribution to Dynamoid
65
+ 6. WHEN null/not_null operators are refined THEN they SHALL be proposed for Dynamoid core
66
+ 7. WHEN uniqueness validator improvements are made THEN they SHALL be contributed upstream
67
+
68
+ ### Requirement 6
69
+
70
+ **User Story:** As a developer upgrading activity_notification, I want clear documentation about the Dynamoid version change so that I can understand any potential impacts on my application.
71
+
72
+ #### Acceptance Criteria
73
+
74
+ 1. WHEN the CHANGELOG is updated THEN it SHALL document the Dynamoid version upgrade
75
+ 2. WHEN breaking changes exist THEN they SHALL be clearly documented with migration instructions
76
+ 3. WHEN new features are available THEN they SHALL be documented with usage examples
77
+ 4. WHEN version compatibility is checked THEN the supported Dynamoid versions SHALL be clearly stated
78
+ 5. WHEN upstream contributions are made THEN they SHALL be documented with links to pull requests