flow_chat 0.7.0 → 0.8.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1d5c2d35096c65d8953ed19fe427a61b13c7b2fabab871cc99562bde861576be
4
- data.tar.gz: ee342523ae86be4397bbb0586a2a38966d473094a1d33aacb047fe26edf93c78
3
+ metadata.gz: abe12ed32427797b8d7c5dc79766bae76113ec62df415e495f900e4216af5c3f
4
+ data.tar.gz: d344431fbde29789a0013a78dd099b253b65dc9e8af90ef2d403a5592b5bb7aa
5
5
  SHA512:
6
- metadata.gz: bae27cb42464181afbc3751082344d9aaa6c8d4ae6915794aa5671fec8622eb24f2d694d14a3414c11f3125c954a022a781ed91eaf4705c0314b240920434eac
7
- data.tar.gz: 50fd0405af1d6e1e79ef62be8bb2e6bca81ed0d46297bf7b1ed5bbd04f0d892b30261fde9c547a523bd9003ad8612efee3aadb11f683af4af0fded396c6a2538
6
+ metadata.gz: b05a61d0e7a41db5ecd2137aaa193c7431f1e16277e093af4eacbcf657696aa42a22d177407718618959b021298a3583101004534d3666a51b2607b4f621db88
7
+ data.tar.gz: a9aab01a2754996205c2b9c1fa8143cdf503f4033c243ed537120c975249eebc0b0cc42f310864a8923b8fd819cc625205cd32463f15e5db93ccd88c2c4ecfe1
data/README.md CHANGED
@@ -186,6 +186,7 @@ See the [Testing Guide](docs/testing.md) for complete setup instructions and tes
186
186
  - **[WhatsApp Setup](docs/whatsapp-setup.md)** - Comprehensive WhatsApp configuration
187
187
  - **[USSD Setup](docs/ussd-setup.md)** - USSD gateway configuration and examples
188
188
  - **[Flow Development](docs/flows.md)** - Advanced flow patterns and techniques
189
+ - **[Session Management](docs/sessions.md)** - Session architecture and configuration
189
190
  - **[Media Support](docs/media.md)** - Rich media handling for WhatsApp
190
191
  - **[Testing Guide](docs/testing.md)** - Complete testing strategies
191
192
  - **[Configuration Reference](docs/configuration.md)** - All configuration options
@@ -19,6 +19,26 @@ FlowChat::Config.combine_validation_error_with_message = true # default
19
19
  FlowChat.setup_instrumentation!
20
20
  ```
21
21
 
22
+ ## Session Configuration
23
+
24
+ ```ruby
25
+ # Session boundaries control how session IDs are constructed
26
+ FlowChat::Config.session.boundaries = [:flow, :platform] # default
27
+ FlowChat::Config.session.hash_phone_numbers = true # hash phone numbers for privacy
28
+ FlowChat::Config.session.identifier = nil # let platforms choose (default)
29
+
30
+ # Available boundary options:
31
+ # :flow - separate sessions per flow class
32
+ # :platform - separate sessions per platform (ussd, whatsapp)
33
+ # :gateway - separate sessions per gateway
34
+ # [] - global sessions (no boundaries)
35
+
36
+ # Available identifier options:
37
+ # nil - platform chooses default (:request_id for USSD, :msisdn for WhatsApp)
38
+ # :msisdn - use phone number (durable sessions)
39
+ # :request_id - use request ID (ephemeral sessions)
40
+ ```
41
+
22
42
  ## USSD Configuration
23
43
 
24
44
  ```ruby
@@ -28,10 +48,6 @@ FlowChat::Config.ussd.pagination_next_option = "#" # option to go to next
28
48
  FlowChat::Config.ussd.pagination_next_text = "More" # text for next option
29
49
  FlowChat::Config.ussd.pagination_back_option = "0" # option to go back
30
50
  FlowChat::Config.ussd.pagination_back_text = "Back" # text for back option
