flow_chat 0.8.1 → 0.9.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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/.cliff.toml +74 -0
  3. data/.github/workflows/ci.yml +2 -3
  4. data/.github/workflows/release.yml +56 -0
  5. data/.standard.yml +4 -0
  6. data/CHANGELOG.md +22 -0
  7. data/CLAUDE.md +327 -0
  8. data/CONTRIBUTING.md +134 -0
  9. data/Gemfile +1 -0
  10. data/README.md +290 -105
  11. data/Rakefile +5 -1
  12. data/SECURITY.md +42 -349
  13. data/docs/architecture.md +510 -0
  14. data/docs/async-background-processing.md +298 -0
  15. data/docs/configuration.md +556 -226
  16. data/docs/factory-pattern.md +355 -0
  17. data/docs/gateway-context-variables.md +171 -0
  18. data/docs/gateway-development.md +723 -0
  19. data/docs/getting-started.md +429 -0
  20. data/docs/instrumentation.md +264 -153
  21. data/docs/platforms/telegram.md +1013 -0
  22. data/docs/platforms/ussd.md +693 -0
  23. data/docs/platforms/whatsapp.md +1395 -0
  24. data/docs/testing.md +243 -365
  25. data/examples/custom_session_id_example.rb +119 -0
  26. data/examples/http_controller.rb +154 -0
  27. data/examples/intercom_configuration_example.rb +118 -0
  28. data/examples/intercom_controller.rb +194 -0
  29. data/examples/multi_tenant_whatsapp_controller.rb +4 -4
  30. data/examples/simulator_controller.rb +21 -1
  31. data/examples/ussd_controller.rb +10 -10
  32. data/examples/whatsapp_controller.rb +2 -2
  33. data/flow_chat.gemspec +4 -0
  34. data/lib/flow_chat/{base_app.rb → app.rb} +28 -9
  35. data/lib/flow_chat/async_job.rb +176 -0
  36. data/lib/flow_chat/config.rb +23 -28
  37. data/lib/flow_chat/{base_executor.rb → executor.rb} +7 -12
  38. data/lib/flow_chat/factory.rb +94 -0
  39. data/lib/flow_chat/gateway_async_support.rb +106 -0
  40. data/lib/flow_chat/generic_async_job.rb +30 -0
  41. data/lib/flow_chat/http/gateway/simple.rb +125 -0
  42. data/lib/flow_chat/http/renderer.rb +41 -0
  43. data/lib/flow_chat/instrumentation/setup.rb +1 -1
  44. data/lib/flow_chat/instrumentation.rb +25 -0
  45. data/lib/flow_chat/intercom/client.rb +155 -0
  46. data/lib/flow_chat/intercom/configuration.rb +149 -0
  47. data/lib/flow_chat/intercom/gateway/intercom_api.rb +396 -0
  48. data/lib/flow_chat/intercom/renderer.rb +71 -0
  49. data/lib/flow_chat/phone_number_util.rb +49 -0
  50. data/lib/flow_chat/processor.rb +188 -0
  51. data/lib/flow_chat/renderers/markdown_support.rb +58 -0
  52. data/lib/flow_chat/session/cache_session_store.rb +1 -17
  53. data/lib/flow_chat/session/middleware.rb +43 -26
  54. data/lib/flow_chat/simulator/controller.rb +17 -5
  55. data/lib/flow_chat/simulator/views/simulator.html.erb +220 -8
  56. data/lib/flow_chat/telegram/client.rb +240 -0
  57. data/lib/flow_chat/telegram/configuration.rb +118 -0
  58. data/lib/flow_chat/telegram/gateway/bot_api.rb +300 -0
  59. data/lib/flow_chat/telegram/middleware/choice_mapper.rb +35 -0
  60. data/lib/flow_chat/telegram/renderer.rb +125 -0
  61. data/lib/flow_chat/telegram.rb +7 -0
  62. data/lib/flow_chat/ussd/gateway/nalo.rb +27 -11
  63. data/lib/flow_chat/ussd/middleware/pagination.rb +9 -5
  64. data/lib/flow_chat/ussd/renderer.rb +1 -1
  65. data/lib/flow_chat/version.rb +1 -1
  66. data/lib/flow_chat/whatsapp/client.rb +144 -13
  67. data/lib/flow_chat/whatsapp/configuration.rb +1 -1
  68. data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +144 -106
  69. data/lib/flow_chat/whatsapp/id_generator.rb +124 -0
  70. data/lib/flow_chat/whatsapp/middleware/choice_mapper.rb +147 -0
  71. data/lib/flow_chat/whatsapp/renderer.rb +145 -11
  72. data/lib/flow_chat.rb +11 -1
  73. data/lib/tasks/release.rake +165 -0
  74. metadata +88 -21
  75. data/docs/flows.md +0 -320
  76. data/docs/images/simulator.png +0 -0
  77. data/docs/media.md +0 -153
  78. data/docs/sessions.md +0 -433
  79. data/docs/ussd-setup.md +0 -322
  80. data/docs/whatsapp-setup.md +0 -162
  81. data/examples/whatsapp_message_job.rb +0 -113
  82. data/lib/flow_chat/base_processor.rb +0 -145
  83. data/lib/flow_chat/session/rails_session_store.rb +0 -68
  84. data/lib/flow_chat/ussd/app.rb +0 -6
  85. data/lib/flow_chat/ussd/gateway/nsano.rb +0 -98
  86. data/lib/flow_chat/ussd/middleware/executor.rb +0 -24
  87. data/lib/flow_chat/ussd/processor.rb +0 -39
  88. data/lib/flow_chat/whatsapp/app.rb +0 -29
  89. data/lib/flow_chat/whatsapp/middleware/executor.rb +0 -24
  90. data/lib/flow_chat/whatsapp/processor.rb +0 -32
  91. data/lib/flow_chat/whatsapp/send_job_support.rb +0 -79
