brainzlab 0.1.10 → 0.1.12
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/README.md +220 -4
- data/lib/brainzlab/beacon/client.rb +21 -1
- data/lib/brainzlab/configuration.rb +51 -2
- data/lib/brainzlab/cortex/client.rb +21 -1
- data/lib/brainzlab/debug.rb +305 -0
- data/lib/brainzlab/dendrite/client.rb +21 -1
- data/lib/brainzlab/development/logger.rb +150 -0
- data/lib/brainzlab/development/store.rb +121 -0
- data/lib/brainzlab/development.rb +72 -0
- data/lib/brainzlab/devtools/assets/devtools.css +326 -103
- data/lib/brainzlab/devtools/assets/devtools.js +79 -5
- data/lib/brainzlab/devtools/assets/templates/debug_panel.html.erb +11 -0
- data/lib/brainzlab/errors.rb +490 -0
- data/lib/brainzlab/nerve/client.rb +21 -1
- data/lib/brainzlab/pulse/client.rb +66 -5
- data/lib/brainzlab/pulse.rb +17 -4
- data/lib/brainzlab/recall/client.rb +74 -6
- data/lib/brainzlab/recall.rb +19 -2
- data/lib/brainzlab/reflex/client.rb +66 -5
- data/lib/brainzlab/reflex.rb +40 -8
- data/lib/brainzlab/sentinel/client.rb +21 -1
- data/lib/brainzlab/synapse/client.rb +21 -1
- data/lib/brainzlab/testing/event_store.rb +377 -0
- data/lib/brainzlab/testing/helpers.rb +650 -0
- data/lib/brainzlab/testing/matchers.rb +391 -0
- data/lib/brainzlab/testing.rb +327 -0
- data/lib/brainzlab/utilities/circuit_breaker.rb +32 -3
- data/lib/brainzlab/vault/client.rb +21 -1
- data/lib/brainzlab/version.rb +1 -1
- data/lib/brainzlab/vision/client.rb +53 -6
- data/lib/brainzlab.rb +42 -0
- metadata +24 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 85a5cf3a64e256a569be39f7caa1ec6268755616ac6037db51f1543ee74dd50c
|
|
4
|
+
data.tar.gz: '09c531bb0793d26db1f62faf41f9ead294130a7036f6e5f6ab270fd8c6c46040'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 28c2f10fc4457d4f2f34c5bcf4d63c14a062d4fcd8437b13cbcebc1a062976361b2014f5567e0fff82682a054fa83363f7e955a23d3d59ae285a9f06500c79e6
|
|
7
|
+
data.tar.gz: 5d130bf7242d57c3149803649262995daf298483c406002ab99e01495efb14df7a6874b8eb485daa90c79da8903ad37f6516ba0ca7e2c8930ff069a1b536a206
|
data/README.md
CHANGED
|
@@ -47,12 +47,21 @@ bundle config set --global rubygems.pkg.github.com USERNAME:TOKEN
|
|
|
47
47
|
|
|
48
48
|
## Quick Start
|
|
49
49
|
|
|
50
|
+
### Get Your API Key
|
|
51
|
+
|
|
52
|
+
1. Sign up at [platform.brainzlab.ai](https://platform.brainzlab.ai)
|
|
53
|
+
2. Create or select a project
|
|
54
|
+
3. Copy your API key (`sk_live_xxx` or `sk_test_xxx`)
|
|
55
|
+
4. Set it as `BRAINZLAB_SECRET_KEY` environment variable
|
|
56
|
+
|
|
57
|
+
**One key, all products**: Your Platform API key works across Recall, Reflex, Pulse, and all BrainzLab products. No separate keys needed.
|
|
58
|
+
|
|
50
59
|
### Configuration
|
|
51
60
|
|
|
52
61
|
```ruby
|
|
53
62
|
# config/initializers/brainzlab.rb
|
|
54
63
|
BrainzLab.configure do |config|
|
|
55
|
-
# Authentication (required)
|
|
64
|
+
# Authentication (required) - Your Platform API key
|
|
56
65
|
config.secret_key = ENV['BRAINZLAB_SECRET_KEY']
|
|
57
66
|
|
|
58
67
|
# Environment
|
|
@@ -289,6 +298,8 @@ end
|
|
|
289
298
|
| `BRAINZLAB_SERVICE` | Service name |
|
|
290
299
|
| `BRAINZLAB_APP_NAME` | App name for auto-provisioning |
|
|
291
300
|
| `BRAINZLAB_DEBUG` | Enable debug logging (`true`/`false`) |
|
|
301
|
+
| `BRAINZLAB_MODE` | SDK mode: `production` (default) or `development` (offline) |
|
|
302
|
+
| `BRAINZLAB_DEV_DB_PATH` | SQLite database path for development mode |
|
|
292
303
|
| `RECALL_URL` | Custom Recall endpoint |
|
|
293
304
|
| `REFLEX_URL` | Custom Reflex endpoint |
|
|
294
305
|
| `PULSE_URL` | Custom Pulse endpoint |
|
|
@@ -303,12 +314,217 @@ config.scrub_fields = [:password, :password_confirmation, :token, :api_key, :sec
|
|
|
303
314
|
|
|
304
315
|
## Debug Mode
|
|
305
316
|
|
|
306
|
-
|
|
317
|
+
Debug mode provides detailed visibility into SDK operations, including all API requests and responses with timing information. This is invaluable for troubleshooting integration issues.
|
|
318
|
+
|
|
319
|
+
### Enabling Debug Mode
|
|
320
|
+
|
|
321
|
+
```ruby
|
|
322
|
+
# In your initializer
|
|
323
|
+
BrainzLab.configure do |config|
|
|
324
|
+
config.debug = true
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# Or via environment variable
|
|
328
|
+
# BRAINZLAB_DEBUG=true
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Debug Output Format
|
|
332
|
+
|
|
333
|
+
When debug mode is enabled, you'll see colorized output in your terminal:
|
|
334
|
+
|
|
335
|
+
```
|
|
336
|
+
[BrainzLab] 12:34:56 -> Recall POST /api/v1/logs (count: 5)
|
|
337
|
+
[BrainzLab] 12:34:56 <- Recall 200 OK (45ms)
|
|
338
|
+
|
|
339
|
+
[BrainzLab] 12:34:57 -> Reflex POST /api/v1/errors (exception: RuntimeError)
|
|
340
|
+
[BrainzLab] 12:34:57 <- Reflex 201 Created (23ms)
|
|
341
|
+
|
|
342
|
+
[BrainzLab] 12:34:58 -> Pulse POST /api/v1/traces (name: GET /users)
|
|
343
|
+
[BrainzLab] 12:34:58 <- Pulse 200 OK (18ms)
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
The output includes:
|
|
347
|
+
- Timestamp for each operation
|
|
348
|
+
- Service name (Recall, Reflex, Pulse, etc.)
|
|
349
|
+
- Request method and path
|
|
350
|
+
- Payload summary (log count, exception type, etc.)
|
|
351
|
+
- Response status code and message
|
|
352
|
+
- Request duration with color coding (green < 100ms, yellow < 1s, red > 1s)
|
|
353
|
+
|
|
354
|
+
### Custom Logger
|
|
355
|
+
|
|
356
|
+
You can provide your own logger to capture debug output:
|
|
357
|
+
|
|
358
|
+
```ruby
|
|
359
|
+
BrainzLab.configure do |config|
|
|
360
|
+
config.debug = true
|
|
361
|
+
config.logger = Rails.logger
|
|
362
|
+
# Or any Logger-compatible object
|
|
363
|
+
config.logger = Logger.new('log/brainzlab.log')
|
|
364
|
+
end
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### Debug Callbacks
|
|
368
|
+
|
|
369
|
+
For advanced debugging and monitoring, you can hook into SDK operations:
|
|
370
|
+
|
|
371
|
+
```ruby
|
|
372
|
+
BrainzLab.configure do |config|
|
|
373
|
+
# Called before each API request
|
|
374
|
+
config.on_send = ->(service, method, path, payload) {
|
|
375
|
+
Rails.logger.debug "[BrainzLab] Sending to #{service}: #{method} #{path}"
|
|
376
|
+
|
|
377
|
+
# You can use this to:
|
|
378
|
+
# - Log all outgoing requests
|
|
379
|
+
# - Send metrics to your monitoring system
|
|
380
|
+
# - Add custom tracing
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
# Called when an SDK error occurs
|
|
384
|
+
config.on_error = ->(error, context) {
|
|
385
|
+
Rails.logger.error "[BrainzLab] Error in #{context[:service]}: #{error.message}"
|
|
386
|
+
|
|
387
|
+
# You can use this to:
|
|
388
|
+
# - Alert on SDK failures
|
|
389
|
+
# - Track error rates
|
|
390
|
+
# - Fallback to alternative logging
|
|
391
|
+
|
|
392
|
+
# Note: This is for SDK errors, not application errors
|
|
393
|
+
# Application errors are sent to Reflex as normal
|
|
394
|
+
}
|
|
395
|
+
end
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Programmatic Debug Logging
|
|
399
|
+
|
|
400
|
+
You can also use the Debug module directly:
|
|
401
|
+
|
|
402
|
+
```ruby
|
|
403
|
+
# Log a debug message (only outputs when debug=true)
|
|
404
|
+
BrainzLab::Debug.log("Custom message", level: :info)
|
|
405
|
+
BrainzLab::Debug.log("Something went wrong", level: :error, error_code: 500)
|
|
406
|
+
|
|
407
|
+
# Measure operation timing
|
|
408
|
+
BrainzLab::Debug.measure(:custom, "expensive_operation") do
|
|
409
|
+
# Your code here
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# Check if debug mode is enabled
|
|
413
|
+
if BrainzLab::Debug.enabled?
|
|
414
|
+
# Perform additional debug operations
|
|
415
|
+
end
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Debug Output Levels
|
|
419
|
+
|
|
420
|
+
Debug messages are color-coded by level:
|
|
421
|
+
- **DEBUG** (gray) - Verbose internal operations
|
|
422
|
+
- **INFO** (cyan) - Normal operations
|
|
423
|
+
- **WARN** (yellow) - Potential issues
|
|
424
|
+
- **ERROR** (red) - Failed operations
|
|
425
|
+
|
|
426
|
+
## Development Mode
|
|
427
|
+
|
|
428
|
+
Development mode allows you to use the SDK without a BrainzLab server connection. Events are logged to stdout in a readable format and stored locally in a SQLite database.
|
|
429
|
+
|
|
430
|
+
### Configuration
|
|
431
|
+
|
|
432
|
+
```ruby
|
|
433
|
+
# config/initializers/brainzlab.rb
|
|
434
|
+
BrainzLab.configure do |config|
|
|
435
|
+
# Enable development mode (works offline)
|
|
436
|
+
config.mode = :development
|
|
437
|
+
|
|
438
|
+
# Optional: customize the SQLite database path (default: tmp/brainzlab.sqlite3)
|
|
439
|
+
config.development_db_path = 'tmp/brainzlab_dev.sqlite3'
|
|
440
|
+
|
|
441
|
+
# Other settings still apply
|
|
442
|
+
config.environment = Rails.env
|
|
443
|
+
config.service = 'my-app'
|
|
444
|
+
end
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
Or use the environment variable:
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
export BRAINZLAB_MODE=development
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Features
|
|
454
|
+
|
|
455
|
+
In development mode:
|
|
456
|
+
|
|
457
|
+
- **No server connection required** - Works completely offline
|
|
458
|
+
- **Stdout logging** - All events are pretty-printed to the console with colors
|
|
459
|
+
- **Local storage** - Events are stored in SQLite at `tmp/brainzlab.sqlite3`
|
|
460
|
+
- **Queryable** - Use `BrainzLab.development_events` to query stored events
|
|
461
|
+
|
|
462
|
+
### Querying Events
|
|
307
463
|
|
|
308
464
|
```ruby
|
|
309
|
-
|
|
310
|
-
|
|
465
|
+
# Get all events
|
|
466
|
+
events = BrainzLab.development_events
|
|
467
|
+
|
|
468
|
+
# Filter by service
|
|
469
|
+
logs = BrainzLab.development_events(service: :recall)
|
|
470
|
+
errors = BrainzLab.development_events(service: :reflex)
|
|
471
|
+
traces = BrainzLab.development_events(service: :pulse)
|
|
472
|
+
|
|
473
|
+
# Filter by event type
|
|
474
|
+
BrainzLab.development_events(event_type: 'log')
|
|
475
|
+
BrainzLab.development_events(event_type: 'error')
|
|
476
|
+
BrainzLab.development_events(event_type: 'trace')
|
|
477
|
+
|
|
478
|
+
# Filter by time
|
|
479
|
+
BrainzLab.development_events(since: 1.hour.ago)
|
|
480
|
+
|
|
481
|
+
# Limit results
|
|
482
|
+
BrainzLab.development_events(limit: 10)
|
|
483
|
+
|
|
484
|
+
# Combine filters
|
|
485
|
+
BrainzLab.development_events(
|
|
486
|
+
service: :recall,
|
|
487
|
+
since: 30.minutes.ago,
|
|
488
|
+
limit: 50
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
# Get stats by service
|
|
492
|
+
BrainzLab.development_stats
|
|
493
|
+
# => { recall: 42, reflex: 3, pulse: 15 }
|
|
494
|
+
|
|
495
|
+
# Clear all stored events
|
|
496
|
+
BrainzLab.clear_development_events!
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### Console Output
|
|
500
|
+
|
|
501
|
+
In development mode, events are pretty-printed to stdout:
|
|
502
|
+
|
|
311
503
|
```
|
|
504
|
+
[14:32:15.123] [RECALL] log [INFO] User signed up
|
|
505
|
+
user_id: 123
|
|
506
|
+
data: {email: "user@example.com"}
|
|
507
|
+
|
|
508
|
+
[14:32:16.456] [REFLEX] error RuntimeError: Something went wrong
|
|
509
|
+
error_class: "RuntimeError"
|
|
510
|
+
environment: "development"
|
|
511
|
+
request_id: "abc-123"
|
|
512
|
+
|
|
513
|
+
[14:32:17.789] [PULSE] trace GET /users (45.2ms)
|
|
514
|
+
request_method: "GET"
|
|
515
|
+
request_path: "/users"
|
|
516
|
+
status: 200
|
|
517
|
+
db_ms: 12.3
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### Use Cases
|
|
521
|
+
|
|
522
|
+
Development mode is useful for:
|
|
523
|
+
|
|
524
|
+
- **Local development** without setting up a BrainzLab account
|
|
525
|
+
- **Testing** SDK integration in CI/CD pipelines
|
|
526
|
+
- **Debugging** to inspect exactly what events would be sent
|
|
527
|
+
- **Offline development** when working without internet access
|
|
312
528
|
|
|
313
529
|
## Self-Hosted
|
|
314
530
|
|
|
@@ -200,7 +200,27 @@ module BrainzLab
|
|
|
200
200
|
end
|
|
201
201
|
|
|
202
202
|
def log_error(operation, error)
|
|
203
|
-
|
|
203
|
+
structured_error = ErrorHandler.wrap(error, service: 'Beacon', operation: operation)
|
|
204
|
+
BrainzLab.debug_log("[Beacon::Client] #{operation} failed: #{structured_error.message}")
|
|
205
|
+
|
|
206
|
+
# Call on_error callback if configured
|
|
207
|
+
if @config.on_error
|
|
208
|
+
@config.on_error.call(structured_error, { service: 'Beacon', operation: operation })
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def handle_response_error(response, operation)
|
|
213
|
+
return if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPCreated) || response.is_a?(Net::HTTPNoContent)
|
|
214
|
+
|
|
215
|
+
structured_error = ErrorHandler.from_response(response, service: 'Beacon', operation: operation)
|
|
216
|
+
BrainzLab.debug_log("[Beacon::Client] #{operation} failed: #{structured_error.message}")
|
|
217
|
+
|
|
218
|
+
# Call on_error callback if configured
|
|
219
|
+
if @config.on_error
|
|
220
|
+
@config.on_error.call(structured_error, { service: 'Beacon', operation: operation })
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
structured_error
|
|
204
224
|
end
|
|
205
225
|
end
|
|
206
226
|
end
|
|
@@ -7,6 +7,9 @@ module BrainzLab
|
|
|
7
7
|
# recall_min_level has a custom setter with validation
|
|
8
8
|
attr_reader :recall_min_level
|
|
9
9
|
|
|
10
|
+
# mode has a custom setter with validation
|
|
11
|
+
attr_reader :mode
|
|
12
|
+
|
|
10
13
|
attr_accessor :secret_key,
|
|
11
14
|
:environment,
|
|
12
15
|
:service,
|
|
@@ -106,6 +109,8 @@ module BrainzLab
|
|
|
106
109
|
:synapse_auto_provision,
|
|
107
110
|
:scrub_fields,
|
|
108
111
|
:logger,
|
|
112
|
+
:on_error,
|
|
113
|
+
:on_send,
|
|
109
114
|
:instrument_http,
|
|
110
115
|
:instrument_active_record,
|
|
111
116
|
:instrument_redis,
|
|
@@ -150,7 +155,9 @@ module BrainzLab
|
|
|
150
155
|
:devtools_asset_path,
|
|
151
156
|
:devtools_panel_position,
|
|
152
157
|
:devtools_expand_by_default,
|
|
153
|
-
:rails_instrumentation_handled_externally
|
|
158
|
+
:rails_instrumentation_handled_externally,
|
|
159
|
+
:development_db_path,
|
|
160
|
+
:development_log_output
|
|
154
161
|
|
|
155
162
|
# Services that should not track themselves to avoid circular dependencies
|
|
156
163
|
SELF_TRACKING_SERVICES = {
|
|
@@ -180,6 +187,13 @@ module BrainzLab
|
|
|
180
187
|
# Debug mode - enables verbose logging
|
|
181
188
|
@debug = ENV['BRAINZLAB_DEBUG'] == 'true'
|
|
182
189
|
|
|
190
|
+
# SDK mode - :production (default) or :development (offline, local storage)
|
|
191
|
+
@mode = ENV['BRAINZLAB_MODE']&.to_sym || :production
|
|
192
|
+
|
|
193
|
+
# Development mode settings
|
|
194
|
+
@development_db_path = ENV['BRAINZLAB_DEV_DB_PATH'] || 'tmp/brainzlab.sqlite3'
|
|
195
|
+
@development_log_output = $stdout
|
|
196
|
+
|
|
183
197
|
# Disable self-tracking - prevents services from tracking to themselves
|
|
184
198
|
# e.g., Recall won't log to itself, Reflex won't track errors to itself
|
|
185
199
|
@disable_self_tracking = ENV.fetch('BRAINZLAB_DISABLE_SELF_TRACKING', 'true') == 'true'
|
|
@@ -306,6 +320,12 @@ module BrainzLab
|
|
|
306
320
|
# Internal logger for debugging SDK issues
|
|
307
321
|
@logger = nil
|
|
308
322
|
|
|
323
|
+
# Debug callbacks
|
|
324
|
+
# Called when an SDK error occurs (lambda/proc receiving error object and context hash)
|
|
325
|
+
@on_error = nil
|
|
326
|
+
# Called before each API request (lambda/proc receiving service, method, path, and payload)
|
|
327
|
+
@on_send = nil
|
|
328
|
+
|
|
309
329
|
# Instrumentation
|
|
310
330
|
@instrument_http = true # Enable HTTP client instrumentation (Net::HTTP, Faraday, HTTParty)
|
|
311
331
|
@instrument_active_record = true # AR breadcrumbs for Reflex
|
|
@@ -363,11 +383,40 @@ module BrainzLab
|
|
|
363
383
|
|
|
364
384
|
def recall_min_level=(level)
|
|
365
385
|
level = level.to_sym
|
|
366
|
-
|
|
386
|
+
unless LEVELS.include?(level)
|
|
387
|
+
raise ValidationError.new(
|
|
388
|
+
"Invalid log level: #{level}",
|
|
389
|
+
hint: "Valid log levels are: #{LEVELS.join(', ')}",
|
|
390
|
+
code: 'invalid_log_level',
|
|
391
|
+
field: 'recall_min_level',
|
|
392
|
+
context: { provided: level, valid_values: LEVELS }
|
|
393
|
+
)
|
|
394
|
+
end
|
|
367
395
|
|
|
368
396
|
@recall_min_level = level
|
|
369
397
|
end
|
|
370
398
|
|
|
399
|
+
MODES = %i[production development].freeze
|
|
400
|
+
|
|
401
|
+
def mode=(mode)
|
|
402
|
+
mode = mode.to_sym
|
|
403
|
+
unless MODES.include?(mode)
|
|
404
|
+
raise ValidationError.new(
|
|
405
|
+
"Invalid mode: #{mode}",
|
|
406
|
+
hint: "Valid modes are: #{MODES.join(', ')}. Use :development for offline mode with local storage.",
|
|
407
|
+
code: 'invalid_mode',
|
|
408
|
+
field: 'mode',
|
|
409
|
+
context: { provided: mode, valid_values: MODES }
|
|
410
|
+
)
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
@mode = mode
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
def development_mode?
|
|
417
|
+
@mode == :development
|
|
418
|
+
end
|
|
419
|
+
|
|
371
420
|
def level_enabled?(level)
|
|
372
421
|
LEVELS.index(level.to_sym) >= LEVELS.index(@recall_min_level)
|
|
373
422
|
end
|
|
@@ -132,7 +132,27 @@ module BrainzLab
|
|
|
132
132
|
end
|
|
133
133
|
|
|
134
134
|
def log_error(operation, error)
|
|
135
|
-
|
|
135
|
+
structured_error = ErrorHandler.wrap(error, service: 'Cortex', operation: operation)
|
|
136
|
+
BrainzLab.debug_log("[Cortex::Client] #{operation} failed: #{structured_error.message}")
|
|
137
|
+
|
|
138
|
+
# Call on_error callback if configured
|
|
139
|
+
if @config.on_error
|
|
140
|
+
@config.on_error.call(structured_error, { service: 'Cortex', operation: operation })
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def handle_response_error(response, operation)
|
|
145
|
+
return if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPCreated) || response.is_a?(Net::HTTPNoContent)
|
|
146
|
+
|
|
147
|
+
structured_error = ErrorHandler.from_response(response, service: 'Cortex', operation: operation)
|
|
148
|
+
BrainzLab.debug_log("[Cortex::Client] #{operation} failed: #{structured_error.message}")
|
|
149
|
+
|
|
150
|
+
# Call on_error callback if configured
|
|
151
|
+
if @config.on_error
|
|
152
|
+
@config.on_error.call(structured_error, { service: 'Cortex', operation: operation })
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
structured_error
|
|
136
156
|
end
|
|
137
157
|
end
|
|
138
158
|
end
|