31
-
32
- # Resumable sessions
33
- FlowChat::Config.ussd.resumable_sessions_enabled = true # default
34
- FlowChat::Config.ussd.resumable_sessions_timeout_seconds = 300 # 5 minutes
35
51
  ```
36
52
 
37
53
  ## WhatsApp Configuration
@@ -174,8 +190,15 @@ processor = FlowChat::Ussd::Processor.new(self) do |config|
174
190
  # Optional middleware
175
191
  config.use_middleware MyCustomMiddleware
176
192
 
177
- # Optional resumable sessions
178
- config.use_resumable_sessions
193
+ # Configure session boundaries
194
+ config.use_session_config(
195
+ boundaries: [:flow, :platform], # which boundaries to enforce
196
+ hash_phone_numbers: true, # hash phone numbers for privacy
197
+ identifier: :msisdn # use MSISDN for durable sessions (optional)
198
+ )
199
+
200
+ # Shorthand for durable sessions (identifier: :msisdn)
201
+ config.use_durable_sessions
179
202
  end
180
203
  ```
181
204
 
data/docs/sessions.md ADDED
@@ -0,0 +1,433 @@
1
+ # Session Management
2
+
3
+ FlowChat provides a powerful and flexible session management system that enables persistent conversational state across multiple requests. This document covers the architecture, configuration, and best practices for session management.
4
+
5
+ ## Overview
6
+
7
+ Sessions in FlowChat store user conversation state between requests, enabling:
8
+
9
+ - **Multi-step workflows** - Collect information across multiple prompts
10
+ - **Context preservation** - Remember user inputs and conversation history
11
+ - **Cross-platform consistency** - Same session behavior across USSD and WhatsApp
12
+ - **Privacy protection** - Automatic phone number hashing for security
13
+ - **Flexible isolation** - Control session boundaries per deployment needs
14
+
15
+ ## Architecture
16
+
17
+ The session system is built around three core components:
18
+
19
+ ### 1. Session Configuration (`FlowChat::Config::SessionConfig`)
20
+
21
+ Controls how session IDs are generated and sessions are isolated:
22
+
23
+ ```ruby
24
+ # Global session configuration
25
+ FlowChat::Config.session.boundaries = [:flow, :gateway, :platform] # isolation boundaries
26
+ FlowChat::Config.session.hash_phone_numbers = true # privacy protection
27
+ FlowChat::Config.session.identifier = nil # platform chooses default
28
+ ```
29
+
30
+ ### 2. Session Middleware (`FlowChat::Session::Middleware`)
31
+
32
+ Automatically manages session creation and ID generation based on configuration:
33
+
34
+ - Generates consistent session IDs based on boundaries and identifiers
35
+ - Creates session store instances for each request
36
+ - Handles platform-specific defaults (USSD = ephemeral, WhatsApp = durable)
37
+ - Provides instrumentation events for monitoring
38
+
39
+ ### 3. Session Stores
40
+
41
+ Store actual session data with different persistence strategies:
42
+
43
+ - **`FlowChat::Session::CacheSessionStore`** - Uses Rails cache (recommended)
44
+ - **`FlowChat::Session::RailsSessionStore`** - Uses Rails session (limited)
45
+
46
+ ## Session Boundaries
47
+
48
+ Boundaries control how session IDs are constructed, determining when sessions are shared vs. isolated:
49
+
50
+ ### Available Boundaries
51
+
52
+ - **`:flow`** - Separate sessions per flow class
53
+ - **`:platform`** - Separate sessions per platform (ussd, whatsapp)
54
+ - **`:gateway`** - Separate sessions per gateway
55
+ - **`:url`** - Separate sessions per request URL (host + path)
56
+ - **`[]`** - Global sessions (no boundaries)
57
+
58
+ ### Examples
59
+
60
+ ```ruby
61
+ # Default: Full isolation
62
+ FlowChat::Config.session.boundaries = [:flow, :gateway, :platform]
63
+ # Session ID: "registration_flow:nalo:ussd:abc123"
64
+
65
+ # Flow isolation only
66
+ FlowChat::Config.session.boundaries = [:flow]
67
+ # Session ID: "registration_flow:abc123"
68
+
69
+ # Platform isolation only
70
+ FlowChat::Config.session.boundaries = [:platform]
71
+ # Session ID: "ussd:abc123"
72
+
73
+ # Global sessions
74
+ FlowChat::Config.session.boundaries = []
75
+ # Session ID: "abc123"
76
+ ```
77
+
78
+ ## Session Identifiers
79
+
80
+ Identifiers determine what makes a session unique:
81
+
82
+ ### Available Identifiers
83
+
84
+ - **`nil`** - Platform chooses default (recommended)
85
+ - USSD: `:request_id` (ephemeral sessions)
86
+ - WhatsApp: `:msisdn` (durable sessions)
87
+ - **`:msisdn`** - Use phone number (durable sessions)
88
+ - **`:request_id`** - Use request ID (ephemeral sessions)
89
+
90
+ ### Platform Defaults
91
+
92
+ ```ruby
93
+ # USSD: ephemeral sessions by default
94
+ identifier: :request_id
95
+ # New session each time USSD times out
96
+
97
+ # WhatsApp: durable sessions by default
98
+ identifier: :msisdn
99
+ # Same session resumes across conversations
100
+ ```
101
+
102
+ ### Phone Number Hashing
103
+
104
+ When using `:msisdn` identifier, phone numbers are automatically hashed for privacy:
105
+
106
+ ```ruby
107
+ FlowChat::Config.session.hash_phone_numbers = true # default
108
+ # "+256700123456" becomes "a1b2c3d4" (8-character hash)
109
+
110
+ FlowChat::Config.session.hash_phone_numbers = false
111
+ # "+256700123456" used directly (not recommended for production)
112
+ ```
113
+
114
+ ## Configuration Examples
115
+
116
+ ### Basic USSD Configuration
117
+
118
+ ```ruby
119
+ processor = FlowChat::Ussd::Processor.new(self) do |config|
120
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
121
+ config.use_session_store FlowChat::Session::CacheSessionStore
122
+
123
+ # Use shorthand for standard durable sessions
124
+ config.use_durable_sessions
125
+ end
126
+ ```
127
+
128
+ ### Custom Session Configuration
129
+
130
+ ```ruby
131
+ processor = FlowChat::Ussd::Processor.new(self) do |config|
132
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
133
+ config.use_session_store FlowChat::Session::CacheSessionStore
134
+
135
+ # Explicit session configuration
136
+ config.use_session_config(
137
+ boundaries: [:flow, :platform], # isolate by flow and platform
138
+ hash_phone_numbers: true, # hash phone numbers for privacy
139
+ identifier: :msisdn # use phone number for durable sessions
140
+ )
141
+ end
142
+ ```
143
+
144
+ ### Cross-gateway Sessions
145
+
146
+ ```ruby
147
+ processor = FlowChat::Ussd::Processor.new(self) do |config|
148
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
149
+ config.use_session_store FlowChat::Session::CacheSessionStore
150
+
151
+ # Allow sessions to work across different gateways
152
+ config.use_session_config(boundaries: [:flow, :platform])
153
+ end
154
+ ```
155
+
156
+ ### Cross-Platform Sessions
157
+
158
+ ```ruby
159
+ processor = FlowChat::Ussd::Processor.new(self) do |config|
160
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
161
+ config.use_session_store FlowChat::Session::CacheSessionStore
162
+
163
+ # Allow same user to continue on USSD or WhatsApp
164
+ config.use_cross_platform_sessions
165
+ # Equivalent to: boundaries: [:flow]
166
+ end
167
+ ```
168
+
169
+ ### URL-Based Session Isolation
170
+
171
+ Perfect for multi-tenant applications:
172
+
173
+ ```ruby
174
+ processor = FlowChat::Ussd::Processor.new(self) do |config|
175
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
176
+ config.use_session_store FlowChat::Session::CacheSessionStore
177
+
178
+ # Isolate sessions by URL (great for multi-tenant SaaS)
179
+ config.use_url_isolation
180
+ # Adds :url to existing boundaries
181
+ end
182
+ ```
183
+
184
+ **URL Boundary Examples:**
185
+ - `tenant1.example.com/ussd` vs `tenant2.example.com/ussd` - Different sessions
186
+ - `api.example.com/v1/ussd` vs `api.example.com/v2/ussd` - Different sessions
187
+ - `dev.example.com/ussd` vs `prod.example.com/ussd` - Different sessions
188
+
189
+ **URL Processing:**
190
+ - Combines host + path: `example.com/api/v1/ussd`
191
+ - Sanitizes special characters: `tenant-1.com/ussd` → `tenant_1.com/ussd`
192
+ - Hashes long URLs (>50 chars): `verylongdomain.../path` → `url_a1b2c3d4`
193
+
194
+ ### Global Sessions
195
+
196
+ ```ruby
197
+ processor = FlowChat::Ussd::Processor.new(self) do |config|
198
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
199
+ config.use_session_store FlowChat::Session::CacheSessionStore
200
+
201
+ # Single session shared across everything
202
+ config.use_session_config(boundaries: [])
203
+ end
204
+ ```
205
+
206
+ ## Session Stores
207
+
208
+ ### Cache Session Store (Recommended)
209
+
210
+ Uses Rails cache backend with automatic TTL management:
211
+
212
+ ```ruby
213
+ config.use_session_store FlowChat::Session::CacheSessionStore
214
+
215
+ # Requires Rails cache to be configured
216
+ # config/environments/production.rb
217
+ config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'] }
218
+ ```
219
+
220
+ **Features:**
221
+ - Automatic session expiration via cache TTL
222
+ - Redis/Memcached support for distributed deployments
223
+ - High performance
224
+ - Memory efficient
225
+
226
+ ### Rails Session Store
227
+
228
+ Uses Rails session for storage (limited scope):
229
+
230
+ ```ruby
231
+ config.use_session_store FlowChat::Session::RailsSessionStore
232
+ ```
233
+
234
+ **Limitations:**
235
+ - Tied to HTTP session lifecycle
236
+ - Limited storage capacity
237
+ - Not suitable for long-running conversations
238
+
239
+ ## Session Data Usage
240
+
241
+ ### In Flows
242
+
243
+ Sessions are automatically available in flows:
244
+
245
+ ```ruby
246
+ class RegistrationFlow < FlowChat::Flow
247
+ def main_page
248
+ # Store data
249
+ app.session.set("step", "registration")
250
+ app.session.set("user_data", {name: "John", age: 25})
251
+
252
+ # Retrieve data
253
+ step = app.session.get("step")
254
+ user_data = app.session.get("user_data")
255
+
256
+ # Check existence
257
+ if app.session.get("completed")
258
+ app.say "Registration already completed!"
259
+ return
260
+ end
261
+
262
+ # Continue flow...
263
+ name = app.screen(:name) { |prompt| prompt.ask "Name?" }
264
+ app.session.set("name", name)
265
+ end
266
+ end
267
+ ```
268
+
269
+ ### Session Store API
270
+
271
+ All session stores implement a consistent interface:
272
+
273
+ ```ruby
274
+ # Basic operations
275
+ session.set(key, value) # Store data
276
+ value = session.get(key) # Retrieve data
277
+ session.delete(key) # Delete specific key
278
+ session.clear # Clear all session data
279
+ session.destroy # Destroy entire session
280
+
281
+ # Utility methods
282
+ session.exists? # Check if session has any data
283
+ ```
284
+
285
+ ## Best Practices
286
+
287
+ ### 1. Choose Appropriate Boundaries
288
+
289
+ ```ruby
290
+ # High-traffic public services
291
+ boundaries: [:flow, :gateway, :platform] # Full isolation
292
+
293
+ # Single-tenant applications
294
+ boundaries: [:flow] # Simpler, allows cross-platform
295
+
296
+ # Global state services (rare)
297
+ boundaries: [] # Shared state across everything
298
+ ```
299
+
300
+ ### 2. Consider Session Lifecycle
301
+
302
+ ```ruby
303
+ # USSD: Short sessions, frequent timeouts
304
+ identifier: :request_id # New session each timeout (default)
305
+
306
+ # WhatsApp: Long conversations, persistent
307
+ identifier: :msisdn # Resume across days/weeks (default)
308
+
309
+ # Custom requirements
310
+ identifier: :msisdn # Make USSD durable
311
+ identifier: :request_id # Make WhatsApp ephemeral
312
+ ```
313
+
314
+ ### 3. Handle Session Expiration
315
+
316
+ ```ruby
317
+ class RegistrationFlow < FlowChat::Flow
318
+ def main_page
319
+ # Check if session expired
320
+ if app.session.get("user_id").nil?
321
+ restart_registration
322
+ return
323
+ end
324
+
325
+ # Continue existing session
326
+ continue_registration
327
+ end
328
+
329
+ private
330
+
331
+ def restart_registration
332
+ app.say "Session expired. Let's start over."
333
+ # Reset flow to beginning
334
+ end
335
+ end
336
+ ```
337
+
338
+ ### 4. Optimize Session Data
339
+
340
+ ```ruby
341
+ # Store only necessary data
342
+ app.session.set("user_id", 123) # Good: minimal data
343
+ app.session.set("user", user_object) # Avoid: large objects
344
+
345
+ # Clean up when done
346
+ def complete_registration
347
+ app.session.set("completed", true)
348
+ app.session.delete("temp_data") # Clean up temporary data
349
+ end
350
+ ```
351
+
352
+ ### 5. Security Considerations
353
+
354
+ ```ruby
355
+ # Always hash phone numbers in production
356
+ FlowChat::Config.session.hash_phone_numbers = true
357
+
358
+ # Use secure cache backends
359
+ config.cache_store = :redis_cache_store, {
360
+ url: ENV['REDIS_URL'],
361
+ ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }
362
+ }
363
+
364
+ # Set appropriate TTLs
365
+ cache_options = { expires_in: 30.minutes } # Reasonable session timeout
366
+ ```
367
+
368
+ ## Troubleshooting
369
+
370
+ ### Session Not Persisting
371
+
372
+ 1. **Check session store configuration:**
373
+ ```ruby
374
+ # Ensure session store is configured
375
+ config.use_session_store FlowChat::Session::CacheSessionStore
376
+ ```
377
+
378
+ 2. **Verify cache backend:**
379
+ ```ruby
380
+ # Test cache is working
381
+ Rails.cache.write("test", "value")
382
+ puts Rails.cache.read("test") # Should output "value"
383
+ ```
384
+
385
+ 3. **Check session boundaries:**
386
+ ```ruby
387
+ # Debug session ID generation
388
+ FlowChat.logger.level = Logger::DEBUG
389
+ # Look for "Session::Middleware: Generated session ID: ..." messages
390
+ ```
391
+
392
+ ### Different Session IDs
393
+
394
+ 1. **Inconsistent request data:**
395
+ - Verify `request.gateway` is consistent
396
+ - Check `request.platform` is set correctly
397
+ - Ensure `request.msisdn` format is consistent
398
+
399
+ 2. **Boundary configuration mismatch:**
400
+ ```ruby
401
+ # Ensure same boundaries across requests
402
+ config.use_session_config(boundaries: [:flow, :platform])
403
+ ```
404
+
405
+ ### Session Data Lost
406
+
407
+ 1. **Cache expiration:**
408
+ ```ruby
409
+ # Increase TTL if needed
410
+ FlowChat::Config.cache = Rails.cache
411
+ # Configure longer expiration in cache store
412
+ ```
413
+
414
+ 2. **Session ID changes:**
415
+ - Check logs for "Generated session ID" messages
416
+ - Verify identifier consistency (`:msisdn` vs `:request_id`)
417
+
418
+ ## Monitoring and Instrumentation
419
+
420
+ FlowChat emits events for session operations:
421
+
422
+ ```ruby
423
+ # Subscribe to session events
424
+ ActiveSupport::Notifications.subscribe("session.created.flow_chat") do |event|
425
+ Rails.logger.info "Session created: #{event.payload[:session_id]}"
426
+ end
427
+
428
+ # Monitor session usage
429
+ ActiveSupport::Notifications.subscribe(/session\..*\.flow_chat/) do |name, start, finish, id, payload|
430
+ # Track session operations for analytics
431
+ Analytics.track("flowchat.session.#{name.split('.')[1]}", payload)
432
+ end
433
+ ```
data/docs/testing.md CHANGED
@@ -43,7 +43,7 @@ class SimulatorController < ApplicationController
43
43
  name: "USSD Integration",