@@ -1,360 +1,690 @@
1
- # Configuration Reference
1
+ # FlowChat Configuration
2
2
 
3
- This document covers all FlowChat configuration options.
3
+ This guide covers all configuration options for FlowChat, from basic setup to advanced customization for production deployments.
4
4
 
5
- ## Framework Configuration
5
+ ## Global Configuration
6
6
 
7
- ```ruby
8
- # config/initializers/flowchat.rb
7
+ Configure FlowChat globally in `config/initializers/flow_chat.rb`:
9
8
 
10
- # Core configuration
9
+ ```ruby
10
+ # Basic configuration
11
11
  FlowChat::Config.logger = Rails.logger
12
12
  FlowChat::Config.cache = Rails.cache
13
- FlowChat::Config.simulator_secret = "your_secure_secret_here"
14
13
 
15
- # Validation error display behavior
16
- FlowChat::Config.combine_validation_error_with_message = true # default
14
+ # Validation behavior
15
+ FlowChat::Config.combine_validation_error_with_message = true
17
16
 
18
- # Setup instrumentation (optional)
19
- FlowChat.setup_instrumentation!
17
+ # Simulator settings (for development/testing)
18
+ FlowChat::Config.simulator_secret = "your_simulator_secret"
20
19
  ```
21
20
 
22
- ## Session Configuration
21
+ ## Platform-Specific Configuration
22
+
23
+ ### USSD Configuration
23
24
 
24
25
  ```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)
26
+ # USSD pagination settings
27
+ FlowChat::Config.ussd.pagination_page_size = 160 # Characters per page
28
+ FlowChat::Config.ussd.pagination_next_option = "#" # Next page option
29
+ FlowChat::Config.ussd.pagination_back_option = "0" # Previous page option
30
+ FlowChat::Config.ussd.pagination_next_text = "More" # Next page text
31
+ FlowChat::Config.ussd.pagination_back_text = "Back" # Previous page text
40
32
  ```
41
33
 
42
- ## USSD Configuration
34
+ ### WhatsApp Configuration
43
35
 
44
36
  ```ruby
45
- # USSD pagination settings
46
- FlowChat::Config.ussd.pagination_page_size = 140 # characters per page
47
- FlowChat::Config.ussd.pagination_next_option = "#" # option to go to next page
48
- FlowChat::Config.ussd.pagination_next_text = "More" # text for next option
49
- FlowChat::Config.ussd.pagination_back_option = "0" # option to go back
50
- FlowChat::Config.ussd.pagination_back_text = "Back" # text for back option
37
+ # WhatsApp API configuration
38
+ FlowChat::Config.whatsapp.api_base_url = "https://graph.facebook.com/v23.0"
51
39
  ```
52
40
 
53
- ## WhatsApp Configuration
41
+ ### HTTP Configuration
54
42
 
55
43
  ```ruby
56
- # Message handling modes
57
- FlowChat::Config.whatsapp.message_handling_mode = :inline # :inline, :background, :simulator
58
- FlowChat::Config.whatsapp.background_job_class = 'WhatsappMessageJob'
44
+ # HTTP gateway settings
45
+ FlowChat::Config.http.default_gateway = :simple
46
+ FlowChat::Config.http.request_timeout = 30
47
+ FlowChat::Config.http.response_format = :json
59
48
  ```
60
49
 
61
- ### WhatsApp Credential Configuration
50
+ ## Processor Configuration
62
51
 
63
- #### Option 1: Rails Credentials
52
+ Configure processors for each request:
64
53
 
65
- ```bash
66
- rails credentials:edit
54
+ ```ruby
55
+ processor = FlowChat::Processor.new(self) do |config|
56
+ # Gateway configuration
57
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
58
+
59
+ # Session configuration
60
+ config.use_session_store FlowChat::Session::CacheSessionStore
61
+ config.use_session_config(
62
+ boundaries: [:flow, :platform, :gateway],
63
+ identifier: :msisdn,
64
+ hash_identifiers: true
65
+ )
66
+
67
+ # Middleware configuration
68
+ config.use_middleware LoggingMiddleware
69
+ config.use_middleware AuthenticationMiddleware
70
+
71
+ # Convenience methods
72
+ config.use_durable_sessions
73
+ config.use_cross_platform_sessions
74
+ config.use_url_isolation
75
+ end
67
76
  ```
