rapitapir 0.1.1 → 2.0.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -7
- data/.rubocop_todo.yml +83 -0
- data/README.md +1319 -235
- data/RUBY_WEEKLY_LAUNCH_POST.md +219 -0
- data/docs/RAILS_INTEGRATION_IMPLEMENTATION.md +209 -0
- data/docs/SINATRA_EXTENSION.md +399 -348
- data/docs/STRICT_VALIDATION.md +229 -0
- data/docs/VALIDATION_IMPROVEMENTS.md +218 -0
- data/docs/ai-integration-plan.md +112 -0
- data/docs/auto-derivation.md +505 -92
- data/docs/endpoint-definition.md +536 -129
- data/docs/n8n-integration.md +212 -0
- data/docs/observability.md +810 -500
- data/docs/using-mcp.md +93 -0
- data/examples/ai/knowledge_base_rag.rb +83 -0
- data/examples/ai/user_management_mcp.rb +92 -0
- data/examples/ai/user_validation_llm.rb +187 -0
- data/examples/rails/RAILS_8_GUIDE.md +165 -0
- data/examples/rails/RAILS_LOADING_FIX.rb +35 -0
- data/examples/rails/README.md +497 -0
- data/examples/rails/comprehensive_test.rb +91 -0
- data/examples/rails/config/routes.rb +48 -0
- data/examples/rails/debug_controller.rb +63 -0
- data/examples/rails/detailed_test.rb +46 -0
- data/examples/rails/enhanced_users_controller.rb +278 -0
- data/examples/rails/final_server_test.rb +50 -0
- data/examples/rails/hello_world_app.rb +116 -0
- data/examples/rails/hello_world_controller.rb +186 -0
- data/examples/rails/hello_world_routes.rb +28 -0
- data/examples/rails/rails8_minimal_demo.rb +132 -0
- data/examples/rails/rails8_simple_demo.rb +140 -0
- data/examples/rails/rails8_working_demo.rb +255 -0
- data/examples/rails/real_world_blog_api.rb +510 -0
- data/examples/rails/server_test.rb +46 -0
- data/examples/rails/test_direct_processing.rb +41 -0
- data/examples/rails/test_hello_world.rb +80 -0
- data/examples/rails/test_rails_integration.rb +54 -0
- data/examples/rails/traditional_app/Gemfile +37 -0
- data/examples/rails/traditional_app/README.md +265 -0
- data/examples/rails/traditional_app/app/controllers/api/v1/posts_controller.rb +254 -0
- data/examples/rails/traditional_app/app/controllers/api/v1/users_controller.rb +220 -0
- data/examples/rails/traditional_app/app/controllers/application_controller.rb +86 -0
- data/examples/rails/traditional_app/app/controllers/application_controller_simplified.rb +87 -0
- data/examples/rails/traditional_app/app/controllers/documentation_controller.rb +149 -0
- data/examples/rails/traditional_app/app/controllers/health_controller.rb +42 -0
- data/examples/rails/traditional_app/config/routes.rb +25 -0
- data/examples/rails/traditional_app/config/routes_best_practice.rb +25 -0
- data/examples/rails/traditional_app/config/routes_simplified.rb +36 -0
- data/examples/rails/traditional_app_runnable.rb +406 -0
- data/examples/rails/users_controller.rb +4 -1
- data/examples/serverless/Gemfile +43 -0
- data/examples/serverless/QUICKSTART.md +331 -0
- data/examples/serverless/README.md +520 -0
- data/examples/serverless/aws_lambda_example.rb +307 -0
- data/examples/serverless/aws_sam_template.yaml +215 -0
- data/examples/serverless/azure_functions_example.rb +407 -0
- data/examples/serverless/deploy.rb +204 -0
- data/examples/serverless/gcp_cloud_functions_example.rb +367 -0
- data/examples/serverless/gcp_function.yaml +23 -0
- data/examples/serverless/host.json +24 -0
- data/examples/serverless/package.json +32 -0
- data/examples/serverless/spec/aws_lambda_spec.rb +196 -0
- data/examples/serverless/spec/spec_helper.rb +89 -0
- data/examples/serverless/vercel.json +31 -0
- data/examples/serverless/vercel_example.rb +404 -0
- data/examples/strict_validation_examples.rb +104 -0
- data/examples/validation_error_examples.rb +173 -0
- data/lib/rapitapir/ai/llm_instruction.rb +456 -0
- data/lib/rapitapir/ai/mcp.rb +134 -0
- data/lib/rapitapir/ai/rag.rb +287 -0
- data/lib/rapitapir/ai/rag_middleware.rb +147 -0
- data/lib/rapitapir/auth/oauth2.rb +43 -57
- data/lib/rapitapir/cli/command.rb +362 -2
- data/lib/rapitapir/cli/mcp_export.rb +18 -0
- data/lib/rapitapir/cli/validator.rb +2 -6
- data/lib/rapitapir/core/endpoint.rb +59 -6
- data/lib/rapitapir/core/enhanced_endpoint.rb +2 -6
- data/lib/rapitapir/dsl/fluent_endpoint_builder.rb +53 -0
- data/lib/rapitapir/endpoint_registry.rb +47 -0
- data/lib/rapitapir/observability/health_check.rb +4 -4
- data/lib/rapitapir/observability/logging.rb +10 -10
- data/lib/rapitapir/schema.rb +2 -2
- data/lib/rapitapir/server/rack_adapter.rb +1 -3
- data/lib/rapitapir/server/rails/configuration.rb +77 -0
- data/lib/rapitapir/server/rails/controller_base.rb +185 -0
- data/lib/rapitapir/server/rails/documentation_helpers.rb +76 -0
- data/lib/rapitapir/server/rails/resource_builder.rb +181 -0
- data/lib/rapitapir/server/rails/routes.rb +114 -0
- data/lib/rapitapir/server/rails_adapter.rb +10 -3
- data/lib/rapitapir/server/rails_adapter_class.rb +1 -3
- data/lib/rapitapir/server/rails_controller.rb +1 -3
- data/lib/rapitapir/server/rails_integration.rb +67 -0
- data/lib/rapitapir/server/rails_response_handler.rb +16 -3
- data/lib/rapitapir/server/sinatra_adapter.rb +29 -5
- data/lib/rapitapir/server/sinatra_integration.rb +4 -4
- data/lib/rapitapir/sinatra/extension.rb +2 -2
- data/lib/rapitapir/sinatra/oauth2_helpers.rb +34 -40
- data/lib/rapitapir/types/array.rb +4 -0
- data/lib/rapitapir/types/auto_derivation.rb +4 -18
- data/lib/rapitapir/types/datetime.rb +1 -3
- data/lib/rapitapir/types/float.rb +2 -6
- data/lib/rapitapir/types/hash.rb +40 -2
- data/lib/rapitapir/types/integer.rb +4 -12
- data/lib/rapitapir/types/object.rb +6 -2
- data/lib/rapitapir/types.rb +6 -2
- data/lib/rapitapir/version.rb +1 -1
- data/lib/rapitapir.rb +5 -3
- data/rapitapir.gemspec +7 -5
- metadata +116 -16
@@ -0,0 +1,229 @@
|
|
1
|
+
# Strict Validation by Default - Implementation Summary
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
This document outlines the implementation of strict validation as the default behavior for RapiTapir v2.0 hash schemas, addressing security concerns about unexpected fields in API payloads.
|
6
|
+
|
7
|
+
## Problem Statement
|
8
|
+
|
9
|
+
**Before**: RapiTapir allowed additional properties in hash schemas by default:
|
10
|
+
```ruby
|
11
|
+
# This would succeed even with extra fields
|
12
|
+
BOOK_SCHEMA = RapiTapir::Types.hash({
|
13
|
+
'title' => RapiTapir::Types.string,
|
14
|
+
'author' => RapiTapir::Types.string
|
15
|
+
})
|
16
|
+
|
17
|
+
# Request with extra fields would be accepted
|
18
|
+
{
|
19
|
+
"title": "Book Title",
|
20
|
+
"author": "Author Name",
|
21
|
+
"malicious_field": "unexpected_data" # ❌ Should be rejected
|
22
|
+
}
|
23
|
+
```
|
24
|
+
|
25
|
+
**Security Issues**:
|
26
|
+
- Data leakage through unvalidated fields
|
27
|
+
- Potential injection attacks via unexpected parameters
|
28
|
+
- API contract violations going unnoticed
|
29
|
+
- Difficulty in maintaining data integrity
|
30
|
+
|
31
|
+
## Solution Implemented
|
32
|
+
|
33
|
+
### 1. Changed Default Behavior
|
34
|
+
|
35
|
+
**File**: `lib/rapitapir/types/hash.rb`
|
36
|
+
|
37
|
+
**Change**: Modified default `additional_properties` from `true` to `false`
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
# Before
|
41
|
+
def initialize(field_types = {}, additional_properties: true, **options)
|
42
|
+
|
43
|
+
# After
|
44
|
+
def initialize(field_types = {}, additional_properties: false, **options)
|
45
|
+
```
|
46
|
+
|
47
|
+
### 2. Added Coercion-Time Validation
|
48
|
+
|
49
|
+
**Added Method**: `validate_no_unexpected_fields`
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
def validate_no_unexpected_fields(value)
|
53
|
+
expected_keys = field_types.keys.map { |k| [k, k.to_s, k.to_sym] }.flatten.uniq
|
54
|
+
unexpected_keys = value.keys - expected_keys
|
55
|
+
return if unexpected_keys.empty?
|
56
|
+
|
57
|
+
unexpected_list = unexpected_keys.map(&:inspect).join(', ')
|
58
|
+
raise CoercionError.new(value, 'Hash', "Unexpected fields in hash: #{unexpected_list}. Only these fields are allowed: #{field_types.keys.join(', ')}")
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
**Integration**: Added check in `coerce_hash_value` method:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
def coerce_hash_value(value)
|
66
|
+
coerced = {}
|
67
|
+
|
68
|
+
# Check for unexpected fields if additional properties are not allowed
|
69
|
+
validate_no_unexpected_fields(value) unless constraints[:additional_properties]
|
70
|
+
|
71
|
+
# ... rest of coercion
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
### 3. Added Explicit Opt-in for Flexible Schemas
|
76
|
+
|
77
|
+
**File**: `lib/rapitapir/types.rb`
|
78
|
+
|
79
|
+
**New Method**: `open_hash` for when additional properties are needed
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
def self.open_hash(field_types = {}, **options)
|
83
|
+
Hash.new(field_types, additional_properties: true, **options)
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
## Usage Examples
|
88
|
+
|
89
|
+
### Strict Validation (Default)
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
# Strict by default - rejects unexpected fields
|
93
|
+
STRICT_SCHEMA = RapiTapir::Types.hash({
|
94
|
+
'name' => RapiTapir::Types.string,
|
95
|
+
'email' => RapiTapir::Types.email
|
96
|
+
})
|
97
|
+
|
98
|
+
# ✅ Valid request
|
99
|
+
{ "name": "John", "email": "john@example.com" }
|
100
|
+
|
101
|
+
# ❌ Rejected request
|
102
|
+
{
|
103
|
+
"name": "John",
|
104
|
+
"email": "john@example.com",
|
105
|
+
"extra_field": "unexpected" # Causes validation error
|
106
|
+
}
|
107
|
+
```
|
108
|
+
|
109
|
+
### Open Validation (Explicit Opt-in)
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
# Explicitly allow additional properties
|
113
|
+
FLEXIBLE_SCHEMA = RapiTapir::Types.open_hash({
|
114
|
+
'name' => RapiTapir::Types.string,
|
115
|
+
'email' => RapiTapir::Types.email
|
116
|
+
})
|
117
|
+
|
118
|
+
# ✅ Valid request with extra fields
|
119
|
+
{
|
120
|
+
"name": "John",
|
121
|
+
"email": "john@example.com",
|
122
|
+
"custom_field": "allowed" # Accepted
|
123
|
+
}
|
124
|
+
```
|
125
|
+
|
126
|
+
## Error Messages
|
127
|
+
|
128
|
+
### Before
|
129
|
+
```json
|
130
|
+
{
|
131
|
+
"error": "Internal Server Error",
|
132
|
+
"message": "Generic error message"
|
133
|
+
}
|
134
|
+
```
|
135
|
+
|
136
|
+
### After
|
137
|
+
```json
|
138
|
+
{
|
139
|
+
"error": "Validation Error",
|
140
|
+
"message": "Unexpected fields in hash: \"extra_field\", \"another_field\". Only these fields are allowed: title, author, isbn, published",
|
141
|
+
"field": null,
|
142
|
+
"value": {...},
|
143
|
+
"expected_type": "Hash"
|
144
|
+
}
|
145
|
+
```
|
146
|
+
|
147
|
+
## Security Benefits
|
148
|
+
|
149
|
+
### 🔒 **Enhanced Security**
|
150
|
+
- **Prevents data injection**: Unexpected fields are rejected at the validation layer
|
151
|
+
- **Reduces attack surface**: Limits what data can be passed to application logic
|
152
|
+
- **Contract enforcement**: Ensures API only accepts explicitly defined data
|
153
|
+
|
154
|
+
### 🛡️ **Data Integrity**
|
155
|
+
- **Schema compliance**: Guarantees data matches expected structure
|
156
|
+
- **Prevents pollution**: Stops unvalidated data from entering the system
|
157
|
+
- **Clear boundaries**: Explicit definition of acceptable input
|
158
|
+
|
159
|
+
### 📋 **Developer Experience**
|
160
|
+
- **Clear errors**: Specific messages about which fields are unexpected
|
161
|
+
- **Explicit intent**: Developers must consciously choose to allow extra fields
|
162
|
+
- **Better debugging**: Easier to track down validation issues
|
163
|
+
|
164
|
+
## When to Use Each Approach
|
165
|
+
|
166
|
+
### Use Strict Validation (Default) For:
|
167
|
+
- ✅ **Production APIs** - Maximum security and data integrity
|
168
|
+
- ✅ **User input forms** - Prevent form tampering
|
169
|
+
- ✅ **Payment/financial data** - Critical data validation
|
170
|
+
- ✅ **Authentication payloads** - Security-sensitive endpoints
|
171
|
+
- ✅ **Most API endpoints** - Default choice for better security
|
172
|
+
|
173
|
+
### Use Open Validation (`open_hash`) For:
|
174
|
+
- 🌐 **Webhook payloads** - Third-party services with varying data
|
175
|
+
- 🔧 **Configuration objects** - User-defined custom fields
|
176
|
+
- 📦 **Migration endpoints** - Backward compatibility needs
|
177
|
+
- 🔄 **Proxy/transformation APIs** - Pass-through scenarios
|
178
|
+
- 📊 **Analytics events** - Variable event properties
|
179
|
+
|
180
|
+
## Backward Compatibility
|
181
|
+
|
182
|
+
### Breaking Change Considerations
|
183
|
+
- **Default behavior changed**: Existing code may need updates
|
184
|
+
- **Migration path**: Replace `Types.hash()` with `Types.open_hash()` where needed
|
185
|
+
- **Gradual adoption**: Can be implemented endpoint by endpoint
|
186
|
+
|
187
|
+
### Recommended Migration Strategy
|
188
|
+
1. **Audit existing schemas**: Identify which endpoints need flexible validation
|
189
|
+
2. **Update specific cases**: Change to `open_hash()` only where required
|
190
|
+
3. **Test thoroughly**: Verify all endpoints work with strict validation
|
191
|
+
4. **Monitor errors**: Watch for unexpected field rejections in production
|
192
|
+
|
193
|
+
## Testing
|
194
|
+
|
195
|
+
All implementations have been tested with:
|
196
|
+
- ✅ Unit tests for strict validation behavior
|
197
|
+
- ✅ Integration tests with Sinatra adapter
|
198
|
+
- ✅ Error message format verification
|
199
|
+
- ✅ Backward compatibility checks
|
200
|
+
- ✅ Performance impact assessment
|
201
|
+
|
202
|
+
## Files Modified
|
203
|
+
|
204
|
+
1. **`lib/rapitapir/types/hash.rb`**
|
205
|
+
- Changed default `additional_properties: false`
|
206
|
+
- Added `validate_no_unexpected_fields` method
|
207
|
+
- Enhanced coercion-time validation
|
208
|
+
|
209
|
+
2. **`lib/rapitapir/types.rb`**
|
210
|
+
- Added `open_hash()` factory method
|
211
|
+
- Maintained existing `hash()` method with new defaults
|
212
|
+
|
213
|
+
3. **`examples/strict_validation_examples.rb`**
|
214
|
+
- Comprehensive usage examples
|
215
|
+
- Security benefit demonstrations
|
216
|
+
|
217
|
+
## Impact Assessment
|
218
|
+
|
219
|
+
### Performance
|
220
|
+
- **Minimal overhead**: Additional validation only when needed
|
221
|
+
- **Early rejection**: Fails fast on invalid data
|
222
|
+
- **Efficient checks**: Simple key comparison operations
|
223
|
+
|
224
|
+
### Security Posture
|
225
|
+
- **Significantly improved**: Default-secure behavior
|
226
|
+
- **Reduced risk**: Fewer attack vectors through unexpected data
|
227
|
+
- **Compliance friendly**: Better for regulatory requirements
|
228
|
+
|
229
|
+
This implementation makes RapiTapir more secure by default while maintaining flexibility for specific use cases that require it.
|
@@ -0,0 +1,218 @@
|
|
1
|
+
# Validation Error Improvements Summary
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
This document summarizes the comprehensive validation error improvements made to RapiTapir v2.0, addressing the user feedback about unclear error messages when API validation fails.
|
6
|
+
|
7
|
+
## Problem Statement
|
8
|
+
|
9
|
+
**Before**: When a required field was missing from an API request, RapiTapir returned a generic error:
|
10
|
+
```json
|
11
|
+
{
|
12
|
+
"error": "Internal Server Error",
|
13
|
+
"message": "Cannot coerce nil to RapiTapir::Types::String: Required value cannot be nil"
|
14
|
+
}
|
15
|
+
```
|
16
|
+
|
17
|
+
This error message didn't tell developers:
|
18
|
+
- Which field was missing
|
19
|
+
- What the expected format was
|
20
|
+
- How to fix the issue
|
21
|
+
|
22
|
+
## Solution Implemented
|
23
|
+
|
24
|
+
### 1. Enhanced Hash Type Validation
|
25
|
+
|
26
|
+
**File**: `lib/rapitapir/types/hash.rb`
|
27
|
+
|
28
|
+
**Changes**:
|
29
|
+
- Added specific missing field detection in `coerce_defined_fields`
|
30
|
+
- Enhanced error messages with field context in validation
|
31
|
+
- Wrapped field coercion errors with field names
|
32
|
+
|
33
|
+
**Before**:
|
34
|
+
```ruby
|
35
|
+
def coerce_defined_fields(value, coerced)
|
36
|
+
field_types.each do |field_name, field_type|
|
37
|
+
field_value = find_field_value(value, field_name)
|
38
|
+
coerced[field_name] = field_type.coerce(field_value) if field_value || !field_type.optional?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
**After**:
|
44
|
+
```ruby
|
45
|
+
def coerce_defined_fields(value, coerced)
|
46
|
+
field_types.each do |field_name, field_type|
|
47
|
+
field_value = find_field_value(value, field_name)
|
48
|
+
|
49
|
+
# Check for missing required fields
|
50
|
+
if field_value.nil? && !field_type.optional?
|
51
|
+
raise CoercionError.new(nil, field_type.class.name, "Required field '#{field_name}' is missing from hash")
|
52
|
+
end
|
53
|
+
|
54
|
+
# Only coerce if we have a value or field is optional
|
55
|
+
if field_value || !field_type.optional?
|
56
|
+
begin
|
57
|
+
coerced[field_name] = field_type.coerce(field_value)
|
58
|
+
rescue CoercionError => e
|
59
|
+
# Re-raise with field context
|
60
|
+
raise CoercionError.new(e.value, e.type, "Field '#{field_name}': #{e.reason}")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
### 2. Improved Sinatra Error Handling
|
68
|
+
|
69
|
+
**File**: `lib/rapitapir/server/sinatra_adapter.rb`
|
70
|
+
|
71
|
+
**Changes**:
|
72
|
+
- Added specific catch blocks for `CoercionError` and `ValidationError`
|
73
|
+
- Enhanced error response format with field context
|
74
|
+
- Added detailed error information for better debugging
|
75
|
+
|
76
|
+
**Before**:
|
77
|
+
```ruby
|
78
|
+
rescue ArgumentError => e
|
79
|
+
error_response(400, e.message)
|
80
|
+
rescue StandardError => e
|
81
|
+
error_response(500, 'Internal Server Error', e.message)
|
82
|
+
```
|
83
|
+
|
84
|
+
**After**:
|
85
|
+
```ruby
|
86
|
+
rescue RapiTapir::Types::CoercionError => e
|
87
|
+
detailed_error_response(400, 'Validation Error', e.reason, {
|
88
|
+
field: extract_field_from_error(e),
|
89
|
+
value: e.value,
|
90
|
+
expected_type: e.type
|
91
|
+
})
|
92
|
+
rescue RapiTapir::Types::ValidationError => e
|
93
|
+
detailed_error_response(400, 'Validation Error', e.message, {
|
94
|
+
errors: e.errors,
|
95
|
+
value: e.value,
|
96
|
+
expected_type: e.type.to_s
|
97
|
+
})
|
98
|
+
```
|
99
|
+
|
100
|
+
### 3. Enhanced Error Response Format
|
101
|
+
|
102
|
+
**New Response Structure**:
|
103
|
+
```json
|
104
|
+
{
|
105
|
+
"error": "Validation Error",
|
106
|
+
"message": "Required field 'isbn' is missing from hash",
|
107
|
+
"field": "isbn",
|
108
|
+
"value": null,
|
109
|
+
"expected_type": "RapiTapir::Types::String"
|
110
|
+
}
|
111
|
+
```
|
112
|
+
|
113
|
+
## Results
|
114
|
+
|
115
|
+
### Before vs After Comparison
|
116
|
+
|
117
|
+
**Scenario**: Missing `isbn` field in book creation request
|
118
|
+
|
119
|
+
**Before**:
|
120
|
+
```json
|
121
|
+
{
|
122
|
+
"error": "Internal Server Error",
|
123
|
+
"message": "Cannot coerce nil to RapiTapir::Types::String: Required value cannot be nil"
|
124
|
+
}
|
125
|
+
```
|
126
|
+
|
127
|
+
**After**:
|
128
|
+
```json
|
129
|
+
{
|
130
|
+
"error": "Validation Error",
|
131
|
+
"message": "Required field 'isbn' is missing from hash",
|
132
|
+
"field": "isbn",
|
133
|
+
"value": null,
|
134
|
+
"expected_type": "RapiTapir::Types::String"
|
135
|
+
}
|
136
|
+
```
|
137
|
+
|
138
|
+
### Types of Improved Error Messages
|
139
|
+
|
140
|
+
1. **Missing Required Fields**:
|
141
|
+
```json
|
142
|
+
{
|
143
|
+
"error": "Validation Error",
|
144
|
+
"message": "Required field 'email' is missing from hash",
|
145
|
+
"field": "email",
|
146
|
+
"value": null,
|
147
|
+
"expected_type": "RapiTapir::Types::Email"
|
148
|
+
}
|
149
|
+
```
|
150
|
+
|
151
|
+
2. **Type Coercion Failures**:
|
152
|
+
```json
|
153
|
+
{
|
154
|
+
"error": "Validation Error",
|
155
|
+
"message": "Field 'published': Cannot convert 'not a boolean' to boolean",
|
156
|
+
"field": "published",
|
157
|
+
"value": "not a boolean",
|
158
|
+
"expected_type": "Boolean"
|
159
|
+
}
|
160
|
+
```
|
161
|
+
|
162
|
+
3. **Nested Field Errors**:
|
163
|
+
```json
|
164
|
+
{
|
165
|
+
"error": "Validation Error",
|
166
|
+
"message": "Field 'profile': Required field 'newsletter' is missing from hash",
|
167
|
+
"field": "profile",
|
168
|
+
"value": null,
|
169
|
+
"expected_type": "RapiTapir::Types::Boolean"
|
170
|
+
}
|
171
|
+
```
|
172
|
+
|
173
|
+
## Benefits
|
174
|
+
|
175
|
+
### 🎯 **Developer Experience**
|
176
|
+
- **Specific field identification**: Developers immediately know which field has the issue
|
177
|
+
- **Clear error context**: Understanding whether field is missing vs. invalid format
|
178
|
+
- **Actionable feedback**: Error messages suggest what needs to be fixed
|
179
|
+
|
180
|
+
### 🔧 **API Debugging**
|
181
|
+
- **Faster development cycles**: Less time spent debugging validation issues
|
182
|
+
- **Better error logs**: More informative server logs for troubleshooting
|
183
|
+
- **Client-side handling**: Frontend can display field-specific error messages
|
184
|
+
|
185
|
+
### 🚀 **Production Benefits**
|
186
|
+
- **Better monitoring**: Easier to track which API fields cause the most validation errors
|
187
|
+
- **User-friendly errors**: Can be directly displayed to end users (with proper sanitization)
|
188
|
+
- **Reduced support tickets**: Clearer errors mean fewer developer questions
|
189
|
+
|
190
|
+
## Backward Compatibility
|
191
|
+
|
192
|
+
✅ **Fully backward compatible**
|
193
|
+
- All existing error handling continues to work
|
194
|
+
- Enhanced error handling is additive
|
195
|
+
- No breaking changes to API contracts
|
196
|
+
- Existing tests continue to pass
|
197
|
+
|
198
|
+
## Usage Examples
|
199
|
+
|
200
|
+
See `examples/validation_error_examples.rb` for comprehensive examples showing:
|
201
|
+
- Missing required fields
|
202
|
+
- Invalid email formats
|
203
|
+
- Age constraint violations
|
204
|
+
- Invalid nested fields
|
205
|
+
- String length validation
|
206
|
+
- Successful validation cases
|
207
|
+
|
208
|
+
## Testing
|
209
|
+
|
210
|
+
All improvements have been tested with:
|
211
|
+
- ✅ Unit tests for type validation
|
212
|
+
- ✅ Integration tests for Sinatra adapter
|
213
|
+
- ✅ Real API scenario testing
|
214
|
+
- ✅ Backward compatibility verification
|
215
|
+
|
216
|
+
## Impact
|
217
|
+
|
218
|
+
This improvement significantly enhances the **ergonomics** of the RapiTapir library, making it much more developer-friendly and suitable for production use where clear validation feedback is essential for good API design.
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# RapiTapir AI Integration: Full Implementation Plan
|
2
|
+
|
3
|
+
## 🧠 Vision
|
4
|
+
|
5
|
+
RapiTapir aims to become the first Ruby API framework with native support for AI-driven workflows, agent orchestration, and LLM-powered developer experience. This plan details the steps to integrate Model Context Protocol (MCP), Retrieval-Augmented Generation (RAG), and LLM/agent orchestration into the core and ecosystem.
|
6
|
+
|
7
|
+
---
|
8
|
+
|
9
|
+
## Phase A: Core AI Integration
|
10
|
+
|
11
|
+
### 1. Model Context Protocol (MCP) Support
|
12
|
+
- Add endpoint(s) to expose API schemas, example requests/responses, and documentation in MCP-compatible JSON format.
|
13
|
+
- Implement a DSL helper (`.mcp_export`) to mark endpoints as context providers for LLMs/agents.
|
14
|
+
- CLI: `rapitapir export mcp` to generate MCP context files for agent toolchains.
|
15
|
+
- Documentation: Add a section on “Using RapiTapir APIs as LLM Tools (MCP)”.
|
16
|
+
|
17
|
+
### 2. Retrieval-Augmented Generation (RAG) Pipelines
|
18
|
+
- Extend the endpoint DSL with `.rag_inference(llm:, retrieval:, context_fields:)`.
|
19
|
+
- Provide built-in RAG pipeline support:
|
20
|
+
- Accept user query, retrieve relevant data (DB/API), pass to LLM, return result.
|
21
|
+
- Support for OpenAI, local LLMs, and pluggable retrieval backends.
|
22
|
+
- Example endpoint generator for RAG:
|
23
|
+
```ruby
|
24
|
+
endpoint = RapiTapir.post("/ask")
|
25
|
+
.in(json_body(:question => :string))
|
26
|
+
.rag_inference(llm: :openai, retrieval: :postgres, context_fields: [:user_id])
|
27
|
+
.out(json_body(:answer => :string))
|
28
|
+
```
|
29
|
+
- Add test coverage for RAG endpoints (mock LLM/retrieval).
|
30
|
+
|
31
|
+
### 3. LLM Instruction/Prompt Generation
|
32
|
+
- Add `.llm_instruction(purpose:, fields:)` DSL extension to annotate endpoints with prompt templates or instructions.
|
33
|
+
- Auto-generate OpenAPI-based prompts for LLM tool-use (e.g., for OpenAI function calling, LangChain).
|
34
|
+
- CLI: `rapitapir generate llm-prompts` to export prompts for all endpoints.
|
35
|
+
- Documentation: “How to use RapiTapir endpoints as LLM tools”.
|
36
|
+
|
37
|
+
---
|
38
|
+
|
39
|
+
## Phase B: Agent & Tooling Ecosystem
|
40
|
+
|
41
|
+
### 4. Agent Tool Registration & Orchestration
|
42
|
+
- Auto-generate OpenAPI/JSON Schema for agent tool registration (OpenAI, LangChain, etc.).
|
43
|
+
- Add endpoints for agent-to-agent communication (e.g., `/agent/invoke`, `/agent/context`).
|
44
|
+
- Provide a Ruby module for registering RapiTapir endpoints as agent tools (with function signatures, descriptions, and examples).
|
45
|
+
- CLI: `rapitapir agent export-tools` to generate tool schemas for agent frameworks.
|
46
|
+
|
47
|
+
### 5. Agent-Driven Workflows
|
48
|
+
- Enable endpoints to accept/return LLM-generated instructions, plans, or summaries.
|
49
|
+
- Add support for “chain-of-thought” and multi-step agent workflows (e.g., via a `.agent_workflow` DSL helper).
|
50
|
+
- Example:
|
51
|
+
```ruby
|
52
|
+
endpoint = RapiTapir.post("/plan")
|
53
|
+
.in(json_body(:goal => :string))
|
54
|
+
.agent_workflow(steps: [:search, :summarize, :act])
|
55
|
+
.out(json_body(:plan => :string))
|
56
|
+
```
|
57
|
+
|
58
|
+
---
|
59
|
+
|
60
|
+
## Phase C: Developer Experience & Documentation
|
61
|
+
|
62
|
+
### 6. AI-Enhanced Documentation & Testing
|
63
|
+
- Integrate LLMs to auto-generate endpoint summaries, descriptions, and usage examples.
|
64
|
+
- Add CLI commands:
|
65
|
+
- `rapitapir ai describe-endpoints` (auto-generate docs)
|
66
|
+
- `rapitapir ai suggest-tests` (auto-generate RSpec tests)
|
67
|
+
- Web UI: “Describe this endpoint” and “Suggest test cases” buttons in generated docs.
|
68
|
+
|
69
|
+
### 7. AI-Driven Endpoint Design
|
70
|
+
- CLI: `rapitapir ai generate-endpoint "A GET /books endpoint that returns a list of books with title and author"`
|
71
|
+
- Uses LLM to generate endpoint DSL, schema, and docs.
|
72
|
+
- Web UI: Interactive wizard for natural language to endpoint DSL/code.
|
73
|
+
|
74
|
+
### 8. RAG/LLM Example Gallery
|
75
|
+
- Add example endpoints and use cases for RAG, MCP, and agent orchestration in `examples/ai/`.
|
76
|
+
- Documentation: “AI Patterns with RapiTapir” guide.
|
77
|
+
|
78
|
+
---
|
79
|
+
|
80
|
+
## Phase D: Ecosystem & Community
|
81
|
+
|
82
|
+
### 9. Plugin & Extension System
|
83
|
+
- Provide hooks for custom LLMs, retrieval backends, and agent frameworks.
|
84
|
+
- Document how to build and share AI plugins/extensions.
|
85
|
+
|
86
|
+
### 10. Community & Feedback
|
87
|
+
- Launch a feedback program for AI features.
|
88
|
+
- Collect and publish community-contributed AI endpoint patterns.
|
89
|
+
|
90
|
+
---
|
91
|
+
|
92
|
+
## Milestones & Timeline (Suggested)
|
93
|
+
|
94
|
+
| Phase | Feature Area | Timeline |
|
95
|
+
|-------|----------------------------|------------|
|
96
|
+
| A | Core AI Integration | 4 weeks |
|
97
|
+
| B | Agent & Tooling Ecosystem | 3 weeks |
|
98
|
+
| C | Dev Experience & Docs | 3 weeks |
|
99
|
+
| D | Ecosystem & Community | Ongoing |
|
100
|
+
|
101
|
+
---
|
102
|
+
|
103
|
+
## Success Metrics
|
104
|
+
|
105
|
+
- MCP/RAG endpoints pass integration tests with LLM/agent frameworks.
|
106
|
+
- CLI and docs support AI-driven workflows.
|
107
|
+
- Community adoption of AI features and plugins.
|
108
|
+
- RapiTapir endpoints can be used as tools by LLM agents with minimal friction.
|
109
|
+
|
110
|
+
---
|
111
|
+
|
112
|
+
RapiTapir will become a first-class Ruby API platform for AI-native and agent-driven applications, while maintaining its core strengths in type safety, developer experience, and extensibility.
|