44
44
  icon: "📱",
45
45
  processor_type: "ussd",
46
- provider: "nalo",
46
+ gateway: "nalo",
47
47
  endpoint: "/ussd",
48
48
  color: "#007bff"
49
49
  },
@@ -51,7 +51,7 @@ class SimulatorController < ApplicationController
51
51
  name: "WhatsApp Integration",
52
52
  icon: "💬",
53
53
  processor_type: "whatsapp",
54
- provider: "cloud_api",
54
+ gateway: "cloud_api",
55
55
  endpoint: "/whatsapp/webhook",
56
56
  color: "#25D366"
57
57
  }
data/docs/ussd-setup.md CHANGED
@@ -119,19 +119,35 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor i
119
119
  0 Back
120
120
  ```
121
121
 
122
- ## Resumable Sessions
122
+ ## Session Management
123
123
 
124
- Enable resumable sessions for better user experience:
124
+ Configure session behavior for better user experience:
125
125
 
126
126
  ```ruby
127
127
  processor = FlowChat::Ussd::Processor.new(self) do |config|
128
128
  config.use_gateway FlowChat::Ussd::Gateway::Nalo
129
129
  config.use_session_store FlowChat::Session::CacheSessionStore
130
- config.use_resumable_sessions # Enable resumable sessions
130
+
131
+ # Enable durable sessions (shorthand)
132
+ config.use_durable_sessions
131
133
  end