68
77
 
69
- ```yaml
70
- whatsapp:
71
- access_token: "your_access_token"
72
- phone_number_id: "your_phone_number_id"
73
- verify_token: "your_verify_token"
74
- app_id: "your_app_id"
75
- app_secret: "your_app_secret"
76
- business_account_id: "your_business_account_id"
77
- skip_signature_validation: false
78
+ ## Session Configuration
79
+
80
+ ### Session Boundaries
81
+
82
+ Control how session IDs are generated:
83
+
84
+ ```ruby
85
+ # Available boundaries
86
+ boundaries: [
87
+ :flow, # Separate sessions per flow class
88
+ :platform, # Separate sessions per platform (ussd, whatsapp, http)
89
+ :gateway, # Separate sessions per gateway (nalo, cloud_api, etc.)
90
+ :url # Separate sessions per URL (multi-tenancy)
91
+ ]
92
+
93
+ # Examples
94
+ config.use_session_config(boundaries: [:flow]) # "survey_flow:user123"
95
+ config.use_session_config(boundaries: [:flow, :platform]) # "survey_flow:ussd:user123"
96
+ config.use_session_config(boundaries: [:flow, :url]) # "survey_flow:tenant1.app.com:user123"
78
97
  ```
79
98
 
80
- #### Option 2: Environment Variables
99
+ ### Custom Session ID Generation
81
100
 
82
- ```bash
83
- export WHATSAPP_ACCESS_TOKEN="your_access_token"
84
- export WHATSAPP_PHONE_NUMBER_ID="your_phone_number_id"
85
- export WHATSAPP_VERIFY_TOKEN="your_verify_token"
86
- export WHATSAPP_APP_ID="your_app_id"
87
- export WHATSAPP_APP_SECRET="your_app_secret"
88
- export WHATSAPP_BUSINESS_ACCOUNT_ID="your_business_account_id"
89
- export WHATSAPP_SKIP_SIGNATURE_VALIDATION="false"
101
+ For complete control over session ID format, use a block:
102
+
103
+ ```ruby
104
+ # Custom session ID with proc/block
105
+ config.use_session_config do |context|
106
+ # Access full context for custom logic
107
+ user_phone = context["request.msisdn"]
108
+ flow_name = context["flow.name"]
109
+ gateway = context["request.gateway"]
110
+ timestamp = Time.current.strftime("%Y%m%d")
111
+
112
+ # Return your custom session ID
113
+ "#{flow_name}_#{gateway}_#{timestamp}_#{hash_phone(user_phone)}"
114
+ end
115
+
116
+ # Multi-tenant example
117
+ config.use_session_config do |context|
118
+ tenant_id = extract_tenant(context)
119
+ user_id = context["request.user_id"] || context["request.msisdn"]
120
+ "tenant_#{tenant_id}_user_#{user_id.hash.abs}"
121
+ end
122
+
123
+ # API-based session IDs
124
+ config.use_session_config do |context|
125
+ api_key = context.controller.request.headers["X-API-Key"]
126
+ "api_#{Digest::SHA256.hexdigest(api_key)[0,8]}_#{context['request.id']}"
127
+ end
90
128
  ```
91
129
 
92
- #### Option 3: Programmatic Configuration
130
+ **Note**: When using a custom proc, it takes precedence over all boundary and identifier settings. The proc receives the full FlowChat context and should return a string session ID.
131
+
132
+ ### Session Identifiers
133
+
134
+ Choose what identifies a user session:
93
135
 
94
136
  ```ruby
95
- config = FlowChat::Whatsapp::Configuration.new(:my_config) # Named configuration
96
- config.access_token = "your_access_token"
97
- config.phone_number_id = "your_phone_number_id"
98
- config.verify_token = "your_verify_token"
99
- config.app_id = "your_app_id"
100
- config.app_secret = "your_app_secret"
101
- config.business_account_id = "your_business_account_id"
102
- config.skip_signature_validation = false
103
- # Configuration is automatically registered as :my_config
137
+ # Identifier types
138
+ :request_id # Ephemeral (new session each request)
139
+ :user_id # Durable using user_id field
140
+ :msisdn # Durable using phone number
141
+
142
+ # Examples
143
+ config.use_session_config(identifier: :request_id) # Default for HTTP
144
+ config.use_session_config(identifier: :msisdn) # Default for WhatsApp/USSD
145
+ config.use_session_config(identifier: :user_id) # For authenticated users
104
146
  ```
105
147
 
106
- **⚠️ Important for Background Jobs:** When using background mode with programmatic configurations, you must register them in an initializer:
148
+ ### Session Stores
149
+
150
+ Choose where session data is stored:
107
151
 
108
152
  ```ruby
109
- # config/initializers/whatsapp_configs.rb
110
- # Register configurations so background jobs can access them
111
- production_config = FlowChat::Whatsapp::Configuration.new(:production)
112
- production_config.access_token = ENV['PROD_WHATSAPP_TOKEN']
113
- # ... other settings
114
-
115
- staging_config = FlowChat::Whatsapp::Configuration.new(:staging)
116
- staging_config.access_token = ENV['STAGING_WHATSAPP_TOKEN']
117
- # ... other settings
153
+ # Built-in session stores
154
+ config.use_session_store FlowChat::Session::CacheSessionStore # Rails sessions
155
+ config.use_session_store FlowChat::Session::CacheSessionStore # Rails cache
156
+
157
+ # Custom session store
158
+ config.use_session_store MyCompany::CustomSessionStore
118
159
  ```
