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,265 @@
|
|
|
1
|
+
# mbuzz-ruby v2.0.0 Upgrade: Sessions Endpoint
|
|
2
|
+
|
|
3
|
+
**Created**: 2025-11-28
|
|
4
|
+
**Status**: Required for full SDK compliance
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Summary
|
|
9
|
+
|
|
10
|
+
The Multibuzz backend now has a dedicated `POST /api/v1/sessions` endpoint for creating sessions with full acquisition context (UTM parameters, referrer, channel). The SDK must be updated to call this endpoint on new session creation.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Current State
|
|
15
|
+
|
|
16
|
+
The current SDK (v0.x/v1.x):
|
|
17
|
+
- Generates visitor_id and session_id cookies
|
|
18
|
+
- Does NOT explicitly create sessions on the backend
|
|
19
|
+
- Sessions are created implicitly when events are tracked
|
|
20
|
+
|
|
21
|
+
## Required Changes
|
|
22
|
+
|
|
23
|
+
### 1. Add Session Creation on New Session
|
|
24
|
+
|
|
25
|
+
When the middleware detects a new session (no session cookie or expired), it must:
|
|
26
|
+
|
|
27
|
+
1. Generate new session_id
|
|
28
|
+
2. POST to `/api/v1/sessions` with full context
|
|
29
|
+
3. Store session_id in cookie
|
|
30
|
+
|
|
31
|
+
**New Endpoint:**
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
POST /api/v1/sessions
|
|
35
|
+
|
|
36
|
+
{
|
|
37
|
+
"session": {
|
|
38
|
+
"visitor_id": "64-char-hex-string",
|
|
39
|
+
"session_id": "64-char-hex-string",
|
|
40
|
+
"url": "https://example.com/landing?utm_source=google&utm_medium=cpc",
|
|
41
|
+
"referrer": "https://www.google.com/search",
|
|
42
|
+
"started_at": "2025-11-28T10:30:00Z"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Response (202 Accepted):**
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"status": "accepted",
|
|
52
|
+
"visitor_id": "abc123...",
|
|
53
|
+
"session_id": "xyz789...",
|
|
54
|
+
"channel": "paid_search"
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 2. New Files Required
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
lib/mbuzz/client/session_request.rb # New request class
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 3. Update Middleware
|
|
65
|
+
|
|
66
|
+
Update `lib/mbuzz/middleware/tracking.rb`:
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
# Add session constant
|
|
70
|
+
SESSION_COOKIE_NAME = "_mbuzz_sid"
|
|
71
|
+
SESSION_COOKIE_MAX_AGE = 30 * 60 # 30 minutes
|
|
72
|
+
|
|
73
|
+
def call(env)
|
|
74
|
+
@request = Rack::Request.new(env)
|
|
75
|
+
|
|
76
|
+
env[ENV_VISITOR_ID_KEY] = visitor_id
|
|
77
|
+
env[ENV_SESSION_ID_KEY] = session_id # NEW
|
|
78
|
+
|
|
79
|
+
RequestContext.with_context(request: request) do
|
|
80
|
+
create_session_if_new # NEW - fire and forget
|
|
81
|
+
|
|
82
|
+
status, headers, body = app.call(env)
|
|
83
|
+
set_visitor_cookie(headers)
|
|
84
|
+
set_session_cookie(headers) # NEW
|
|
85
|
+
[status, headers, body]
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
def session_id
|
|
92
|
+
@session_id ||= session_id_from_cookie || generate_session_id
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def session_id_from_cookie
|
|
96
|
+
request.cookies[SESSION_COOKIE_NAME]
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def generate_session_id
|
|
100
|
+
SecureRandom.hex(32)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def new_session?
|
|
104
|
+
session_id_from_cookie.nil?
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def create_session_if_new
|
|
108
|
+
return unless new_session?
|
|
109
|
+
|
|
110
|
+
# Fire and forget - don't block the request
|
|
111
|
+
Thread.new do
|
|
112
|
+
Client.session(
|
|
113
|
+
visitor_id: visitor_id,
|
|
114
|
+
session_id: session_id,
|
|
115
|
+
url: request.url,
|
|
116
|
+
referrer: request.referer
|
|
117
|
+
)
|
|
118
|
+
rescue => e
|
|
119
|
+
# Log but don't raise - session creation is non-critical
|
|
120
|
+
Mbuzz.config.logger&.error("Session creation failed: #{e.message}")
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def set_session_cookie(headers)
|
|
125
|
+
cookie_options = {
|
|
126
|
+
value: session_id,
|
|
127
|
+
path: VISITOR_COOKIE_PATH,
|
|
128
|
+
max_age: SESSION_COOKIE_MAX_AGE,
|
|
129
|
+
httponly: true,
|
|
130
|
+
same_site: VISITOR_COOKIE_SAME_SITE
|
|
131
|
+
}
|
|
132
|
+
cookie_options[:secure] = true if request.ssl?
|
|
133
|
+
|
|
134
|
+
Rack::Utils.set_cookie_header!(headers, SESSION_COOKIE_NAME, cookie_options)
|
|
135
|
+
end
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 4. Add Client Method
|
|
139
|
+
|
|
140
|
+
Update `lib/mbuzz/client.rb`:
|
|
141
|
+
|
|
142
|
+
```ruby
|
|
143
|
+
def self.session(visitor_id:, session_id:, url:, referrer: nil, started_at: nil)
|
|
144
|
+
SessionRequest.new(visitor_id, session_id, url, referrer, started_at).call
|
|
145
|
+
end
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 5. Create SessionRequest Class
|
|
149
|
+
|
|
150
|
+
Create `lib/mbuzz/client/session_request.rb`:
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
# frozen_string_literal: true
|
|
154
|
+
|
|
155
|
+
module Mbuzz
|
|
156
|
+
class Client
|
|
157
|
+
class SessionRequest
|
|
158
|
+
def initialize(visitor_id, session_id, url, referrer, started_at)
|
|
159
|
+
@visitor_id = visitor_id
|
|
160
|
+
@session_id = session_id
|
|
161
|
+
@url = url
|
|
162
|
+
@referrer = referrer
|
|
163
|
+
@started_at = started_at || Time.now.utc.iso8601
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def call
|
|
167
|
+
return false unless valid?
|
|
168
|
+
|
|
169
|
+
Api.post(SESSIONS_PATH, payload)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
private
|
|
173
|
+
|
|
174
|
+
attr_reader :visitor_id, :session_id, :url, :referrer, :started_at
|
|
175
|
+
|
|
176
|
+
def valid?
|
|
177
|
+
visitor_id.present? && session_id.present? && url.present?
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def payload
|
|
181
|
+
{
|
|
182
|
+
session: {
|
|
183
|
+
visitor_id: visitor_id,
|
|
184
|
+
session_id: session_id,
|
|
185
|
+
url: url,
|
|
186
|
+
referrer: referrer,
|
|
187
|
+
started_at: started_at
|
|
188
|
+
}.compact
|
|
189
|
+
}
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### 6. Add Constant
|
|
197
|
+
|
|
198
|
+
Update `lib/mbuzz.rb`:
|
|
199
|
+
|
|
200
|
+
```ruby
|
|
201
|
+
SESSIONS_PATH = "/sessions"
|
|
202
|
+
SESSION_COOKIE_NAME = "_mbuzz_sid"
|
|
203
|
+
SESSION_COOKIE_MAX_AGE = 30 * 60 # 30 minutes
|
|
204
|
+
|
|
205
|
+
ENV_SESSION_ID_KEY = "mbuzz.session_id"
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 7. Add Helper Method
|
|
209
|
+
|
|
210
|
+
Update `lib/mbuzz/controller_helpers.rb`:
|
|
211
|
+
|
|
212
|
+
```ruby
|
|
213
|
+
def mbuzz_session_id
|
|
214
|
+
request.env[Mbuzz::ENV_SESSION_ID_KEY]
|
|
215
|
+
end
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Benefits
|
|
221
|
+
|
|
222
|
+
1. **Accurate Attribution**: Sessions are created with full acquisition context (UTMs, referrer, channel) at session start, not when first event is tracked
|
|
223
|
+
2. **Channel Attribution**: Backend determines channel immediately from URL/referrer
|
|
224
|
+
3. **Consistent Data**: Session data matches the landing page, not a later page view
|
|
225
|
+
4. **Decoupled from Events**: Sessions exist even if no events are tracked
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Migration Path
|
|
230
|
+
|
|
231
|
+
### Breaking Changes
|
|
232
|
+
|
|
233
|
+
None - this is additive. Existing events endpoint still creates sessions implicitly.
|
|
234
|
+
|
|
235
|
+
### Recommended Upgrade
|
|
236
|
+
|
|
237
|
+
1. Add session creation to middleware
|
|
238
|
+
2. Add session cookie management
|
|
239
|
+
3. Add `mbuzz_session_id` helper
|
|
240
|
+
4. Bump version to 2.0.0
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Testing
|
|
245
|
+
|
|
246
|
+
Add tests for:
|
|
247
|
+
|
|
248
|
+
1. Session cookie is created on new visit
|
|
249
|
+
2. Session cookie is reused within 30 minutes
|
|
250
|
+
3. New session is created after 30 minute gap
|
|
251
|
+
4. `POST /api/v1/sessions` is called on new session
|
|
252
|
+
5. Session creation failures don't break the request
|
|
253
|
+
6. `mbuzz_session_id` helper returns correct value
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## API Contract
|
|
258
|
+
|
|
259
|
+
See: `multibuzz/lib/docs/sdk/api_contract.md` for full endpoint documentation.
|
|
260
|
+
|
|
261
|
+
Key points:
|
|
262
|
+
- Sessions endpoint requires: `visitor_id`, `session_id`, `url`
|
|
263
|
+
- Optional: `referrer`, `started_at`
|
|
264
|
+
- Returns: `status`, `visitor_id`, `session_id`, `channel`
|
|
265
|
+
- Backend extracts UTMs from URL, determines channel from UTM/referrer
|