132
134
  ```
133
135
 
134
- Users can continue interrupted conversations within the timeout period.
136
+ ### Session Boundaries
137
+
138
+ Session boundaries control how session IDs are constructed:
139
+
140
+ - **`:flow`** - Separate sessions per flow class
141
+ - **`:platform`** - Separate USSD from WhatsApp sessions
142
+ - **`:gateway`** - Separate sessions per gateway
143
+ - **`[]`** - Global sessions (no boundaries)
144
+
145
+ Session identifier options:
146
+
147
+ - **`nil`** - Platform chooses default (`:request_id` for USSD, `:msisdn` for WhatsApp)
148
+ - **`:msisdn`** - Use phone number (durable sessions)
149
+ - **`:request_id`** - Use request ID (ephemeral sessions)
150
+ - **`hash_phone_numbers`** - Hash phone numbers for privacy (recommended)
135
151
 
136
152
  ## Middleware
137
153
 
@@ -17,7 +17,7 @@ class SimulatorController < ApplicationController
17
17
  name: "Main USSD Endpoint",
18
18
  description: "Primary USSD integration",
19
19
  processor_type: "ussd",
20
- provider: "nalo",
20
+ gateway: "nalo",
21
21
  endpoint: "/ussd",
22
22
  icon: "📱",
23
23
  color: "#28a745"
@@ -26,7 +26,7 @@ class SimulatorController < ApplicationController
26
26
  name: "Main WhatsApp Endpoint",
27
27
  description: "Primary WhatsApp webhook",
28
28
  processor_type: "whatsapp",
29
- provider: "cloud_api",
29
+ gateway: "cloud_api",
30
30
  endpoint: "/whatsapp/webhook",
31
31
  icon: "💬",
32
32
  color: "#25D366"
@@ -35,7 +35,7 @@ class SimulatorController < ApplicationController
35
35
  name: "Tenant A WhatsApp",
36
36
  description: "Multi-tenant endpoint for Tenant A",
37
37
  processor_type: "whatsapp",
38
- provider: "cloud_api",
38
+ gateway: "cloud_api",
39
39
  endpoint: "/tenants/a/whatsapp/webhook",
40
40
  icon: "🏢",
41
41
  color: "#fd7e14"
@@ -44,7 +44,7 @@ class SimulatorController < ApplicationController
44
44
  name: "Legacy WhatsApp",
45
45
  description: "Legacy endpoint for compatibility",
46
46
  processor_type: "whatsapp",
47
- provider: "cloud_api",
47
+ gateway: "cloud_api",
48
48
  endpoint: "/legacy/whatsapp",
49
49
  icon: "📦",
50
50
  color: "#6c757d"
@@ -10,8 +10,8 @@ class UssdController < ApplicationController
10
10
  # Use Rails session for USSD (shorter sessions)
11
11
  config.use_session_store FlowChat::Session::RailsSessionStore
12
12
 
13
- # Enable resumable sessions (optional)
14
- config.use_resumable_sessions
13
+ # Enable durable sessions (optional)
14
+ config.use_durable_sessions # Configures flow+platform isolation with durable sessions
15
15
  end
16
16
 
17
17
  processor.run WelcomeFlow, :main_page
@@ -232,7 +232,13 @@ class UssdController < ApplicationController
232
232
  config.use_gateway FlowChat::Ussd::Gateway::Nalo
233
233
  config.use_session_store FlowChat::Session::RailsSessionStore
234
234
  config.use_middleware LoggingMiddleware # Add custom logging
235
- config.use_resumable_sessions # Enable resumable sessions
235
+ config.use_durable_sessions # Enable durable sessions
236
+
237
+ # Or configure session boundaries explicitly:
238
+ # config.use_session_config(
239
+ # boundaries: [:flow, :platform], # which boundaries to enforce
240
+ # hash_phone_numbers: true # hash phone numbers for privacy
241
+ # )
236
242
  end
237
243
 
238
244
  processor.run WelcomeFlow, :main_page
@@ -0,0 +1,74 @@
1
+ module FlowChat
2
+ class BaseApp
3
+ attr_reader :session, :input, :context, :navigation_stack
4
+
5
+ def initialize(context)
6
+ @context = context
7
+ @session = context.session
8
+ @input = context.input
9
+ @navigation_stack = []
10
+ end
11
+
12
+ def screen(key)
13
+ raise ArgumentError, "a block is expected" unless block_given?
14
+ raise ArgumentError, "screen has already been presented" if navigation_stack.include?(key)
15
+
16
+ navigation_stack << key
17
+ return session.get(key) if session.get(key).present?
18
+
19
+ user_input = prepare_user_input
20
+ prompt = FlowChat::Prompt.new user_input
21
+ @input = nil # input is being submitted to prompt so we clear it
22
+
23
+ value = yield prompt
24
+ session.set(key, value)
25
+ value
26
+ end
27
+
28
+ def go_back
29
+ return false if navigation_stack.empty?
30
+
31
+ @context.input = nil
32
+ current_screen = navigation_stack.last
33
+ session.delete(current_screen)
34
+
35
+ # Restart the flow from the beginning
36
+ raise FlowChat::Interrupt::RestartFlow.new
37
+ end
38
+
39
+ def say(msg, media: nil)
40
+ raise FlowChat::Interrupt::Terminate.new(msg, media: media)
41
+ end
42
+
43
+ def phone_number
44
+ context["request.msisdn"]
45
+ end
46
+
47
+ def message_id
48
+ context["request.message_id"]
49
+ end
50
+
51
+ def timestamp
52
+ context["request.timestamp"]
53
+ end
54
+
55
+ def contact_name
56
+ nil
57
+ end
58
+
59
+ def location
60
+ nil
61
+ end
62
+
63
+ def media
64
+ nil
65
+ end
66
+
67
+ protected
68
+
69
+ # Platform-specific methods to be overridden
70
+ def prepare_user_input
71
+ input
72
+ end
73
+ end
74
+ end