119
160
 
120
- Then use named configurations in controllers:
161
+ ### Convenience Methods
121
162
 
122
163
  ```ruby
123
- # Use registered configuration
124
- config = FlowChat::Whatsapp::Configuration.get(:production)
125
- processor = FlowChat::Whatsapp::Processor.new(self) do |config|
126
- config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, config
127
- end
164
+ # Durable sessions (use phone number, survive timeouts)
165
+ config.use_durable_sessions
166
+
167
+ # Cross-platform sessions (same user across USSD/WhatsApp)
168
+ config.use_cross_platform_sessions
169
+
170
+ # URL-based isolation (multi-tenancy)
171
+ config.use_url_isolation
128
172
  ```
129
173
 
130
- ## Security Configuration
174
+ ## Gateway Configuration
175
+
176
+ ### Built-in Gateways
177
+
178
+ #### USSD - Nalo Gateway
131
179
 
132
- ### WhatsApp Security
180
+ ```ruby
181
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
182
+
183
+ # No additional configuration required for Nalo
184
+ ```
185
+
186
+ #### WhatsApp - Cloud API Gateway
133
187
 
134
188
  ```ruby
135
- # Production security (recommended)
136
- config.app_secret = "your_whatsapp_app_secret"
137
- config.skip_signature_validation = false # default
189
+ # Basic configuration (uses Rails credentials)
190
+ config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
191
+
192
+ # Custom configuration
193
+ whatsapp_config = FlowChat::Whatsapp::Configuration.new
194
+ whatsapp_config.access_token = ENV["WHATSAPP_ACCESS_TOKEN"]
195
+ whatsapp_config.phone_number_id = ENV["WHATSAPP_PHONE_NUMBER_ID"]
196
+ whatsapp_config.verify_token = ENV["WHATSAPP_VERIFY_TOKEN"]
197
+ whatsapp_config.app_secret = ENV["WHATSAPP_APP_SECRET"]
198
+ whatsapp_config.skip_signature_validation = Rails.env.development?
199
+
200
+ config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, whatsapp_config
201
+ ```
202
+
203
+ #### HTTP - Simple Gateway
138
204
 
139
- # Development mode (disable validation)
140
- config.app_secret = nil
141
- config.skip_signature_validation = true
205
+ ```ruby
206
+ # HTTP gateway requires user_params hash
207
+ config.use_gateway FlowChat::Http::Gateway::Simple, {
208
+ session_id: "unique_session_id", # required
209
+ user_id: "user_identifier", # required
210
+ name: "John Doe", # optional
211
+ msisdn: "+256700123456", # optional
212
+ email: "user@example.com" # optional
213
+ }
142
214
  ```
143
215
 
144
- ### Simulator Security
216
+ **Required Parameters:**
217
+ - `session_id` - Unique identifier for the session
218
+ - `user_id` - User identifier
219
+
220
+ **Optional Parameters:**
221
+ - `name` - User's display name (sets `request.user_name`)
222
+ - `msisdn` - E.164 formatted phone number (sets `request.msisdn`)
223
+ - `email` - User email address (sets `request.email`)
224
+
225
+ **Note:** User input comes from `params["input"]` in the request. The `message_id` is auto-generated as a UUID.
226
+
227
+ ### Custom Gateway Configuration
145
228
 
146
229
  ```ruby
147
- # Use Rails secret for uniqueness
148
- FlowChat::Config.simulator_secret = Rails.application.secret_key_base + "_simulator"
230
+ # Custom gateway with configuration
231
+ my_config = MyCompany::CustomGatewayConfig.new
232
+ my_config.api_key = ENV["CUSTOM_API_KEY"]
233
+ my_config.endpoint = ENV["CUSTOM_ENDPOINT"]
149
234
 
150
- # Or use dedicated secret
151
- FlowChat::Config.simulator_secret = ENV['FLOWCHAT_SIMULATOR_SECRET']
235
+ config.use_gateway MyCompany::CustomGateway, my_config
152
236
  ```
153
237
 
154
238
  ## Environment-Specific Configuration
155
239
 
