rapitapir 0.1.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 (157) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +57 -0
  4. data/CHANGELOG.md +94 -0
  5. data/CLEANUP_SUMMARY.md +155 -0
  6. data/CONTRIBUTING.md +280 -0
  7. data/LICENSE +21 -0
  8. data/README.md +485 -0
  9. data/debug_hash.rb +20 -0
  10. data/docs/EXTENSION_COMPARISON.md +388 -0
  11. data/docs/SINATRA_EXTENSION.md +467 -0
  12. data/docs/archive/PHASE_1_2_COMPLETE.md +77 -0
  13. data/docs/archive/PHASE_1_3_COMPLETE.md +152 -0
  14. data/docs/archive/PHASE_2_1_OBSERVABILITY_COMPLETED.md +203 -0
  15. data/docs/archive/PHASE_2_SUMMARY.md +209 -0
  16. data/docs/archive/REFACTORING_SUMMARY.md +184 -0
  17. data/docs/archive/phase_1_3_plan.md +136 -0
  18. data/docs/archive/sinatra_extension_summary.md +188 -0
  19. data/docs/archive/sinatra_working_solution.md +113 -0
  20. data/docs/archive/typescript-client-generator-summary.md +259 -0
  21. data/docs/auto-derivation.md +146 -0
  22. data/docs/blueprint.md +1091 -0
  23. data/docs/endpoint-definition.md +211 -0
  24. data/docs/github_pages_fix.md +52 -0
  25. data/docs/github_pages_setup.md +49 -0
  26. data/docs/implementation-status.md +357 -0
  27. data/docs/observability.md +647 -0
  28. data/docs/phase3-plan.md +108 -0
  29. data/docs/sinatra_rapitapir.md +87 -0
  30. data/docs/type_shortcuts.md +146 -0
  31. data/examples/README_ENTERPRISE.md +202 -0
  32. data/examples/authentication_example.rb +192 -0
  33. data/examples/auto_derivation_ruby_friendly.rb +163 -0
  34. data/examples/cli/user_api_endpoints.rb +56 -0
  35. data/examples/client/typescript_client_example.rb +102 -0
  36. data/examples/client/user-api-client.ts +193 -0
  37. data/examples/demo_api.rb +41 -0
  38. data/examples/docs/documentation_example.rb +112 -0
  39. data/examples/docs/user-api-docs.html +789 -0
  40. data/examples/docs/user-api-docs.md +403 -0
  41. data/examples/enhanced_auto_derivation_test.rb +83 -0
  42. data/examples/enterprise_extension_demo.rb +417 -0
  43. data/examples/enterprise_rapitapir_api.rb +662 -0
  44. data/examples/getting_started_extension.rb +218 -0
  45. data/examples/hello_world.rb +74 -0
  46. data/examples/oauth2/.env.example +19 -0
  47. data/examples/oauth2/README.md +205 -0
  48. data/examples/oauth2/generic_oauth2_api.rb +226 -0
  49. data/examples/oauth2/get_token.rb +72 -0
  50. data/examples/oauth2/songs_api_with_auth0.rb +320 -0
  51. data/examples/oauth2/test_api.sh +16 -0
  52. data/examples/oauth2/test_songs_api.sh +110 -0
  53. data/examples/observability/.env.example +35 -0
  54. data/examples/observability/README.md +230 -0
  55. data/examples/observability/README_HONEYCOMB.md +332 -0
  56. data/examples/observability/advanced_setup.rb +384 -0
  57. data/examples/observability/basic_setup.rb +192 -0
  58. data/examples/observability/complete_test.rb +121 -0
  59. data/examples/observability/honeycomb_example.rb +523 -0
  60. data/examples/observability/honeycomb_rapitapir_clean.rb +488 -0
  61. data/examples/observability/honeycomb_rapitapir_example.rb +523 -0
  62. data/examples/observability/honeycomb_working_example.rb +489 -0
  63. data/examples/observability/quick_test.rb +78 -0
  64. data/examples/observability/simple_test.rb +14 -0
  65. data/examples/observability/test_honeycomb_demo.rb +354 -0
  66. data/examples/observability/test_live_honeycomb.rb +111 -0
  67. data/examples/observability/test_validation.rb +78 -0
  68. data/examples/observability/test_working_validation.rb +66 -0
  69. data/examples/openapi/user_api_schema.rb +132 -0
  70. data/examples/production_ready_example.rb +105 -0
  71. data/examples/rails/users_controller.rb +146 -0
  72. data/examples/readme/basic_sinatra_example.rb +128 -0
  73. data/examples/server/user_api.rb +179 -0
  74. data/examples/simple_auto_derivation_demo.rb +44 -0
  75. data/examples/simple_demo_api.rb +18 -0
  76. data/examples/sinatra/user_app.rb +127 -0
  77. data/examples/t_shortcut_demo.rb +59 -0
  78. data/examples/user_api.rb +190 -0
  79. data/examples/working_getting_started.rb +184 -0
  80. data/examples/working_simple_example.rb +195 -0
  81. data/lib/rapitapir/auth/configuration.rb +129 -0
  82. data/lib/rapitapir/auth/context.rb +122 -0
  83. data/lib/rapitapir/auth/errors.rb +104 -0
  84. data/lib/rapitapir/auth/middleware.rb +324 -0
  85. data/lib/rapitapir/auth/oauth2.rb +350 -0
  86. data/lib/rapitapir/auth/schemes.rb +420 -0
  87. data/lib/rapitapir/auth.rb +113 -0
  88. data/lib/rapitapir/cli/command.rb +535 -0
  89. data/lib/rapitapir/cli/server.rb +243 -0
  90. data/lib/rapitapir/cli/validator.rb +373 -0
  91. data/lib/rapitapir/client/generator_base.rb +272 -0
  92. data/lib/rapitapir/client/typescript_generator.rb +350 -0
  93. data/lib/rapitapir/core/endpoint.rb +158 -0
  94. data/lib/rapitapir/core/enhanced_endpoint.rb +235 -0
  95. data/lib/rapitapir/core/input.rb +182 -0
  96. data/lib/rapitapir/core/output.rb +164 -0
  97. data/lib/rapitapir/core/request.rb +19 -0
  98. data/lib/rapitapir/core/response.rb +17 -0
  99. data/lib/rapitapir/docs/html_generator.rb +780 -0
  100. data/lib/rapitapir/docs/markdown_generator.rb +464 -0
  101. data/lib/rapitapir/dsl/endpoint_dsl.rb +116 -0
  102. data/lib/rapitapir/dsl/enhanced_endpoint_dsl.rb +62 -0
  103. data/lib/rapitapir/dsl/enhanced_input.rb +73 -0
  104. data/lib/rapitapir/dsl/enhanced_output.rb +63 -0
  105. data/lib/rapitapir/dsl/enhanced_structures.rb +393 -0
  106. data/lib/rapitapir/dsl/fluent_dsl.rb +72 -0
  107. data/lib/rapitapir/dsl/fluent_endpoint_builder.rb +316 -0
  108. data/lib/rapitapir/dsl/http_verbs.rb +77 -0
  109. data/lib/rapitapir/dsl/input_methods.rb +47 -0
  110. data/lib/rapitapir/dsl/observability_methods.rb +81 -0
  111. data/lib/rapitapir/dsl/output_methods.rb +43 -0
  112. data/lib/rapitapir/dsl/type_resolution.rb +43 -0
  113. data/lib/rapitapir/observability/configuration.rb +108 -0
  114. data/lib/rapitapir/observability/health_check.rb +236 -0
  115. data/lib/rapitapir/observability/logging.rb +270 -0
  116. data/lib/rapitapir/observability/metrics.rb +203 -0
  117. data/lib/rapitapir/observability/middleware.rb +243 -0
  118. data/lib/rapitapir/observability/tracing.rb +143 -0
  119. data/lib/rapitapir/observability.rb +28 -0
  120. data/lib/rapitapir/openapi/schema_generator.rb +403 -0
  121. data/lib/rapitapir/schema.rb +136 -0
  122. data/lib/rapitapir/server/enhanced_rack_adapter.rb +379 -0
  123. data/lib/rapitapir/server/middleware.rb +120 -0
  124. data/lib/rapitapir/server/path_matcher.rb +45 -0
  125. data/lib/rapitapir/server/rack_adapter.rb +215 -0
  126. data/lib/rapitapir/server/rails_adapter.rb +17 -0
  127. data/lib/rapitapir/server/rails_adapter_class.rb +53 -0
  128. data/lib/rapitapir/server/rails_controller.rb +72 -0
  129. data/lib/rapitapir/server/rails_input_processor.rb +73 -0
  130. data/lib/rapitapir/server/rails_response_handler.rb +29 -0
  131. data/lib/rapitapir/server/sinatra_adapter.rb +200 -0
  132. data/lib/rapitapir/server/sinatra_integration.rb +93 -0
  133. data/lib/rapitapir/sinatra/configuration.rb +91 -0
  134. data/lib/rapitapir/sinatra/extension.rb +214 -0
  135. data/lib/rapitapir/sinatra/oauth2_helpers.rb +236 -0
  136. data/lib/rapitapir/sinatra/resource_builder.rb +152 -0
  137. data/lib/rapitapir/sinatra/swagger_ui_generator.rb +166 -0
  138. data/lib/rapitapir/sinatra_rapitapir.rb +40 -0
  139. data/lib/rapitapir/types/array.rb +163 -0
  140. data/lib/rapitapir/types/auto_derivation.rb +265 -0
  141. data/lib/rapitapir/types/base.rb +146 -0
  142. data/lib/rapitapir/types/boolean.rb +46 -0
  143. data/lib/rapitapir/types/date.rb +92 -0
  144. data/lib/rapitapir/types/datetime.rb +98 -0
  145. data/lib/rapitapir/types/email.rb +32 -0
  146. data/lib/rapitapir/types/float.rb +134 -0
  147. data/lib/rapitapir/types/hash.rb +161 -0
  148. data/lib/rapitapir/types/integer.rb +143 -0
  149. data/lib/rapitapir/types/object.rb +156 -0
  150. data/lib/rapitapir/types/optional.rb +65 -0
  151. data/lib/rapitapir/types/string.rb +185 -0
  152. data/lib/rapitapir/types/uuid.rb +32 -0
  153. data/lib/rapitapir/types.rb +155 -0
  154. data/lib/rapitapir/version.rb +5 -0
  155. data/lib/rapitapir.rb +173 -0
  156. data/rapitapir.gemspec +66 -0
  157. metadata +387 -0
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RapiTapir
4
+ module DSL
5
+ # Enhanced Output class that uses the new type system
6
+ class EnhancedOutput
7
+ attr_reader :kind, :type, :options
8
+
9
+ def initialize(kind:, type:, options: {})
10
+ @kind = kind
11
+ @type = type
12
+ @options = options.freeze
13
+ end
14
+
15
+ def validate(value)
16
+ return { valid: true, errors: [] } if kind == :status
17
+
18
+ type.validate(value)
19
+ end
20
+
21
+ def serialize(value)
22
+ case kind
23
+ when :json
24
+ JSON.generate(value)
25
+ when :status
26
+ # Status codes don't need serialization
27
+ value
28
+ else # :text and other formats
29
+ value.to_s
30
+ end
31
+ end
32
+
33
+ def to_openapi_response
34
+ if kind == :status
35
+ {
36
+ description: options[:description] || "Response with status #{type}",
37
+ content: {}
38
+ }
39
+ else
40
+ schema = type.respond_to?(:to_json_schema) ? type.to_json_schema : { type: 'string' }
41
+ content_type = kind == :json ? 'application/json' : 'text/plain'
42
+
43
+ {
44
+ description: options[:description] || 'Successful response',
45
+ content: {
46
+ content_type => {
47
+ schema: schema
48
+ }
49
+ }
50
+ }
51
+ end
52
+ end
53
+
54
+ def to_h
55
+ {
56
+ kind: kind,
57
+ type: type.respond_to?(:to_s) ? type.to_s : type.class.name,
58
+ options: options
59
+ }
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,393 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RapiTapir
4
+ module DSL
5
+ # Enhanced input specification with full type system integration
6
+ class EnhancedInput
7
+ attr_reader :kind, :name, :type, :required, :description, :example, :format, :content_type
8
+
9
+ def initialize(kind:, name:, type:, required: true, **options)
10
+ @kind = kind.to_sym
11
+ @name = name.to_sym
12
+ @type = type
13
+ @required = required
14
+ @description = options[:description]
15
+ @example = options[:example]
16
+ @format = options[:format]
17
+ @content_type = options[:content_type]
18
+ end
19
+
20
+ def required?
21
+ # Check if the input is required based on:
22
+ # 1. Explicit required parameter
23
+ # 2. Whether the type is optional
24
+ return false if @required == false # Explicitly set to false
25
+ return false if @type.respond_to?(:optional?) && @type.optional?
26
+
27
+ @required.nil? || @required # Default to true if not specified
28
+ end
29
+
30
+ def optional?
31
+ !@required
32
+ end
33
+
34
+ def to_openapi_spec
35
+ spec = {
36
+ name: @name.to_s,
37
+ in: openapi_location,
38
+ required: @required,
39
+ schema: @type.to_json_schema
40
+ }
41
+
42
+ spec[:description] = @description if @description
43
+ spec[:example] = @example if @example
44
+
45
+ spec
46
+ end
47
+
48
+ def validate(value)
49
+ @type.validate(value)
50
+ end
51
+
52
+ def coerce(value)
53
+ @type.coerce(value)
54
+ end
55
+
56
+ private
57
+
58
+ def openapi_location
59
+ case @kind
60
+ when :query
61
+ 'query'
62
+ when :path
63
+ 'path'
64
+ when :header
65
+ 'header'
66
+ when :body
67
+ 'requestBody'
68
+ else
69
+ @kind.to_s
70
+ end
71
+ end
72
+ end
73
+
74
+ # Enhanced output specification with status codes and content types
75
+ class EnhancedOutput
76
+ attr_reader :status_code, :type, :content_type, :description, :example, :headers
77
+
78
+ def initialize(status_code:, type: nil, content_type: 'application/json', **options)
79
+ @status_code = status_code.to_i
80
+ @type = type
81
+ @content_type = content_type
82
+ @description = options[:description]
83
+ @example = options[:example]
84
+ @headers = options[:headers] || {}
85
+ end
86
+
87
+ # Legacy compatibility method for validators
88
+ def kind
89
+ return :status if @type.nil?
90
+
91
+ case @content_type
92
+ when 'application/xml', 'text/xml'
93
+ :xml
94
+ else # Default to json for application/json and unknown content types
95
+ :json
96
+ end
97
+ end
98
+
99
+ def to_openapi_spec
100
+ spec = {
101
+ description: @description || http_status_description
102
+ }
103
+
104
+ add_content_to_spec(spec) if @type
105
+ add_headers_to_spec(spec) if @headers.any?
106
+
107
+ spec
108
+ end
109
+
110
+ def add_content_to_spec(spec)
111
+ spec[:content] = {
112
+ @content_type => {
113
+ schema: @type.to_json_schema
114
+ }
115
+ }
116
+
117
+ spec[:content][@content_type][:example] = @example if @example
118
+ end
119
+
120
+ def add_headers_to_spec(spec)
121
+ spec[:headers] = @headers.transform_values do |header_spec|
122
+ transform_header_spec(header_spec)
123
+ end
124
+ end
125
+
126
+ def transform_header_spec(header_spec)
127
+ case header_spec
128
+ when Hash
129
+ header_spec
130
+ when String
131
+ { description: header_spec }
132
+ else
133
+ { description: header_spec.to_s }
134
+ end
135
+ end
136
+
137
+ def validate(value)
138
+ return { valid: true, errors: [] } unless @type
139
+
140
+ @type.validate(value)
141
+ end
142
+
143
+ def serialize(value)
144
+ return nil unless @type
145
+
146
+ case @content_type
147
+ when 'application/json'
148
+ JSON.generate(value)
149
+ when 'text/plain'
150
+ value.to_s
151
+ else
152
+ value
153
+ end
154
+ end
155
+
156
+ private
157
+
158
+ def http_status_description
159
+ case @status_code
160
+ when 200, 201, 202, 204
161
+ success_status_description
162
+ when 400, 401, 403, 404, 422
163
+ client_error_status_description
164
+ when 500
165
+ 'Internal Server Error'
166
+ else
167
+ "HTTP #{@status_code}"
168
+ end
169
+ end
170
+
171
+ def success_status_description
172
+ case @status_code
173
+ when 200 then 'OK'
174
+ when 201 then 'Created'
175
+ when 202 then 'Accepted'
176
+ when 204 then 'No Content'
177
+ end
178
+ end
179
+
180
+ def client_error_status_description
181
+ case @status_code
182
+ when 400 then 'Bad Request'
183
+ when 401 then 'Unauthorized'
184
+ when 403 then 'Forbidden'
185
+ when 404 then 'Not Found'
186
+ when 422 then 'Unprocessable Entity'
187
+ end
188
+ end
189
+ end
190
+
191
+ # Enhanced error specification for detailed error responses
192
+ class EnhancedError
193
+ attr_reader :status_code, :type, :description, :example
194
+
195
+ def initialize(status_code:, type: nil, description: nil, example: nil)
196
+ @status_code = status_code.to_i
197
+ @type = type
198
+ @description = description
199
+ @example = example
200
+ end
201
+
202
+ def to_openapi_spec
203
+ spec = {
204
+ description: @description || http_error_description
205
+ }
206
+
207
+ if @type
208
+ spec[:content] = {
209
+ 'application/json' => {
210
+ schema: @type.to_json_schema
211
+ }
212
+ }
213
+
214
+ spec[:content]['application/json'][:example] = @example if @example
215
+ end
216
+
217
+ spec
218
+ end
219
+
220
+ def matches?(error)
221
+ error.respond_to?(:status_code) && error.status_code == @status_code
222
+ end
223
+
224
+ private
225
+
226
+ def http_error_description
227
+ case @status_code
228
+ when 400
229
+ 'Bad Request - Invalid input parameters'
230
+ when 401
231
+ 'Unauthorized - Authentication required'
232
+ when 403
233
+ 'Forbidden - Insufficient permissions'
234
+ when 404
235
+ 'Not Found - Resource not found'
236
+ when 422
237
+ 'Unprocessable Entity - Validation failed'
238
+ when 500
239
+ 'Internal Server Error - Server encountered an error'
240
+ else
241
+ "HTTP Error #{@status_code}"
242
+ end
243
+ end
244
+ end
245
+
246
+ # Enhanced security specification for authentication schemes
247
+ class EnhancedSecurity
248
+ attr_reader :type, :description, :name, :location, :scopes, :flows
249
+
250
+ def initialize(type:, description:, **config)
251
+ @type = type.to_sym
252
+ @description = description
253
+ @name = config[:name]
254
+ @location = config[:location]&.to_sym
255
+ @scopes = Array(config[:scopes] || [])
256
+ @flows = config[:flows]
257
+ @options = config.except(:name, :location, :scopes, :flows)
258
+ end
259
+
260
+ def to_openapi_spec
261
+ spec = {
262
+ type: openapi_type,
263
+ description: @description
264
+ }
265
+
266
+ add_auth_specific_fields(spec)
267
+ spec
268
+ end
269
+
270
+ def add_auth_specific_fields(spec)
271
+ case @type
272
+ when :bearer
273
+ add_bearer_fields(spec)
274
+ when :api_key
275
+ add_api_key_fields(spec)
276
+ when :basic
277
+ add_basic_fields(spec)
278
+ when :oauth2
279
+ add_oauth2_fields(spec)
280
+ end
281
+ end
282
+
283
+ def add_bearer_fields(spec)
284
+ spec[:scheme] = 'bearer'
285
+ spec[:bearerFormat] = @options[:bearer_format] if @options[:bearer_format]
286
+ end
287
+
288
+ def add_api_key_fields(spec)
289
+ spec[:name] = @name || 'X-API-Key'
290
+ spec[:in] = (@location || :header).to_s
291
+ end
292
+
293
+ def add_basic_fields(spec)
294
+ spec[:scheme] = 'basic'
295
+ end
296
+
297
+ def add_oauth2_fields(spec)
298
+ spec[:flows] = @flows || default_oauth2_flows
299
+ end
300
+
301
+ def validate_request(request)
302
+ case @type
303
+ when :bearer
304
+ validate_bearer_token(request)
305
+ when :api_key
306
+ validate_api_key(request)
307
+ when :basic
308
+ validate_basic_auth(request)
309
+ when :oauth2
310
+ validate_oauth2_token(request)
311
+ else
312
+ { valid: false, error: "Unsupported auth type: #{@type}" }
313
+ end
314
+ end
315
+
316
+ private
317
+
318
+ def openapi_type
319
+ case @type
320
+ when :bearer, :basic
321
+ 'http'
322
+ when :api_key
323
+ 'apiKey'
324
+ when :oauth2
325
+ 'oauth2'
326
+ else
327
+ @type.to_s
328
+ end
329
+ end
330
+
331
+ def default_oauth2_flows
332
+ {
333
+ implicit: {
334
+ authorizationUrl: @options[:authorization_url] || 'https://example.com/oauth/authorize',
335
+ scopes: @scopes.each_with_object({}) { |scope, hash| hash[scope] = scope.to_s.humanize }
336
+ }
337
+ }
338
+ end
339
+
340
+ def validate_bearer_token(request)
341
+ auth_header = request.env['HTTP_AUTHORIZATION']
342
+ return { valid: false, error: 'Missing Authorization header' } unless auth_header
343
+
344
+ return { valid: false, error: 'Invalid Authorization header format' } unless auth_header.start_with?('Bearer ')
345
+
346
+ token = auth_header[7..] # Remove 'Bearer ' prefix
347
+ return { valid: false, error: 'Empty token' } if token.empty?
348
+
349
+ { valid: true, token: token }
350
+ end
351
+
352
+ def validate_api_key(request)
353
+ key_name = @name || 'X-API-Key'
354
+
355
+ key_value = case @location
356
+ when :query
357
+ request.params[key_name]
358
+ else # Default to header for :header and unknown locations
359
+ request.env["HTTP_#{key_name.upcase.gsub('-', '_')}"]
360
+ end
361
+
362
+ return { valid: false, error: "Missing API key: #{key_name}" } unless key_value
363
+ return { valid: false, error: 'Empty API key' } if key_value.empty?
364
+
365
+ { valid: true, api_key: key_value }
366
+ end
367
+
368
+ def validate_basic_auth(request)
369
+ auth_header = request.env['HTTP_AUTHORIZATION']
370
+ return { valid: false, error: 'Missing Authorization header' } unless auth_header
371
+
372
+ return { valid: false, error: 'Invalid Authorization header format' } unless auth_header.start_with?('Basic ')
373
+
374
+ encoded_credentials = auth_header[6..] # Remove 'Basic ' prefix
375
+ return { valid: false, error: 'Empty credentials' } if encoded_credentials.empty?
376
+
377
+ begin
378
+ decoded_credentials = Base64.decode64(encoded_credentials)
379
+ username, password = decoded_credentials.split(':', 2)
380
+ { valid: true, username: username, password: password }
381
+ rescue StandardError => e
382
+ { valid: false, error: "Invalid credentials encoding: #{e.message}" }
383
+ end
384
+ end
385
+
386
+ def validate_oauth2_token(request)
387
+ # OAuth2 validation would typically involve token introspection
388
+ # For now, we'll do basic Bearer token validation
389
+ validate_bearer_token(request)
390
+ end
391
+ end
392
+ end
393
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/endpoint'
4
+ require_relative 'fluent_endpoint_builder'
5
+
6
+ # RapiTapir Ruby library for building type-safe HTTP APIs
7
+ # Provides a declarative way to define REST APIs with automatic validation
8
+ module RapiTapir
9
+ # Fluent DSL for defining HTTP endpoints
10
+ # Provides chainable methods for declarative API definition
11
+ #
12
+ # @example Basic usage
13
+ # RapiTapir.get('/users')
14
+ # .ok(RapiTapir::Types.array(RapiTapir::Types.hash({"id" => RapiTapir::Types.integer})))
15
+ # .summary('Get all users')
16
+ # .build
17
+ #
18
+ # @example With parameters
19
+ # RapiTapir.get('/users/{id}')
20
+ # .path_param(:id, RapiTapir::Types.integer)
21
+ # .ok(RapiTapir::Types.hash({"id" => RapiTapir::Types.integer, "name" => RapiTapir::Types.string}))
22
+ # .build
23
+
24
+ # HTTP GET endpoint
25
+ # @param path [String] The endpoint path
26
+ # @return [DSL::FluentEndpointBuilder] Builder for method chaining
27
+ def self.get(path)
28
+ DSL::FluentEndpointBuilder.new(:get, path)
29
+ end
30
+
31
+ # HTTP POST endpoint
32
+ # @param path [String] The endpoint path
33
+ # @return [DSL::FluentEndpointBuilder] Builder for method chaining
34
+ def self.post(path)
35
+ DSL::FluentEndpointBuilder.new(:post, path)
36
+ end
37
+
38
+ # HTTP PUT endpoint
39
+ # @param path [String] The endpoint path
40
+ # @return [DSL::FluentEndpointBuilder] Builder for method chaining
41
+ def self.put(path)
42
+ DSL::FluentEndpointBuilder.new(:put, path)
43
+ end
44
+
45
+ # HTTP PATCH endpoint
46
+ # @param path [String] The endpoint path
47
+ # @return [DSL::FluentEndpointBuilder] Builder for method chaining
48
+ def self.patch(path)
49
+ DSL::FluentEndpointBuilder.new(:patch, path)
50
+ end
51
+
52
+ # HTTP DELETE endpoint
53
+ # @param path [String] The endpoint path
54
+ # @return [DSL::FluentEndpointBuilder] Builder for method chaining
55
+ def self.delete(path)
56
+ DSL::FluentEndpointBuilder.new(:delete, path)
57
+ end
58
+
59
+ # HTTP HEAD endpoint
60
+ # @param path [String] The endpoint path
61
+ # @return [DSL::FluentEndpointBuilder] Builder for method chaining
62
+ def self.head(path)
63
+ DSL::FluentEndpointBuilder.new(:head, path)
64
+ end
65
+
66
+ # HTTP OPTIONS endpoint
67
+ # @param path [String] The endpoint path
68
+ # @return [DSL::FluentEndpointBuilder] Builder for method chaining
69
+ def self.options(path)
70
+ DSL::FluentEndpointBuilder.new(:options, path)
71
+ end
72
+ end