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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +57 -0
- data/CHANGELOG.md +94 -0
- data/CLEANUP_SUMMARY.md +155 -0
- data/CONTRIBUTING.md +280 -0
- data/LICENSE +21 -0
- data/README.md +485 -0
- data/debug_hash.rb +20 -0
- data/docs/EXTENSION_COMPARISON.md +388 -0
- data/docs/SINATRA_EXTENSION.md +467 -0
- data/docs/archive/PHASE_1_2_COMPLETE.md +77 -0
- data/docs/archive/PHASE_1_3_COMPLETE.md +152 -0
- data/docs/archive/PHASE_2_1_OBSERVABILITY_COMPLETED.md +203 -0
- data/docs/archive/PHASE_2_SUMMARY.md +209 -0
- data/docs/archive/REFACTORING_SUMMARY.md +184 -0
- data/docs/archive/phase_1_3_plan.md +136 -0
- data/docs/archive/sinatra_extension_summary.md +188 -0
- data/docs/archive/sinatra_working_solution.md +113 -0
- data/docs/archive/typescript-client-generator-summary.md +259 -0
- data/docs/auto-derivation.md +146 -0
- data/docs/blueprint.md +1091 -0
- data/docs/endpoint-definition.md +211 -0
- data/docs/github_pages_fix.md +52 -0
- data/docs/github_pages_setup.md +49 -0
- data/docs/implementation-status.md +357 -0
- data/docs/observability.md +647 -0
- data/docs/phase3-plan.md +108 -0
- data/docs/sinatra_rapitapir.md +87 -0
- data/docs/type_shortcuts.md +146 -0
- data/examples/README_ENTERPRISE.md +202 -0
- data/examples/authentication_example.rb +192 -0
- data/examples/auto_derivation_ruby_friendly.rb +163 -0
- data/examples/cli/user_api_endpoints.rb +56 -0
- data/examples/client/typescript_client_example.rb +102 -0
- data/examples/client/user-api-client.ts +193 -0
- data/examples/demo_api.rb +41 -0
- data/examples/docs/documentation_example.rb +112 -0
- data/examples/docs/user-api-docs.html +789 -0
- data/examples/docs/user-api-docs.md +403 -0
- data/examples/enhanced_auto_derivation_test.rb +83 -0
- data/examples/enterprise_extension_demo.rb +417 -0
- data/examples/enterprise_rapitapir_api.rb +662 -0
- data/examples/getting_started_extension.rb +218 -0
- data/examples/hello_world.rb +74 -0
- data/examples/oauth2/.env.example +19 -0
- data/examples/oauth2/README.md +205 -0
- data/examples/oauth2/generic_oauth2_api.rb +226 -0
- data/examples/oauth2/get_token.rb +72 -0
- data/examples/oauth2/songs_api_with_auth0.rb +320 -0
- data/examples/oauth2/test_api.sh +16 -0
- data/examples/oauth2/test_songs_api.sh +110 -0
- data/examples/observability/.env.example +35 -0
- data/examples/observability/README.md +230 -0
- data/examples/observability/README_HONEYCOMB.md +332 -0
- data/examples/observability/advanced_setup.rb +384 -0
- data/examples/observability/basic_setup.rb +192 -0
- data/examples/observability/complete_test.rb +121 -0
- data/examples/observability/honeycomb_example.rb +523 -0
- data/examples/observability/honeycomb_rapitapir_clean.rb +488 -0
- data/examples/observability/honeycomb_rapitapir_example.rb +523 -0
- data/examples/observability/honeycomb_working_example.rb +489 -0
- data/examples/observability/quick_test.rb +78 -0
- data/examples/observability/simple_test.rb +14 -0
- data/examples/observability/test_honeycomb_demo.rb +354 -0
- data/examples/observability/test_live_honeycomb.rb +111 -0
- data/examples/observability/test_validation.rb +78 -0
- data/examples/observability/test_working_validation.rb +66 -0
- data/examples/openapi/user_api_schema.rb +132 -0
- data/examples/production_ready_example.rb +105 -0
- data/examples/rails/users_controller.rb +146 -0
- data/examples/readme/basic_sinatra_example.rb +128 -0
- data/examples/server/user_api.rb +179 -0
- data/examples/simple_auto_derivation_demo.rb +44 -0
- data/examples/simple_demo_api.rb +18 -0
- data/examples/sinatra/user_app.rb +127 -0
- data/examples/t_shortcut_demo.rb +59 -0
- data/examples/user_api.rb +190 -0
- data/examples/working_getting_started.rb +184 -0
- data/examples/working_simple_example.rb +195 -0
- data/lib/rapitapir/auth/configuration.rb +129 -0
- data/lib/rapitapir/auth/context.rb +122 -0
- data/lib/rapitapir/auth/errors.rb +104 -0
- data/lib/rapitapir/auth/middleware.rb +324 -0
- data/lib/rapitapir/auth/oauth2.rb +350 -0
- data/lib/rapitapir/auth/schemes.rb +420 -0
- data/lib/rapitapir/auth.rb +113 -0
- data/lib/rapitapir/cli/command.rb +535 -0
- data/lib/rapitapir/cli/server.rb +243 -0
- data/lib/rapitapir/cli/validator.rb +373 -0
- data/lib/rapitapir/client/generator_base.rb +272 -0
- data/lib/rapitapir/client/typescript_generator.rb +350 -0
- data/lib/rapitapir/core/endpoint.rb +158 -0
- data/lib/rapitapir/core/enhanced_endpoint.rb +235 -0
- data/lib/rapitapir/core/input.rb +182 -0
- data/lib/rapitapir/core/output.rb +164 -0
- data/lib/rapitapir/core/request.rb +19 -0
- data/lib/rapitapir/core/response.rb +17 -0
- data/lib/rapitapir/docs/html_generator.rb +780 -0
- data/lib/rapitapir/docs/markdown_generator.rb +464 -0
- data/lib/rapitapir/dsl/endpoint_dsl.rb +116 -0
- data/lib/rapitapir/dsl/enhanced_endpoint_dsl.rb +62 -0
- data/lib/rapitapir/dsl/enhanced_input.rb +73 -0
- data/lib/rapitapir/dsl/enhanced_output.rb +63 -0
- data/lib/rapitapir/dsl/enhanced_structures.rb +393 -0
- data/lib/rapitapir/dsl/fluent_dsl.rb +72 -0
- data/lib/rapitapir/dsl/fluent_endpoint_builder.rb +316 -0
- data/lib/rapitapir/dsl/http_verbs.rb +77 -0
- data/lib/rapitapir/dsl/input_methods.rb +47 -0
- data/lib/rapitapir/dsl/observability_methods.rb +81 -0
- data/lib/rapitapir/dsl/output_methods.rb +43 -0
- data/lib/rapitapir/dsl/type_resolution.rb +43 -0
- data/lib/rapitapir/observability/configuration.rb +108 -0
- data/lib/rapitapir/observability/health_check.rb +236 -0
- data/lib/rapitapir/observability/logging.rb +270 -0
- data/lib/rapitapir/observability/metrics.rb +203 -0
- data/lib/rapitapir/observability/middleware.rb +243 -0
- data/lib/rapitapir/observability/tracing.rb +143 -0
- data/lib/rapitapir/observability.rb +28 -0
- data/lib/rapitapir/openapi/schema_generator.rb +403 -0
- data/lib/rapitapir/schema.rb +136 -0
- data/lib/rapitapir/server/enhanced_rack_adapter.rb +379 -0
- data/lib/rapitapir/server/middleware.rb +120 -0
- data/lib/rapitapir/server/path_matcher.rb +45 -0
- data/lib/rapitapir/server/rack_adapter.rb +215 -0
- data/lib/rapitapir/server/rails_adapter.rb +17 -0
- data/lib/rapitapir/server/rails_adapter_class.rb +53 -0
- data/lib/rapitapir/server/rails_controller.rb +72 -0
- data/lib/rapitapir/server/rails_input_processor.rb +73 -0
- data/lib/rapitapir/server/rails_response_handler.rb +29 -0
- data/lib/rapitapir/server/sinatra_adapter.rb +200 -0
- data/lib/rapitapir/server/sinatra_integration.rb +93 -0
- data/lib/rapitapir/sinatra/configuration.rb +91 -0
- data/lib/rapitapir/sinatra/extension.rb +214 -0
- data/lib/rapitapir/sinatra/oauth2_helpers.rb +236 -0
- data/lib/rapitapir/sinatra/resource_builder.rb +152 -0
- data/lib/rapitapir/sinatra/swagger_ui_generator.rb +166 -0
- data/lib/rapitapir/sinatra_rapitapir.rb +40 -0
- data/lib/rapitapir/types/array.rb +163 -0
- data/lib/rapitapir/types/auto_derivation.rb +265 -0
- data/lib/rapitapir/types/base.rb +146 -0
- data/lib/rapitapir/types/boolean.rb +46 -0
- data/lib/rapitapir/types/date.rb +92 -0
- data/lib/rapitapir/types/datetime.rb +98 -0
- data/lib/rapitapir/types/email.rb +32 -0
- data/lib/rapitapir/types/float.rb +134 -0
- data/lib/rapitapir/types/hash.rb +161 -0
- data/lib/rapitapir/types/integer.rb +143 -0
- data/lib/rapitapir/types/object.rb +156 -0
- data/lib/rapitapir/types/optional.rb +65 -0
- data/lib/rapitapir/types/string.rb +185 -0
- data/lib/rapitapir/types/uuid.rb +32 -0
- data/lib/rapitapir/types.rb +155 -0
- data/lib/rapitapir/version.rb +5 -0
- data/lib/rapitapir.rb +173 -0
- data/rapitapir.gemspec +66 -0
- metadata +387 -0
@@ -0,0 +1,789 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>User Management API</title>
|
7
|
+
<style>
|
8
|
+
* {
|
9
|
+
margin: 0;
|
10
|
+
padding: 0;
|
11
|
+
box-sizing: border-box;
|
12
|
+
}
|
13
|
+
|
14
|
+
body {
|
15
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
16
|
+
line-height: 1.6;
|
17
|
+
color: #333;
|
18
|
+
background-color: #f8f9fa;
|
19
|
+
}
|
20
|
+
|
21
|
+
.container {
|
22
|
+
display: flex;
|
23
|
+
min-height: 100vh;
|
24
|
+
}
|
25
|
+
|
26
|
+
.header {
|
27
|
+
position: fixed;
|
28
|
+
top: 0;
|
29
|
+
left: 0;
|
30
|
+
right: 0;
|
31
|
+
background: #fff;
|
32
|
+
border-bottom: 1px solid #e1e5e9;
|
33
|
+
padding: 1rem 2rem;
|
34
|
+
z-index: 1000;
|
35
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
36
|
+
}
|
37
|
+
|
38
|
+
.header h1 {
|
39
|
+
color: #2c3e50;
|
40
|
+
font-size: 1.5rem;
|
41
|
+
margin-bottom: 0.5rem;
|
42
|
+
}
|
43
|
+
|
44
|
+
.header .meta {
|
45
|
+
color: #6c757d;
|
46
|
+
font-size: 0.9rem;
|
47
|
+
}
|
48
|
+
|
49
|
+
.sidebar {
|
50
|
+
width: 300px;
|
51
|
+
background: #fff;
|
52
|
+
border-right: 1px solid #e1e5e9;
|
53
|
+
position: fixed;
|
54
|
+
top: 80px;
|
55
|
+
bottom: 0;
|
56
|
+
overflow-y: auto;
|
57
|
+
padding: 1rem;
|
58
|
+
}
|
59
|
+
|
60
|
+
.sidebar h3 {
|
61
|
+
color: #2c3e50;
|
62
|
+
margin-bottom: 1rem;
|
63
|
+
font-size: 1.1rem;
|
64
|
+
}
|
65
|
+
|
66
|
+
.sidebar ul {
|
67
|
+
list-style: none;
|
68
|
+
}
|
69
|
+
|
70
|
+
.sidebar li {
|
71
|
+
margin-bottom: 0.5rem;
|
72
|
+
}
|
73
|
+
|
74
|
+
.sidebar a {
|
75
|
+
text-decoration: none;
|
76
|
+
color: #495057;
|
77
|
+
padding: 0.5rem;
|
78
|
+
border-radius: 4px;
|
79
|
+
display: block;
|
80
|
+
transition: background-color 0.2s;
|
81
|
+
}
|
82
|
+
|
83
|
+
.sidebar a:hover {
|
84
|
+
background-color: #f8f9fa;
|
85
|
+
}
|
86
|
+
|
87
|
+
.method-badge {
|
88
|
+
display: inline-block;
|
89
|
+
padding: 0.2rem 0.5rem;
|
90
|
+
border-radius: 3px;
|
91
|
+
font-size: 0.7rem;
|
92
|
+
font-weight: bold;
|
93
|
+
margin-right: 0.5rem;
|
94
|
+
min-width: 45px;
|
95
|
+
text-align: center;
|
96
|
+
}
|
97
|
+
|
98
|
+
.method-get { background-color: #28a745; color: white; }
|
99
|
+
.method-post { background-color: #007bff; color: white; }
|
100
|
+
.method-put { background-color: #ffc107; color: black; }
|
101
|
+
.method-delete { background-color: #dc3545; color: white; }
|
102
|
+
.method-patch { background-color: #17a2b8; color: white; }
|
103
|
+
|
104
|
+
.main-content {
|
105
|
+
flex: 1;
|
106
|
+
margin-left: 300px;
|
107
|
+
margin-top: 80px;
|
108
|
+
padding: 2rem;
|
109
|
+
}
|
110
|
+
|
111
|
+
.endpoint {
|
112
|
+
background: #fff;
|
113
|
+
border-radius: 8px;
|
114
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
115
|
+
margin-bottom: 2rem;
|
116
|
+
overflow: hidden;
|
117
|
+
}
|
118
|
+
|
119
|
+
.endpoint-header {
|
120
|
+
padding: 1.5rem;
|
121
|
+
border-bottom: 1px solid #e1e5e9;
|
122
|
+
}
|
123
|
+
|
124
|
+
.endpoint-title {
|
125
|
+
display: flex;
|
126
|
+
align-items: center;
|
127
|
+
margin-bottom: 0.5rem;
|
128
|
+
}
|
129
|
+
|
130
|
+
.endpoint-path {
|
131
|
+
font-family: 'Monaco', 'Menlo', monospace;
|
132
|
+
font-size: 1.1rem;
|
133
|
+
margin-left: 0.5rem;
|
134
|
+
}
|
135
|
+
|
136
|
+
.endpoint-description {
|
137
|
+
color: #6c757d;
|
138
|
+
margin-top: 0.5rem;
|
139
|
+
}
|
140
|
+
|
141
|
+
.endpoint-content {
|
142
|
+
padding: 1.5rem;
|
143
|
+
}
|
144
|
+
|
145
|
+
.section {
|
146
|
+
margin-bottom: 2rem;
|
147
|
+
}
|
148
|
+
|
149
|
+
.section h4 {
|
150
|
+
color: #2c3e50;
|
151
|
+
margin-bottom: 1rem;
|
152
|
+
padding-bottom: 0.5rem;
|
153
|
+
border-bottom: 2px solid #e1e5e9;
|
154
|
+
}
|
155
|
+
|
156
|
+
.params-table {
|
157
|
+
width: 100%;
|
158
|
+
border-collapse: collapse;
|
159
|
+
margin-bottom: 1rem;
|
160
|
+
}
|
161
|
+
|
162
|
+
.params-table th,
|
163
|
+
.params-table td {
|
164
|
+
padding: 0.75rem;
|
165
|
+
text-align: left;
|
166
|
+
border-bottom: 1px solid #e1e5e9;
|
167
|
+
}
|
168
|
+
|
169
|
+
.params-table th {
|
170
|
+
background-color: #f8f9fa;
|
171
|
+
font-weight: 600;
|
172
|
+
}
|
173
|
+
|
174
|
+
.param-name {
|
175
|
+
font-family: 'Monaco', 'Menlo', monospace;
|
176
|
+
background-color: #f8f9fa;
|
177
|
+
padding: 0.2rem 0.4rem;
|
178
|
+
border-radius: 3px;
|
179
|
+
}
|
180
|
+
|
181
|
+
.code-block {
|
182
|
+
background-color: #f8f9fa;
|
183
|
+
border: 1px solid #e1e5e9;
|
184
|
+
border-radius: 4px;
|
185
|
+
padding: 1rem;
|
186
|
+
overflow-x: auto;
|
187
|
+
font-family: 'Monaco', 'Menlo', monospace;
|
188
|
+
font-size: 0.9rem;
|
189
|
+
}
|
190
|
+
|
191
|
+
.try-it-section {
|
192
|
+
background-color: #f8f9fa;
|
193
|
+
border-radius: 8px;
|
194
|
+
padding: 1.5rem;
|
195
|
+
margin-top: 2rem;
|
196
|
+
}
|
197
|
+
|
198
|
+
.try-it-form {
|
199
|
+
display: grid;
|
200
|
+
gap: 1rem;
|
201
|
+
}
|
202
|
+
|
203
|
+
.form-group {
|
204
|
+
display: flex;
|
205
|
+
flex-direction: column;
|
206
|
+
}
|
207
|
+
|
208
|
+
.form-group label {
|
209
|
+
margin-bottom: 0.5rem;
|
210
|
+
font-weight: 600;
|
211
|
+
}
|
212
|
+
|
213
|
+
.form-group input,
|
214
|
+
.form-group textarea {
|
215
|
+
padding: 0.5rem;
|
216
|
+
border: 1px solid #ced4da;
|
217
|
+
border-radius: 4px;
|
218
|
+
font-family: inherit;
|
219
|
+
}
|
220
|
+
|
221
|
+
.btn {
|
222
|
+
padding: 0.75rem 1.5rem;
|
223
|
+
border: none;
|
224
|
+
border-radius: 4px;
|
225
|
+
cursor: pointer;
|
226
|
+
font-weight: 600;
|
227
|
+
transition: background-color 0.2s;
|
228
|
+
}
|
229
|
+
|
230
|
+
.btn-primary {
|
231
|
+
background-color: #007bff;
|
232
|
+
color: white;
|
233
|
+
}
|
234
|
+
|
235
|
+
.btn-primary:hover {
|
236
|
+
background-color: #0056b3;
|
237
|
+
}
|
238
|
+
|
239
|
+
.response-section {
|
240
|
+
margin-top: 1rem;
|
241
|
+
padding: 1rem;
|
242
|
+
background-color: #fff;
|
243
|
+
border-radius: 4px;
|
244
|
+
border: 1px solid #e1e5e9;
|
245
|
+
}
|
246
|
+
|
247
|
+
.required-badge {
|
248
|
+
background-color: #dc3545;
|
249
|
+
color: white;
|
250
|
+
padding: 0.1rem 0.3rem;
|
251
|
+
border-radius: 3px;
|
252
|
+
font-size: 0.7rem;
|
253
|
+
margin-left: 0.5rem;
|
254
|
+
}
|
255
|
+
|
256
|
+
.optional-badge {
|
257
|
+
background-color: #6c757d;
|
258
|
+
color: white;
|
259
|
+
padding: 0.1rem 0.3rem;
|
260
|
+
border-radius: 3px;
|
261
|
+
font-size: 0.7rem;
|
262
|
+
margin-left: 0.5rem;
|
263
|
+
}
|
264
|
+
</style>
|
265
|
+
|
266
|
+
</head>
|
267
|
+
<body>
|
268
|
+
<div class="container">
|
269
|
+
<div class="header">
|
270
|
+
<h1>User Management API</h1>
|
271
|
+
<div class="meta">
|
272
|
+
Version: 2.0.0 | Base URL: <code>https://api.example.com/v2</code>
|
273
|
+
</div>
|
274
|
+
</div>
|
275
|
+
|
276
|
+
<div class="sidebar">
|
277
|
+
<h3>Endpoints</h3>
|
278
|
+
<ul>
|
279
|
+
<li>
|
280
|
+
<a href="#get-users">
|
281
|
+
<span class="method-badge method-get">GET</span>
|
282
|
+
Get all users
|
283
|
+
</a>
|
284
|
+
</li>
|
285
|
+
<li>
|
286
|
+
<a href="#get-usersid">
|
287
|
+
<span class="method-badge method-get">GET</span>
|
288
|
+
Get user by ID
|
289
|
+
</a>
|
290
|
+
</li>
|
291
|
+
<li>
|
292
|
+
<a href="#post-users">
|
293
|
+
<span class="method-badge method-post">POST</span>
|
294
|
+
Create new user
|
295
|
+
</a>
|
296
|
+
</li>
|
297
|
+
<li>
|
298
|
+
<a href="#put-usersid">
|
299
|
+
<span class="method-badge method-put">PUT</span>
|
300
|
+
Update user
|
301
|
+
</a>
|
302
|
+
</li>
|
303
|
+
<li>
|
304
|
+
<a href="#delete-usersid">
|
305
|
+
<span class="method-badge method-delete">DELETE</span>
|
306
|
+
Delete user
|
307
|
+
</a>
|
308
|
+
</li>
|
309
|
+
<li>
|
310
|
+
<a href="#get-userssearch">
|
311
|
+
<span class="method-badge method-get">GET</span>
|
312
|
+
Search users
|
313
|
+
</a>
|
314
|
+
</li>
|
315
|
+
|
316
|
+
</ul>
|
317
|
+
</div>
|
318
|
+
|
319
|
+
<div class="main-content">
|
320
|
+
<div class="endpoint" id="get-users">
|
321
|
+
<div class="endpoint-header">
|
322
|
+
<div class="endpoint-title">
|
323
|
+
<span class="method-badge method-get">GET</span>
|
324
|
+
<span class="endpoint-path">/users</span>
|
325
|
+
</div>
|
326
|
+
<div class="endpoint-summary"><strong>Get all users</strong></div>
|
327
|
+
<div class="endpoint-description">Retrieve a paginated list of all users in the system</div>
|
328
|
+
</div>
|
329
|
+
<div class="endpoint-content">
|
330
|
+
<div class="section">
|
331
|
+
<h4>Response</h4>
|
332
|
+
<p><strong>Content-Type:</strong> <code>application/json</code></p>
|
333
|
+
<div class="code-block">[
|
334
|
+
{
|
335
|
+
"id": 123,
|
336
|
+
"name": "example string",
|
337
|
+
"email": "example string",
|
338
|
+
"created_at": "2025-01-15T10:30:00Z"
|
339
|
+
}
|
340
|
+
]</div>
|
341
|
+
|
342
|
+
</div>
|
343
|
+
<div class="try-it-section">
|
344
|
+
<h4>Try it out</h4>
|
345
|
+
<form class="try-it-form" onsubmit="return tryRequest('get_users', 'GET', '/users')">
|
346
|
+
|
347
|
+
<button type="submit" class="btn btn-primary">Send Request</button>
|
348
|
+
</form>
|
349
|
+
<div id="get_users_response" class="response-section" style="display: none;">
|
350
|
+
<h5>Response</h5>
|
351
|
+
<div class="code-block" id="get_users_response_content"></div>
|
352
|
+
</div>
|
353
|
+
</div>
|
354
|
+
|
355
|
+
</div>
|
356
|
+
</div>
|
357
|
+
<div class="endpoint" id="get-usersid">
|
358
|
+
<div class="endpoint-header">
|
359
|
+
<div class="endpoint-title">
|
360
|
+
<span class="method-badge method-get">GET</span>
|
361
|
+
<span class="endpoint-path">/users/:id</span>
|
362
|
+
</div>
|
363
|
+
<div class="endpoint-summary"><strong>Get user by ID</strong></div>
|
364
|
+
<div class="endpoint-description">Retrieve a specific user by their unique identifier</div>
|
365
|
+
</div>
|
366
|
+
<div class="endpoint-content">
|
367
|
+
<div class="section">
|
368
|
+
<h4>Path Parameters</h4>
|
369
|
+
<table class="params-table">
|
370
|
+
<thead>
|
371
|
+
<tr>
|
372
|
+
<th>Parameter</th>
|
373
|
+
<th>Type</th>
|
374
|
+
<th>Required</th>
|
375
|
+
<th>Description</th>
|
376
|
+
</tr>
|
377
|
+
</thead>
|
378
|
+
<tbody>
|
379
|
+
<tr>
|
380
|
+
<td><code class="param-name">id</code></td>
|
381
|
+
<td>integer</td>
|
382
|
+
<td><span class="required-badge">Required</span></td>
|
383
|
+
<td>No description</td>
|
384
|
+
</tr>
|
385
|
+
|
386
|
+
</tbody>
|
387
|
+
</table>
|
388
|
+
</div>
|
389
|
+
<div class="section">
|
390
|
+
<h4>Response</h4>
|
391
|
+
<p><strong>Content-Type:</strong> <code>application/json</code></p>
|
392
|
+
<div class="code-block">{
|
393
|
+
"id": 123,
|
394
|
+
"name": "example string",
|
395
|
+
"email": "example string",
|
396
|
+
"created_at": "2025-01-15T10:30:00Z",
|
397
|
+
"updated_at": "2025-01-15T10:30:00Z"
|
398
|
+
}</div>
|
399
|
+
|
400
|
+
</div>
|
401
|
+
<div class="try-it-section">
|
402
|
+
<h4>Try it out</h4>
|
403
|
+
<form class="try-it-form" onsubmit="return tryRequest('get_usersid', 'GET', '/users/:id')">
|
404
|
+
<div class="form-group">
|
405
|
+
<label for="get_usersid_id">id (path parameter)</label>
|
406
|
+
<input type="text" id="get_usersid_id" name="id" placeholder="Enter id" required>
|
407
|
+
</div>
|
408
|
+
|
409
|
+
<button type="submit" class="btn btn-primary">Send Request</button>
|
410
|
+
</form>
|
411
|
+
<div id="get_usersid_response" class="response-section" style="display: none;">
|
412
|
+
<h5>Response</h5>
|
413
|
+
<div class="code-block" id="get_usersid_response_content"></div>
|
414
|
+
</div>
|
415
|
+
</div>
|
416
|
+
|
417
|
+
</div>
|
418
|
+
</div>
|
419
|
+
<div class="endpoint" id="post-users">
|
420
|
+
<div class="endpoint-header">
|
421
|
+
<div class="endpoint-title">
|
422
|
+
<span class="method-badge method-post">POST</span>
|
423
|
+
<span class="endpoint-path">/users</span>
|
424
|
+
</div>
|
425
|
+
<div class="endpoint-summary"><strong>Create new user</strong></div>
|
426
|
+
<div class="endpoint-description">Create a new user account with the provided information</div>
|
427
|
+
</div>
|
428
|
+
<div class="endpoint-content">
|
429
|
+
<div class="section">
|
430
|
+
<h4>Request Body</h4>
|
431
|
+
<p><strong>Content-Type:</strong> <code>application/json</code></p>
|
432
|
+
<div class="code-block">{
|
433
|
+
"name": "example string",
|
434
|
+
"email": "example string",
|
435
|
+
"password": "example string"
|
436
|
+
}</div>
|
437
|
+
</div>
|
438
|
+
<div class="section">
|
439
|
+
<h4>Response</h4>
|
440
|
+
<p><strong>Content-Type:</strong> <code>application/json</code></p>
|
441
|
+
<div class="code-block">{
|
442
|
+
"id": 123,
|
443
|
+
"name": "example string",
|
444
|
+
"email": "example string",
|
445
|
+
"created_at": "2025-01-15T10:30:00Z"
|
446
|
+
}</div>
|
447
|
+
|
448
|
+
</div>
|
449
|
+
<div class="try-it-section">
|
450
|
+
<h4>Try it out</h4>
|
451
|
+
<form class="try-it-form" onsubmit="return tryRequest('post_users', 'POST', '/users')">
|
452
|
+
<div class="form-group">
|
453
|
+
<label for="post_users_body">Request Body (JSON)</label>
|
454
|
+
<textarea id="post_users_body" name="body" rows="6" placeholder="Enter JSON body">{
|
455
|
+
"name": "example string",
|
456
|
+
"email": "example string",
|
457
|
+
"password": "example string"
|
458
|
+
}</textarea>
|
459
|
+
</div>
|
460
|
+
|
461
|
+
<button type="submit" class="btn btn-primary">Send Request</button>
|
462
|
+
</form>
|
463
|
+
<div id="post_users_response" class="response-section" style="display: none;">
|
464
|
+
<h5>Response</h5>
|
465
|
+
<div class="code-block" id="post_users_response_content"></div>
|
466
|
+
</div>
|
467
|
+
</div>
|
468
|
+
|
469
|
+
</div>
|
470
|
+
</div>
|
471
|
+
<div class="endpoint" id="put-usersid">
|
472
|
+
<div class="endpoint-header">
|
473
|
+
<div class="endpoint-title">
|
474
|
+
<span class="method-badge method-put">PUT</span>
|
475
|
+
<span class="endpoint-path">/users/:id</span>
|
476
|
+
</div>
|
477
|
+
<div class="endpoint-summary"><strong>Update user</strong></div>
|
478
|
+
<div class="endpoint-description">Update an existing user's information</div>
|
479
|
+
</div>
|
480
|
+
<div class="endpoint-content">
|
481
|
+
<div class="section">
|
482
|
+
<h4>Path Parameters</h4>
|
483
|
+
<table class="params-table">
|
484
|
+
<thead>
|
485
|
+
<tr>
|
486
|
+
<th>Parameter</th>
|
487
|
+
<th>Type</th>
|
488
|
+
<th>Required</th>
|
489
|
+
<th>Description</th>
|
490
|
+
</tr>
|
491
|
+
</thead>
|
492
|
+
<tbody>
|
493
|
+
<tr>
|
494
|
+
<td><code class="param-name">id</code></td>
|
495
|
+
<td>integer</td>
|
496
|
+
<td><span class="required-badge">Required</span></td>
|
497
|
+
<td>No description</td>
|
498
|
+
</tr>
|
499
|
+
|
500
|
+
</tbody>
|
501
|
+
</table>
|
502
|
+
</div>
|
503
|
+
<div class="section">
|
504
|
+
<h4>Request Body</h4>
|
505
|
+
<p><strong>Content-Type:</strong> <code>application/json</code></p>
|
506
|
+
<div class="code-block">{
|
507
|
+
"name": "example string",
|
508
|
+
"email": "example string"
|
509
|
+
}</div>
|
510
|
+
</div>
|
511
|
+
<div class="section">
|
512
|
+
<h4>Response</h4>
|
513
|
+
<p><strong>Content-Type:</strong> <code>application/json</code></p>
|
514
|
+
<div class="code-block">{
|
515
|
+
"id": 123,
|
516
|
+
"name": "example string",
|
517
|
+
"email": "example string",
|
518
|
+
"updated_at": "2025-01-15T10:30:00Z"
|
519
|
+
}</div>
|
520
|
+
|
521
|
+
</div>
|
522
|
+
<div class="try-it-section">
|
523
|
+
<h4>Try it out</h4>
|
524
|
+
<form class="try-it-form" onsubmit="return tryRequest('put_usersid', 'PUT', '/users/:id')">
|
525
|
+
<div class="form-group">
|
526
|
+
<label for="put_usersid_id">id (path parameter)</label>
|
527
|
+
<input type="text" id="put_usersid_id" name="id" placeholder="Enter id" required>
|
528
|
+
</div>
|
529
|
+
<div class="form-group">
|
530
|
+
<label for="put_usersid_body">Request Body (JSON)</label>
|
531
|
+
<textarea id="put_usersid_body" name="body" rows="6" placeholder="Enter JSON body">{
|
532
|
+
"name": "example string",
|
533
|
+
"email": "example string"
|
534
|
+
}</textarea>
|
535
|
+
</div>
|
536
|
+
|
537
|
+
<button type="submit" class="btn btn-primary">Send Request</button>
|
538
|
+
</form>
|
539
|
+
<div id="put_usersid_response" class="response-section" style="display: none;">
|
540
|
+
<h5>Response</h5>
|
541
|
+
<div class="code-block" id="put_usersid_response_content"></div>
|
542
|
+
</div>
|
543
|
+
</div>
|
544
|
+
|
545
|
+
</div>
|
546
|
+
</div>
|
547
|
+
<div class="endpoint" id="delete-usersid">
|
548
|
+
<div class="endpoint-header">
|
549
|
+
<div class="endpoint-title">
|
550
|
+
<span class="method-badge method-delete">DELETE</span>
|
551
|
+
<span class="endpoint-path">/users/:id</span>
|
552
|
+
</div>
|
553
|
+
<div class="endpoint-summary"><strong>Delete user</strong></div>
|
554
|
+
<div class="endpoint-description">Delete a user account permanently</div>
|
555
|
+
</div>
|
556
|
+
<div class="endpoint-content">
|
557
|
+
<div class="section">
|
558
|
+
<h4>Path Parameters</h4>
|
559
|
+
<table class="params-table">
|
560
|
+
<thead>
|
561
|
+
<tr>
|
562
|
+
<th>Parameter</th>
|
563
|
+
<th>Type</th>
|
564
|
+
<th>Required</th>
|
565
|
+
<th>Description</th>
|
566
|
+
</tr>
|
567
|
+
</thead>
|
568
|
+
<tbody>
|
569
|
+
<tr>
|
570
|
+
<td><code class="param-name">id</code></td>
|
571
|
+
<td>integer</td>
|
572
|
+
<td><span class="required-badge">Required</span></td>
|
573
|
+
<td>No description</td>
|
574
|
+
</tr>
|
575
|
+
|
576
|
+
</tbody>
|
577
|
+
</table>
|
578
|
+
</div>
|
579
|
+
<div class="section">
|
580
|
+
<h4>Response</h4>
|
581
|
+
<p><strong>Content-Type:</strong> <code>application/json</code></p>
|
582
|
+
<div class="code-block">{
|
583
|
+
"success": true,
|
584
|
+
"message": "example string"
|
585
|
+
}</div>
|
586
|
+
|
587
|
+
</div>
|
588
|
+
<div class="try-it-section">
|
589
|
+
<h4>Try it out</h4>
|
590
|
+
<form class="try-it-form" onsubmit="return tryRequest('delete_usersid', 'DELETE', '/users/:id')">
|
591
|
+
<div class="form-group">
|
592
|
+
<label for="delete_usersid_id">id (path parameter)</label>
|
593
|
+
<input type="text" id="delete_usersid_id" name="id" placeholder="Enter id" required>
|
594
|
+
</div>
|
595
|
+
|
596
|
+
<button type="submit" class="btn btn-primary">Send Request</button>
|
597
|
+
</form>
|
598
|
+
<div id="delete_usersid_response" class="response-section" style="display: none;">
|
599
|
+
<h5>Response</h5>
|
600
|
+
<div class="code-block" id="delete_usersid_response_content"></div>
|
601
|
+
</div>
|
602
|
+
</div>
|
603
|
+
|
604
|
+
</div>
|
605
|
+
</div>
|
606
|
+
<div class="endpoint" id="get-userssearch">
|
607
|
+
<div class="endpoint-header">
|
608
|
+
<div class="endpoint-title">
|
609
|
+
<span class="method-badge method-get">GET</span>
|
610
|
+
<span class="endpoint-path">/users/search</span>
|
611
|
+
</div>
|
612
|
+
<div class="endpoint-summary"><strong>Search users</strong></div>
|
613
|
+
<div class="endpoint-description">Search for users by name or email with pagination support</div>
|
614
|
+
</div>
|
615
|
+
<div class="endpoint-content">
|
616
|
+
<div class="section">
|
617
|
+
<h4>Query Parameters</h4>
|
618
|
+
<table class="params-table">
|
619
|
+
<thead>
|
620
|
+
<tr>
|
621
|
+
<th>Parameter</th>
|
622
|
+
<th>Type</th>
|
623
|
+
<th>Required</th>
|
624
|
+
<th>Description</th>
|
625
|
+
</tr>
|
626
|
+
</thead>
|
627
|
+
<tbody>
|
628
|
+
<tr>
|
629
|
+
<td><code class="param-name">q</code></td>
|
630
|
+
<td>string</td>
|
631
|
+
<td><span class="required-badge">Required</span></td>
|
632
|
+
<td>No description</td>
|
633
|
+
</tr>
|
634
|
+
<tr>
|
635
|
+
<td><code class="param-name">limit</code></td>
|
636
|
+
<td>integer</td>
|
637
|
+
<td><span class="optional-badge">Optional</span></td>
|
638
|
+
<td>No description</td>
|
639
|
+
</tr>
|
640
|
+
<tr>
|
641
|
+
<td><code class="param-name">offset</code></td>
|
642
|
+
<td>integer</td>
|
643
|
+
<td><span class="optional-badge">Optional</span></td>
|
644
|
+
<td>No description</td>
|
645
|
+
</tr>
|
646
|
+
|
647
|
+
</tbody>
|
648
|
+
</table>
|
649
|
+
</div>
|
650
|
+
<div class="section">
|
651
|
+
<h4>Response</h4>
|
652
|
+
<p><strong>Content-Type:</strong> <code>application/json</code></p>
|
653
|
+
<div class="code-block">{
|
654
|
+
"users": [
|
655
|
+
{
|
656
|
+
"id": 123,
|
657
|
+
"name": "example string",
|
658
|
+
"email": "example string"
|
659
|
+
}
|
660
|
+
],
|
661
|
+
"total": 123,
|
662
|
+
"limit": 123,
|
663
|
+
"offset": 123
|
664
|
+
}</div>
|
665
|
+
|
666
|
+
</div>
|
667
|
+
<div class="try-it-section">
|
668
|
+
<h4>Try it out</h4>
|
669
|
+
<form class="try-it-form" onsubmit="return tryRequest('get_userssearch', 'GET', '/users/search')">
|
670
|
+
<div class="form-group">
|
671
|
+
<label for="get_userssearch_q">q (query parameter)</label>
|
672
|
+
<input type="text" id="get_userssearch_q" name="q" placeholder="Enter q" required>
|
673
|
+
</div>
|
674
|
+
<div class="form-group">
|
675
|
+
<label for="get_userssearch_limit">limit (query parameter)</label>
|
676
|
+
<input type="text" id="get_userssearch_limit" name="limit" placeholder="Enter limit" >
|
677
|
+
</div>
|
678
|
+
<div class="form-group">
|
679
|
+
<label for="get_userssearch_offset">offset (query parameter)</label>
|
680
|
+
<input type="text" id="get_userssearch_offset" name="offset" placeholder="Enter offset" >
|
681
|
+
</div>
|
682
|
+
|
683
|
+
<button type="submit" class="btn btn-primary">Send Request</button>
|
684
|
+
</form>
|
685
|
+
<div id="get_userssearch_response" class="response-section" style="display: none;">
|
686
|
+
<h5>Response</h5>
|
687
|
+
<div class="code-block" id="get_userssearch_response_content"></div>
|
688
|
+
</div>
|
689
|
+
</div>
|
690
|
+
|
691
|
+
</div>
|
692
|
+
</div>
|
693
|
+
|
694
|
+
</div>
|
695
|
+
|
696
|
+
</div>
|
697
|
+
<script>
|
698
|
+
async function tryRequest(endpointId, method, path) {
|
699
|
+
event.preventDefault();
|
700
|
+
|
701
|
+
const form = event.target;
|
702
|
+
const formData = new FormData(form);
|
703
|
+
|
704
|
+
// Build URL with path parameters
|
705
|
+
let url = 'https://api.example.com/v2' + path;
|
706
|
+
const pathParams = {};
|
707
|
+
const queryParams = {};
|
708
|
+
let body = null;
|
709
|
+
|
710
|
+
// Process form data
|
711
|
+
for (const [key, value] of formData.entries()) {
|
712
|
+
if (key === 'body') {
|
713
|
+
if (value.trim()) {
|
714
|
+
try {
|
715
|
+
body = JSON.parse(value);
|
716
|
+
} catch (e) {
|
717
|
+
alert('Invalid JSON in request body');
|
718
|
+
return false;
|
719
|
+
}
|
720
|
+
}
|
721
|
+
} else if (path.includes(':' + key)) {
|
722
|
+
pathParams[key] = value;
|
723
|
+
} else if (value.trim()) {
|
724
|
+
queryParams[key] = value;
|
725
|
+
}
|
726
|
+
}
|
727
|
+
|
728
|
+
// Replace path parameters
|
729
|
+
for (const [key, value] of Object.entries(pathParams)) {
|
730
|
+
url = url.replace(':' + key, encodeURIComponent(value));
|
731
|
+
}
|
732
|
+
|
733
|
+
// Add query parameters
|
734
|
+
const queryString = new URLSearchParams(queryParams).toString();
|
735
|
+
if (queryString) {
|
736
|
+
url += '?' + queryString;
|
737
|
+
}
|
738
|
+
|
739
|
+
// Prepare request options
|
740
|
+
const options = {
|
741
|
+
method: method,
|
742
|
+
headers: {
|
743
|
+
'Content-Type': 'application/json',
|
744
|
+
'Accept': 'application/json'
|
745
|
+
}
|
746
|
+
};
|
747
|
+
|
748
|
+
if (body && (method === 'POST' || method === 'PUT' || method === 'PATCH')) {
|
749
|
+
options.body = JSON.stringify(body);
|
750
|
+
}
|
751
|
+
|
752
|
+
// Show loading state
|
753
|
+
const responseDiv = document.getElementById(endpointId + '_response');
|
754
|
+
const responseContent = document.getElementById(endpointId + '_response_content');
|
755
|
+
responseDiv.style.display = 'block';
|
756
|
+
responseContent.textContent = 'Loading...';
|
757
|
+
|
758
|
+
try {
|
759
|
+
const response = await fetch(url, options);
|
760
|
+
const responseText = await response.text();
|
761
|
+
|
762
|
+
let responseData;
|
763
|
+
try {
|
764
|
+
responseData = JSON.parse(responseText);
|
765
|
+
responseContent.innerHTML = `
|
766
|
+
<strong>Status:</strong> ${response.status} ${response.statusText}<br><br>
|
767
|
+
<strong>Response:</strong><br>
|
768
|
+
${JSON.stringify(responseData, null, 2)}
|
769
|
+
`;
|
770
|
+
} catch (e) {
|
771
|
+
responseContent.innerHTML = `
|
772
|
+
<strong>Status:</strong> ${response.status} ${response.statusText}<br><br>
|
773
|
+
<strong>Response:</strong><br>
|
774
|
+
${responseText}
|
775
|
+
`;
|
776
|
+
}
|
777
|
+
} catch (error) {
|
778
|
+
responseContent.innerHTML = `
|
779
|
+
<strong>Error:</strong><br>
|
780
|
+
${error.message}
|
781
|
+
`;
|
782
|
+
}
|
783
|
+
|
784
|
+
return false;
|
785
|
+
}
|
786
|
+
</script>
|
787
|
+
|
788
|
+
</body>
|
789
|
+
</html>
|