240
+ ### Development Configuration
241
+
156
242
  ```ruby
157
- # config/initializers/flowchat.rb
158
- case Rails.env
159
- when 'development'
160
- FlowChat::Config.whatsapp.message_handling_mode = :simulator
161
- FlowChat::Config.simulator_secret = Rails.application.secret_key_base + "_dev"
243
+ # config/environments/development.rb
244
+ Rails.application.configure do
245
+ # Enable detailed logging
246
+ config.log_level = :debug
162
247
 
163
- when 'test'
164
- FlowChat::Config.whatsapp.message_handling_mode = :simulator
165
- FlowChat::Config.simulator_secret = "test_secret"
166
-
167
- when 'staging'
168
- FlowChat::Config.whatsapp.message_handling_mode = :inline
169
- FlowChat::Config.simulator_secret = ENV['FLOWCHAT_SIMULATOR_SECRET']
170
-
171
- when 'production'
172
- FlowChat::Config.whatsapp.message_handling_mode = :background
173
- FlowChat::Config.whatsapp.background_job_class = 'WhatsappMessageJob'
174
- FlowChat::Config.simulator_secret = ENV['FLOWCHAT_SIMULATOR_SECRET']
248
+ # FlowChat development settings
249
+ config.after_initialize do
250
+ FlowChat::Config.logger = Rails.logger
251
+ FlowChat::Config.logger.level = Logger::DEBUG
252
+
253
+ # USSD settings for development
254
+ FlowChat::Config.ussd.pagination_page_size = 200 # Larger for easier testing
255
+
256
+ # WhatsApp settings for development
257
+ FlowChat::Config.whatsapp.message_handling_mode = :inline
258
+
259
+ # Enable simulator
260
+ FlowChat::Config.simulator_secret = "dev_secret_123"
261
+ end
175
262
  end
176
263
  ```
177
264
 
178
- ## Processor Configuration
179
-
180
- ### USSD Processor
265
+ ### Production Configuration
181
266
 
182
267
  ```ruby
183
- processor = FlowChat::Ussd::Processor.new(self) do |config|
184
- # Gateway (required)
185
- config.use_gateway FlowChat::Ussd::Gateway::Nalo
186
-
187
- # Session storage (required)
188
- config.use_session_store FlowChat::Session::CacheSessionStore
189
-
190
- # Optional middleware
191
- config.use_middleware MyCustomMiddleware
192
-
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
- )
268
+ # config/environments/production.rb
269
+ Rails.application.configure do
270
+ # Production logging
271
+ config.log_level = :info
199
272
 
200
- # Shorthand for durable sessions (identifier: :msisdn)
201
- config.use_durable_sessions
273
+ # FlowChat production settings
274
+ config.after_initialize do
275
+ FlowChat::Config.logger = Rails.logger
276
+
277
+ # USSD production settings
278
+ FlowChat::Config.ussd.pagination_page_size = 140 # Conservative for compatibility
279
+
280
+ # WhatsApp uses inline by default
281
+
282
+ # Disable simulator in production
283
+ FlowChat::Config.simulator_secret = nil
284
+ end
202
285
  end
203
286
  ```
204
287
 
205
- ### WhatsApp Processor
288
+ ### Staging Configuration
206
289
 
207
290
  ```ruby
208
- processor = FlowChat::Whatsapp::Processor.new(self, enable_simulator: Rails.env.development?) do |config|
209
- # Gateway (required)
210
- config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
211
-
212
- # Session storage (required)
213
- config.use_session_store FlowChat::Session::CacheSessionStore
214
-
215
- # Optional custom configuration
216
- config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, custom_whatsapp_config
291
+ # config/environments/staging.rb
292
+ Rails.application.configure do
293
+ # Staging-specific settings
294
+ config.after_initialize do
295
+ FlowChat::Config.logger = Rails.logger
296
+ FlowChat::Config.logger.level = Logger::DEBUG # More verbose for staging
297
+
298
+ # Use background processing but with shorter delays
299
+ FlowChat::Config.whatsapp.message_handling_mode = :background
300
+
301
+ # Enable simulator with staging secret
302
+ FlowChat::Config.simulator_secret = ENV["STAGING_SIMULATOR_SECRET"]
303
+ end
217
304
  end
218
305
  ```
219
306
 
220
- ## Session Store Options
307
+ ## Multi-Tenant Configuration
221
308
 
222
- ### Cache Session Store
309
+ ### URL-Based Tenancy
223
310
 
224
311
  ```ruby
225
- config.use_session_store FlowChat::Session::CacheSessionStore
226
- ```
312
+ class MultiTenantController < ApplicationController
313
+ def process_request
314
+ processor = FlowChat::Processor.new(self) do |config|
315
+ config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, tenant_whatsapp_config
316
+ config.use_session_store FlowChat::Session::CacheSessionStore
317
+
318
+ # Enable URL-based session isolation
319
+ config.use_url_isolation
320
+
321
+ # Optional: Additional tenant isolation
322
+ config.use_session_config(
323
+ boundaries: [:flow, :platform, :url],
324
+ identifier: :user_id
325
+ )
326
+ end
327
+
328
+ processor.run tenant_flow_class, :main_action
329
+ end
227
330
 
228
- Uses Rails cache backend with automatic TTL management. This is the primary session store available in FlowChat.
331
+ private
229
332
 
230
- ## Middleware Configuration
333
+ def tenant_whatsapp_config
334
+ tenant = extract_tenant_from_request
335
+ tenant.whatsapp_configuration
336
+ end
337
+
338
+ def tenant_flow_class
339
+ tenant = extract_tenant_from_request
340
+ tenant.flow_class
341
+ end
342
+
343
+ def extract_tenant_from_request
344
+ # Extract tenant from subdomain, domain, or path
345
+ subdomain = request.subdomain
346
+ Tenant.find_by(subdomain: subdomain)
347
+ end
348
+ end
349
+ ```
231
350
 
232
- ### Built-in Middleware
351
+ ### Database-Based Tenancy
233
352
 
234
353
  ```ruby
