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.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +7 -39
- data/CHANGELOG.md +18 -0
- data/Gemfile +1 -3
- data/README.md +9 -1
- data/activity_notification.gemspec +5 -5
- data/ai-curated-specs/issues/172/design.md +220 -0
- data/ai-curated-specs/issues/172/tasks.md +326 -0
- data/ai-curated-specs/issues/188/design.md +227 -0
- data/ai-curated-specs/issues/188/requirements.md +78 -0
- data/ai-curated-specs/issues/188/tasks.md +203 -0
- data/ai-curated-specs/issues/188/upstream-contributions.md +592 -0
- data/ai-curated-specs/issues/50/design.md +235 -0
- data/ai-curated-specs/issues/50/requirements.md +49 -0
- data/ai-curated-specs/issues/50/tasks.md +232 -0
- data/app/controllers/activity_notification/notifications_api_controller.rb +22 -0
- data/app/controllers/activity_notification/notifications_controller.rb +27 -1
- data/app/mailers/activity_notification/mailer.rb +2 -2
- data/app/views/activity_notification/notifications/default/_index.html.erb +6 -1
- data/app/views/activity_notification/notifications/default/destroy_all.js.erb +6 -0
- data/docs/Setup.md +43 -6
- data/gemfiles/Gemfile.rails-7.0 +2 -0
- data/gemfiles/Gemfile.rails-8.0 +0 -2
- data/lib/activity_notification/apis/notification_api.rb +51 -2
- data/lib/activity_notification/controllers/concerns/swagger/notifications_api.rb +59 -0
- data/lib/activity_notification/helpers/view_helpers.rb +28 -0
- data/lib/activity_notification/mailers/helpers.rb +14 -7
- data/lib/activity_notification/models/concerns/target.rb +16 -0
- data/lib/activity_notification/models.rb +1 -1
- data/lib/activity_notification/notification_resilience.rb +115 -0
- data/lib/activity_notification/orm/dynamoid/extension.rb +4 -87
- data/lib/activity_notification/orm/dynamoid/notification.rb +19 -2
- data/lib/activity_notification/orm/dynamoid.rb +42 -6
- data/lib/activity_notification/rails/routes.rb +1 -0
- data/lib/activity_notification/version.rb +1 -1
- data/lib/activity_notification.rb +1 -0
- data/lib/generators/templates/controllers/notifications_api_controller.rb +5 -0
- data/lib/generators/templates/controllers/notifications_api_with_devise_controller.rb +5 -0
- data/lib/generators/templates/controllers/notifications_controller.rb +5 -0
- data/lib/generators/templates/controllers/notifications_with_devise_controller.rb +5 -0
- data/spec/concerns/apis/notification_api_spec.rb +161 -5
- data/spec/concerns/models/target_spec.rb +7 -0
- data/spec/controllers/controller_spec_utility.rb +1 -1
- data/spec/controllers/notifications_api_controller_shared_examples.rb +113 -0
- data/spec/controllers/notifications_controller_shared_examples.rb +150 -0
- data/spec/helpers/view_helpers_spec.rb +14 -0
- data/spec/jobs/notification_resilience_job_spec.rb +167 -0
- data/spec/mailers/notification_resilience_spec.rb +263 -0
- data/spec/models/notification_spec.rb +1 -1
- data/spec/models/subscription_spec.rb +1 -1
- data/spec/rails_app/app/helpers/devise_helper.rb +2 -0
- data/spec/rails_app/config/application.rb +1 -0
- data/spec/rails_app/config/initializers/zeitwerk.rb +10 -0
- 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
|