mbuzz 0.6.2
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 +7 -0
- data/CHANGELOG.md +92 -0
- data/LICENSE.txt +21 -0
- data/README.md +173 -0
- data/Rakefile +8 -0
- data/lib/mbuzz/api.rb +102 -0
- data/lib/mbuzz/client/conversion_request.rb +85 -0
- data/lib/mbuzz/client/identify_request.rb +37 -0
- data/lib/mbuzz/client/session_request.rb +43 -0
- data/lib/mbuzz/client/track_request.rb +50 -0
- data/lib/mbuzz/client.rb +36 -0
- data/lib/mbuzz/configuration.rb +51 -0
- data/lib/mbuzz/controller_helpers.rb +34 -0
- data/lib/mbuzz/middleware/tracking.rb +157 -0
- data/lib/mbuzz/railtie.rb +13 -0
- data/lib/mbuzz/request_context.rb +40 -0
- data/lib/mbuzz/version.rb +5 -0
- data/lib/mbuzz/visitor/identifier.rb +13 -0
- data/lib/mbuzz.rb +178 -0
- data/lib/specs/old/SPECIFICATION.md +695 -0
- data/lib/specs/old/conversions.md +585 -0
- data/lib/specs/old/event_ids_response.md +346 -0
- data/lib/specs/old/v0.2.0_breaking_changes.md +519 -0
- data/lib/specs/old/v2.0.0_sessions_upgrade.md +265 -0
- data/lib/specs/v0.5.0_four_call_model.md +505 -0
- data/mbuzz-0.6.0.gem +0 -0
- data/sig/mbuzz.rbs +4 -0
- metadata +89 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# Event IDs in API Response - Technical Specification
|
|
2
|
+
|
|
3
|
+
**Version**: 1.1.0
|
|
4
|
+
**Created**: 2025-11-26
|
|
5
|
+
**Status**: Ready for Implementation
|
|
6
|
+
**Breaking Change**: Yes (return type change for `track`)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
Update the Ruby SDK to return event IDs from the API response. This enables users to:
|
|
13
|
+
1. Link events to conversions via the `event_id` parameter
|
|
14
|
+
2. Debug and trace specific events
|
|
15
|
+
3. Reference events in support requests
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Breaking Change
|
|
20
|
+
|
|
21
|
+
### Before (v1.0)
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
result = Mbuzz::Client.track(
|
|
25
|
+
visitor_id: "abc123",
|
|
26
|
+
event_type: "page_view",
|
|
27
|
+
properties: { url: "https://example.com" }
|
|
28
|
+
)
|
|
29
|
+
# => true or false
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### After (v1.1)
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
result = Mbuzz::Client.track(
|
|
36
|
+
visitor_id: "abc123",
|
|
37
|
+
event_type: "page_view",
|
|
38
|
+
properties: { url: "https://example.com" }
|
|
39
|
+
)
|
|
40
|
+
# => { success: true, event_id: "evt_abc123" } or false
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Migration Guide
|
|
44
|
+
|
|
45
|
+
**Existing code** (still works):
|
|
46
|
+
```ruby
|
|
47
|
+
# Boolean check still works because Hash is truthy
|
|
48
|
+
if Mbuzz::Client.track(visitor_id: vid, event_type: "page_view")
|
|
49
|
+
puts "Event tracked"
|
|
50
|
+
end
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**New code** (recommended):
|
|
54
|
+
```ruby
|
|
55
|
+
result = Mbuzz::Client.track(visitor_id: vid, event_type: "page_view")
|
|
56
|
+
if result && result[:success]
|
|
57
|
+
puts "Event ID: #{result[:event_id]}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Or with safe navigation
|
|
61
|
+
if result&.dig(:success)
|
|
62
|
+
event_id = result[:event_id]
|
|
63
|
+
end
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## API Changes
|
|
69
|
+
|
|
70
|
+
### Backend Response Format
|
|
71
|
+
|
|
72
|
+
The Events API now returns event details in the response:
|
|
73
|
+
|
|
74
|
+
**POST /api/v1/events**
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"accepted": 1,
|
|
79
|
+
"rejected": [],
|
|
80
|
+
"events": [
|
|
81
|
+
{
|
|
82
|
+
"id": "evt_abc123def456",
|
|
83
|
+
"event_type": "page_view",
|
|
84
|
+
"visitor_id": "65dabef8d611f332d5bb88f5d6870c733d89f962594575b66f0e1de1ede1ebf0",
|
|
85
|
+
"session_id": "sess_xyz789",
|
|
86
|
+
"status": "accepted"
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Event ID Format
|
|
93
|
+
|
|
94
|
+
- **Prefix**: `evt_`
|
|
95
|
+
- **Entropy**: 128-bit (32 hex characters after prefix)
|
|
96
|
+
- **Example**: `evt_abc123def456789...`
|
|
97
|
+
- **Security**: Account-scoped, not guessable
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Implementation
|
|
102
|
+
|
|
103
|
+
### Mbuzz::Api Changes
|
|
104
|
+
|
|
105
|
+
Add new method to return parsed response:
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
# lib/mbuzz/api.rb
|
|
109
|
+
|
|
110
|
+
def self.post_with_response(path, payload)
|
|
111
|
+
return nil unless enabled_and_configured?
|
|
112
|
+
|
|
113
|
+
response = http_client(path).request(build_request(path, payload))
|
|
114
|
+
return nil unless success?(response)
|
|
115
|
+
|
|
116
|
+
JSON.parse(response.body)
|
|
117
|
+
rescue ConfigurationError, Net::ReadTimeout, Net::OpenTimeout, Net::HTTPError, JSON::ParserError => e
|
|
118
|
+
log_error("#{e.class}: #{e.message}")
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Mbuzz::Client Changes
|
|
124
|
+
|
|
125
|
+
Update `track` to use new API method and return event ID:
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
# lib/mbuzz/client.rb
|
|
129
|
+
|
|
130
|
+
def self.track(user_id: nil, visitor_id: nil, event_type:, properties: {})
|
|
131
|
+
return false unless valid_event_type?(event_type)
|
|
132
|
+
return false unless valid_properties?(properties)
|
|
133
|
+
return false unless valid_identifier?(user_id, visitor_id)
|
|
134
|
+
|
|
135
|
+
event = {
|
|
136
|
+
user_id: user_id,
|
|
137
|
+
visitor_id: visitor_id,
|
|
138
|
+
event_type: event_type,
|
|
139
|
+
properties: properties,
|
|
140
|
+
timestamp: Time.now.utc.iso8601
|
|
141
|
+
}.compact
|
|
142
|
+
|
|
143
|
+
response = Api.post_with_response(EVENTS_PATH, { events: [event] })
|
|
144
|
+
return false unless response
|
|
145
|
+
|
|
146
|
+
event_data = response["events"]&.first
|
|
147
|
+
return false unless event_data
|
|
148
|
+
|
|
149
|
+
{
|
|
150
|
+
success: true,
|
|
151
|
+
event_id: event_data["id"],
|
|
152
|
+
event_type: event_data["event_type"],
|
|
153
|
+
visitor_id: event_data["visitor_id"],
|
|
154
|
+
session_id: event_data["session_id"]
|
|
155
|
+
}
|
|
156
|
+
rescue StandardError => e
|
|
157
|
+
log_error("Track error: #{e.message}")
|
|
158
|
+
false
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
private_class_method def self.log_error(message)
|
|
162
|
+
warn "[mbuzz] #{message}" if Mbuzz.config.debug
|
|
163
|
+
end
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Use Cases
|
|
169
|
+
|
|
170
|
+
### 1. Link Event to Conversion
|
|
171
|
+
|
|
172
|
+
```ruby
|
|
173
|
+
# Track the purchase event
|
|
174
|
+
result = Mbuzz::Client.track(
|
|
175
|
+
visitor_id: mbuzz_visitor_id,
|
|
176
|
+
event_type: "purchase_completed",
|
|
177
|
+
properties: { order_id: order.id, total: order.total }
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Create conversion linked to the event
|
|
181
|
+
if result[:success]
|
|
182
|
+
Mbuzz::Client.conversion(
|
|
183
|
+
visitor_id: mbuzz_visitor_id,
|
|
184
|
+
conversion_type: "purchase",
|
|
185
|
+
revenue: order.total,
|
|
186
|
+
event_id: result[:event_id] # Link to triggering event
|
|
187
|
+
)
|
|
188
|
+
end
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### 2. Debug Event Flow
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
result = Mbuzz::Client.track(
|
|
195
|
+
visitor_id: visitor_id,
|
|
196
|
+
event_type: "signup",
|
|
197
|
+
properties: { plan: "pro" }
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
if result[:success]
|
|
201
|
+
Rails.logger.info "Tracked signup: #{result[:event_id]} for visitor #{result[:visitor_id]}"
|
|
202
|
+
else
|
|
203
|
+
Rails.logger.warn "Failed to track signup for visitor #{visitor_id}"
|
|
204
|
+
end
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 3. Store Event References
|
|
208
|
+
|
|
209
|
+
```ruby
|
|
210
|
+
class Order < ApplicationRecord
|
|
211
|
+
def track_purchase
|
|
212
|
+
result = Mbuzz::Client.track(
|
|
213
|
+
visitor_id: user.mbuzz_visitor_id,
|
|
214
|
+
event_type: "purchase",
|
|
215
|
+
properties: { order_id: id, total: total }
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
update!(mbuzz_event_id: result[:event_id]) if result[:success]
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Testing
|
|
226
|
+
|
|
227
|
+
### Unit Tests
|
|
228
|
+
|
|
229
|
+
```ruby
|
|
230
|
+
class ClientTrackTest < Minitest::Test
|
|
231
|
+
def test_track_returns_event_id_on_success
|
|
232
|
+
stub_successful_events_response
|
|
233
|
+
|
|
234
|
+
result = Mbuzz::Client.track(
|
|
235
|
+
visitor_id: "abc123",
|
|
236
|
+
event_type: "page_view"
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
assert result[:success]
|
|
240
|
+
assert result[:event_id].start_with?("evt_")
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def test_track_returns_false_on_validation_failure
|
|
244
|
+
result = Mbuzz::Client.track(
|
|
245
|
+
visitor_id: nil,
|
|
246
|
+
event_type: "page_view"
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
assert_equal false, result
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def test_track_returns_false_on_api_failure
|
|
253
|
+
stub_failed_events_response
|
|
254
|
+
|
|
255
|
+
result = Mbuzz::Client.track(
|
|
256
|
+
visitor_id: "abc123",
|
|
257
|
+
event_type: "page_view"
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
assert_equal false, result
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def test_track_still_truthy_for_boolean_checks
|
|
264
|
+
stub_successful_events_response
|
|
265
|
+
|
|
266
|
+
result = Mbuzz::Client.track(
|
|
267
|
+
visitor_id: "abc123",
|
|
268
|
+
event_type: "page_view"
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
# Backwards compatibility: result is truthy
|
|
272
|
+
assert result
|
|
273
|
+
if result
|
|
274
|
+
assert true, "Boolean check still works"
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Integration Test
|
|
281
|
+
|
|
282
|
+
```ruby
|
|
283
|
+
def test_track_and_conversion_with_event_id
|
|
284
|
+
visitor_id = "test_#{SecureRandom.hex(8)}"
|
|
285
|
+
|
|
286
|
+
# Track event
|
|
287
|
+
track_result = Mbuzz::Client.track(
|
|
288
|
+
visitor_id: visitor_id,
|
|
289
|
+
event_type: "signup",
|
|
290
|
+
properties: { plan: "pro" }
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
assert track_result[:success]
|
|
294
|
+
assert track_result[:event_id].present?
|
|
295
|
+
|
|
296
|
+
# Create conversion linked to event
|
|
297
|
+
conv_result = Mbuzz::Client.conversion(
|
|
298
|
+
visitor_id: visitor_id,
|
|
299
|
+
conversion_type: "signup",
|
|
300
|
+
event_id: track_result[:event_id]
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
assert conv_result[:success]
|
|
304
|
+
assert conv_result[:conversion_id].present?
|
|
305
|
+
end
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Backwards Compatibility
|
|
311
|
+
|
|
312
|
+
The change is **mostly backwards compatible**:
|
|
313
|
+
|
|
314
|
+
| Pattern | v1.0 | v1.1 | Works? |
|
|
315
|
+
|---------|------|------|--------|
|
|
316
|
+
| `if Mbuzz::Client.track(...)` | `true` | `{ success: true, ... }` | ✅ Yes (Hash is truthy) |
|
|
317
|
+
| `result == true` | `true` | `{ ... }` | ❌ No |
|
|
318
|
+
| `result[:event_id]` | Error | `"evt_..."` | ✅ Yes (new feature) |
|
|
319
|
+
| `!result` | `false` | `false` | ✅ Yes |
|
|
320
|
+
|
|
321
|
+
**Recommendation**: Update code to check `result[:success]` instead of `result == true`.
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Version Bump
|
|
326
|
+
|
|
327
|
+
This is a minor version bump (1.0 → 1.1) because:
|
|
328
|
+
- Return type change could break `result == true` checks
|
|
329
|
+
- New functionality added (event IDs)
|
|
330
|
+
- Existing boolean checks still work (Hash is truthy)
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Success Criteria
|
|
335
|
+
|
|
336
|
+
- [ ] `Api.post_with_response` method implemented
|
|
337
|
+
- [ ] `Client.track` returns hash with event_id on success
|
|
338
|
+
- [ ] `Client.track` returns `false` on failure (unchanged)
|
|
339
|
+
- [ ] Boolean checks still work (backwards compatible)
|
|
340
|
+
- [ ] Unit tests cover all cases
|
|
341
|
+
- [ ] Integration test validates event_id linking to conversion
|
|
342
|
+
- [ ] CHANGELOG updated with breaking change note
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
Built for mbuzz.co
|