235
- # Pagination (USSD only, automatic)
236
- FlowChat::Ussd::Middleware::Pagination
354
+ class TenantSpecificController < ApplicationController
355
+ before_action :set_tenant
356
+
357
+ def process_request
358
+ processor = FlowChat::Processor.new(self) do |config|
359
+ config.use_gateway gateway_for_tenant
360
+ config.use_session_store FlowChat::Session::CacheSessionStore
361
+
362
+ # Custom session boundaries for tenant isolation
363
+ config.use_session_config(
364
+ boundaries: [:flow, :platform],
365
+ identifier: :user_id
366
+ )
367
+ end
368
+
369
+ processor.run @tenant.flow_class.constantize, :main_action
370
+ end
237
371
 
238
- # Session management (automatic)
239
- FlowChat::Session::Middleware
372
+ private
240
373
 
241
- # Gateway communication (automatic)
242
- FlowChat::Ussd::Gateway::Nalo
243
- FlowChat::Whatsapp::Gateway::CloudApi
374
+ def set_tenant
375
+ @tenant = Tenant.find(params[:tenant_id])
376
+ end
377
+
378
+ def gateway_for_tenant
379
+ case @tenant.platform
380
+ when 'whatsapp'
381
+ FlowChat::Whatsapp::Gateway::CloudApi
382
+ when 'ussd'
383
+ FlowChat::Ussd::Gateway::Nalo
384
+ else
385
+ FlowChat::Http::Gateway::Simple
386
+ end
387
+ end
388
+ end
244
389
  ```
245
390
 
246
- ### Custom Middleware
391
+ ## Security Configuration
392
+
393
+ ### WhatsApp Signature Validation
247
394
 
248
395
  ```ruby
249
- class LoggingMiddleware
250
- def initialize(app)
251
- @app = app
396
+ # Always validate signatures in production
397
+ whatsapp_config = FlowChat::Whatsapp::Configuration.new
398
+ whatsapp_config.app_secret = ENV["WHATSAPP_APP_SECRET"]
399
+ whatsapp_config.skip_signature_validation = false # Never skip in production
400
+
401
+ # Only skip validation in development if needed
402
+ whatsapp_config.skip_signature_validation = Rails.env.development?
403
+ ```
404
+
405
+ ### Session Security
406
+
407
+ ```ruby
408
+ # Hash sensitive identifiers
409
+ config.use_session_config(
410
+ identifier: :msisdn,
411
+ hash_identifiers: true # Phone numbers are hashed for privacy
412
+ )
413
+
414
+ # Implement custom session store with encryption
415
+ class EncryptedSessionStore < FlowChat::Session::CacheSessionStore
416
+ def set(key, value)
417
+ encrypted_value = encrypt(value.to_json)
418
+ super(key, encrypted_value)
252
419
  end
253
420
 
254
- def call(context)
255
- Rails.logger.info "Processing request: #{context.input}"
256
- result = @app.call(context)
257
- Rails.logger.info "Response: #{result[1]}"
258
- result
421
+ def get(key)
422
+ encrypted_value = super(key)
423
+ return nil unless encrypted_value
424
+
425
+ decrypted = decrypt(encrypted_value)
426
+ JSON.parse(decrypted)
427
+ rescue
428
+ nil # Return nil if decryption fails
429
+ end
430
+
431
+ private
432
+
433
+ def encrypt(data)
434
+ # Use Rails credentials or environment variable
435
+ secret = Rails.application.credentials.session_encryption_key
436
+ crypt = ActiveSupport::MessageEncryptor.new(secret)
437
+ crypt.encrypt_and_sign(data)
438
+ end
439
+
440
+ def decrypt(encrypted_data)
441
+ secret = Rails.application.credentials.session_encryption_key
442
+ crypt = ActiveSupport::MessageEncryptor.new(secret)
443
+ crypt.decrypt_and_verify(encrypted_data)
259
444
  end
260
445
  end
261
446
 
262
- # Use custom middleware
263
- config.use_middleware LoggingMiddleware
447
+ # Use encrypted session store
448
+ config.use_session_store EncryptedSessionStore
264
449
  ```
265
450
 
266
- ## Validation Configuration
451
+ ## Performance Configuration
267
452
 
268
- ### Error Display Options
453
+ ### Redis Cache Configuration
269
454
 
270
455
  ```ruby
271
- # Combine validation error with original message (default)
272
- FlowChat::Config.combine_validation_error_with_message = true
273
- # User sees: "Invalid email format\n\nEnter your email:"
456
+ # config/initializers/redis.rb
457
+ redis_config = {
458
+ url: ENV["REDIS_URL"],
459
+ pool_size: ENV.fetch("REDIS_POOL_SIZE", 5).to_i,
460
+ pool_timeout: ENV.fetch("REDIS_POOL_TIMEOUT", 5).to_i
461
+ }
462
+
463
+ # Use Redis for session storage
464
+ Rails.application.configure do
465
+ config.cache_store = :redis_cache_store, redis_config
466
+ end
274
467
 
275
- # Show only validation error
276
- FlowChat::Config.combine_validation_error_with_message = false
277
- # User sees: "Invalid email format"
468
+ # FlowChat will automatically use Rails.cache
469
+ FlowChat::Config.cache = Rails.cache
278
470
  ```
279
471
 
280
- ## Background Job Configuration
281
-
282
- ### Job Class Setup
472
+ ### Background Job Configuration
283
473
 
284
474
  ```ruby
285
- # app/jobs/whatsapp_message_job.rb
286
- class WhatsappMessageJob < ApplicationJob
287
- include FlowChat::Whatsapp::SendJobSupport
475
+ # config/initializers/sidekiq.rb (if using Sidekiq)
476
+ Sidekiq.configure_server do |config|
477
+ config.redis = { url: ENV["REDIS_URL"] }
478
+ end
288
479
 
289
- def perform(send_data)
290
- perform_whatsapp_send(send_data)
291
- end
480
+ Sidekiq.configure_client do |config|
481
+ config.redis = { url: ENV["REDIS_URL"] }
292
482
  end
483
+
484
+ # WhatsApp uses inline responses by defaul
293
485
  ```
294
486
 
295
- **Configuration Resolution:** The job automatically resolves configurations using:
296
- 1. Named configuration from `send_data[:configuration_name]` if present
297
- 2. Default configuration from credentials/environment variables
487
+ ### Database Connection Configuration
488
+
489
+ ```ruby
490
+ # config/database.yml
491
+ production:
492
+ adapter: postgresql
493
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS", 5) %>
494
+ timeout: 5000
495
+ # ... other database settings
496
+
497
+ # For high-volume USSD applications
498
+ checkout_timeout: 2
499
+ reaping_frequency: 10
500
+ ```
298
501
 
299
- For custom resolution logic, override the configuration resolution:
502
+ ## Monitoring Configuration
503
+
504
+ ### Instrumentation Setup
300
505
 
301
506
  ```ruby
302
- class CustomWhatsappMessageJob < ApplicationJob
303
- include FlowChat::Whatsapp::SendJobSupport
507
+ # config/initializers/flow_chat.rb
508
+ FlowChat.setup_instrumentation!
509
+
510
+ # Subscribe to FlowChat events
511
+ ActiveSupport::Notifications.subscribe("flow_chat.message_received") do |event|
512
+ # Log message received
513
+ Rails.logger.info "Message received: #{event.payload}"
514
+ end
515
+
516
+ ActiveSupport::Notifications.subscribe("flow_chat.message_sent") do |event|
517
+ # Log message sent
518
+ Rails.logger.info "Message sent: #{event.payload}"
519
+ end
520
+
521
+ ActiveSupport::Notifications.subscribe("flow_chat.flow_execution_error") do |event|
522
+ # Alert on flow errors
523
+ ErrorNotificationService.notify(event.payload)
524
+ end
525
+ ```
526
+
527
+ ### Custom Metrics
304
528
 
305
- def perform(send_data)
306
- perform_whatsapp_send(send_data)
529
+ ```ruby
530
+ # Custom metrics collector
531
+ class CustomMetricsCollector
532
+ def self.collect_metrics
533
+ {
534
+ active_sessions: count_active_sessions,
535
+ messages_per_minute: calculate_message_rate,
536
+ error_rate: calculate_error_rate
537
+ }
307
538
  end
308
539
 
309
540
  private
310
541
 
311
- def resolve_whatsapp_configuration(send_data)
312
- # Custom logic to resolve configuration
313
- tenant_id = ...
314
- FlowChat::Whatsapp::Configuration.get("tenant_#{tenant_id}")
542
+ def self.count_active_sessions
543
+ # Implementation depends on your session store
544
+ Rails.cache.redis.keys("flow_chat:session:*").count
545
+ end
546
+
547
+ def self.calculate_message_rate
548
+ # Implementation depends on your metrics storage
549
+ # Return messages processed in the last minute
550
+ end
551
+
552
+ def self.calculate_error_rate
553
+ # Calculate error percentage
315
554
  end
316
555
  end
556
+
557
+ # Setup metrics collection
558
+ FlowChat::Config.metrics_collector = CustomMetricsCollector
317
559
  ```
318
560
 
319
- ### Queue Configuration
561
+ ## Testing Configuration
562
+
563
+ ### Test Environment
320
564
 
321
565
  ```ruby
322
- # config/application.rb
323
- config.active_job.queue_adapter = :sidekiq
566
+ # config/environments/test.rb
567
+ Rails.application.configure do
568
+ config.after_initialize do
569
+ FlowChat::Config.logger = Rails.logger
570
+ FlowChat::Config.logger.level = Logger::ERROR # Reduce noise in tests
571
+
572
+ # Use memory cache for tests
573
+ FlowChat::Config.cache = ActiveSupport::Cache::MemoryStore.new
574
+
575
+ # Disable background processing in tests
576
+ FlowChat::Config.whatsapp.message_handling_mode = :inline
577
+
578
+ # Enable simulator for testing
579
+ FlowChat::Config.simulator_secret = "test_secret"
580
+ end
581
+ end
582
+ ```
583
+
584
+ ### Test Helper Configuration
324
585
 
325
- # config/initializers/flowchat.rb
326
- FlowChat::Config.whatsapp.background_job_class = 'WhatsappMessageJob'
586
+ ```ruby
587
+ # test/test_helper.rb
588
+ class ActiveSupport::TestCase
589
+ def setup_flow_chat_test_environment
590
+ # Reset FlowChat state between tests
591
+ FlowChat::Config.cache.clear
592
+
593
+ # Mock external API calls
594
+ stub_whatsapp_api_calls
595
+ stub_ussd_gateway_calls
596
+ end
597
+
598
+ def create_test_processor(platform: :ussd, **options)
599
+ gateway_class = case platform
600
+ when :ussd then FlowChat::Ussd::Gateway::Nalo
601
+ when :whatsapp then FlowChat::Whatsapp::Gateway::CloudApi
602
+ when :http then FlowChat::Http::Gateway::Simple
603
+ end
604
+
605
+ FlowChat::Processor.new(MockController.new) do |config|
606
+ config.use_gateway gateway_class
607
+ config.use_session_store FlowChat::Session::MemorySessionStore
608
+ options.each { |key, value| config.send(key, value) }
609
+ end
610
+ end
611
+ end
327
612
  ```
328
613
 
329
- ## Instrumentation Configuration
614
+ ## Troubleshooting Configuration
330
615
 
331
- ### Basic Setup
616
+ ### Debug Mode
332
617
 
333
618
  ```ruby
334
- # Enable instrumentation
335
- FlowChat.setup_instrumentation!
619
+ # Enable comprehensive debugging
620
+ FlowChat::Config.logger.level = Logger::DEBUG
621
+
622
+ # Add custom debug middleware
623
+ class DebugMiddleware
624
+ def initialize(app)
625
+ @app = app
626
+ end
627
+
628
+ def call(context)
629
+ Rails.logger.debug "=== FlowChat Debug ==="
630
+ Rails.logger.debug "Context: #{context.to_h}"
631
+ Rails.logger.debug "Input: #{context.input.inspect}"
632
+ Rails.logger.debug "Session ID: #{context['session.id']}"
633
+
634
+ result = @app.call(context)
635
+
636
+ Rails.logger.debug "Result: #{result.inspect}"
637
+ Rails.logger.debug "======================"
638
+
639
+ result
640
+ end
641
+ end
642
+
643
+ # Use debug middleware
644
+ config.use_middleware DebugMiddleware
336
645
  ```
337
646
 
338
- ### Custom Event Subscribers
647
+ ### Configuration Validation
339
648
 
340
649
  ```ruby
341
- # Subscribe to specific events
342
- ActiveSupport::Notifications.subscribe("flow.execution.end.flow_chat") do |event|
343
- # Custom handling
344
- ExternalMonitoring.track_flow_execution(
345
- event.payload[:flow_name],
346
- event.duration
347
- )
650
+ # Add configuration validation
651
+ class ConfigurationValidator
652
+ def self.validate!
653
+ validate_environment_variables!
654
+ validate_gateway_configuration!
655
+ validate_session_configuration!
656
+ end
657
+
658
+ private
659
+
660
+ def self.validate_environment_variables!
661
+ required_vars = %w[
662
+ WHATSAPP_ACCESS_TOKEN
663
+ WHATSAPP_PHONE_NUMBER_ID
664
+ WHATSAPP_VERIFY_TOKEN
665
+ ]
666
+
667
+ missing = required_vars.select { |var| ENV[var].blank? }
668
+ raise "Missing environment variables: #{missing.join(', ')}" if missing.any?
669
+ end
670
+
671
+ def self.validate_gateway_configuration!
672
+ # Validate gateway-specific configuration
673
+ end
674
+
675
+ def self.validate_session_configuration!
676
+ # Validate session store connectivity
677
+ FlowChat::Config.cache.write("test_key", "test_value")
678
+ FlowChat::Config.cache.delete("test_key")
679
+ rescue => e
680
+ raise "Session store configuration invalid: #{e.message}"
681
+ end
348
682
  end
349
683
 
350
- # Subscribe to all FlowChat events
351
- ActiveSupport::Notifications.subscribe(/\.flow_chat$/) do |name, start, finish, id, payload|
352
- CustomLogger.log_event(name, payload.merge(duration: finish - start))
684
+ # Run validation on startup
685
+ Rails.application.config.after_initialize do
686
+ ConfigurationValidator.validate! if Rails.env.production?
353
687
  end
354
688
  ```
355
689
 
356
- ## Configuration Validation
357
-
358
- FlowChat validates configuration at runtime and provides helpful error messages:
359
-
360
- FlowChat validates configuration at runtime and provides helpful error messages for missing or invalid configurations.
690
+ This configuration guide covers all aspects of FlowChat setup. For platform-specific configuration details, see the individual platform guides.