drf-to-mkdoc 0.2.2__py3-none-any.whl → 0.2.4__py3-none-any.whl
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.
Potentially problematic release.
This version of drf-to-mkdoc might be problematic. Click here for more details.
- drf_to_mkdoc/conf/defaults.py +1 -0
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/field-sections-loader.js +29 -0
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/query-parameters-loader.js +16 -0
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/field-extractor.js +200 -0
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/form-manager.js +465 -0
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/main.js +50 -0
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/modal.js +359 -0
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/query-parameters-extractor.js +94 -0
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/request-executor.js +327 -0
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/response-modal.js +173 -0
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/suggestions.js +123 -0
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/tabs.js +77 -0
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/badges.css +13 -5
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/theme-toggle.css +297 -25
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/try-out/fab.css +204 -0
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/try-out/response.css +323 -0
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/try-out/variables.css +139 -0
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/field-sections.css +136 -0
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/buttons.css +71 -0
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/fab.css +47 -0
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/form.css +663 -0
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/key-value.css +161 -0
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/main.css +57 -0
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/modal.css +334 -0
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/response.css +618 -0
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/tabs.css +114 -0
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/variables.css +94 -0
- drf_to_mkdoc/templates/endpoints/detail/base.html +3 -1
- drf_to_mkdoc/templates/endpoints/detail/query_parameters.html +1 -8
- drf_to_mkdoc/templates/endpoints/detail/request_body.html +2 -0
- drf_to_mkdoc/templates/endpoints/detail/responses.html +4 -4
- drf_to_mkdoc/templates/try-out/fab.html +68 -0
- drf_to_mkdoc/templates/try-out/form.html +260 -0
- drf_to_mkdoc/templates/try-out/main.html +4 -0
- drf_to_mkdoc/templates/try-out/modal.html +82 -0
- drf_to_mkdoc/templates/try-out/response-modal.html +149 -0
- drf_to_mkdoc/templatetags/custom_filters.py +33 -1
- drf_to_mkdoc/utils/commons/schema_utils.py +5 -14
- drf_to_mkdoc/utils/endpoint_detail_generator.py +141 -21
- drf_to_mkdoc/utils/extractors/query_parameter_extractors.py +0 -15
- {drf_to_mkdoc-0.2.2.dist-info → drf_to_mkdoc-0.2.4.dist-info}/METADATA +68 -9
- {drf_to_mkdoc-0.2.2.dist-info → drf_to_mkdoc-0.2.4.dist-info}/RECORD +45 -18
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out-sidebar.js +0 -879
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/try-out-sidebar.css +0 -728
- {drf_to_mkdoc-0.2.2.dist-info → drf_to_mkdoc-0.2.4.dist-info}/WHEEL +0 -0
- {drf_to_mkdoc-0.2.2.dist-info → drf_to_mkdoc-0.2.4.dist-info}/licenses/LICENSE +0 -0
- {drf_to_mkdoc-0.2.2.dist-info → drf_to_mkdoc-0.2.4.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
// Request execution functionality
|
|
2
|
+
const RequestExecutor = {
|
|
3
|
+
// Show validation error in the appropriate section
|
|
4
|
+
showValidationError: function(message, section) {
|
|
5
|
+
let errorContainer;
|
|
6
|
+
|
|
7
|
+
if (section === 'json') {
|
|
8
|
+
// Show error in JSON validation status
|
|
9
|
+
errorContainer = document.querySelector('.validation-status');
|
|
10
|
+
if (errorContainer) {
|
|
11
|
+
errorContainer.textContent = '✗ ' + message;
|
|
12
|
+
errorContainer.className = 'validation-status invalid';
|
|
13
|
+
}
|
|
14
|
+
} else if (section === 'parameters') {
|
|
15
|
+
// Show error in parameters section
|
|
16
|
+
const parametersTab = document.querySelector('[data-tab="parameters"]');
|
|
17
|
+
if (parametersTab) {
|
|
18
|
+
window.TabManager?.switchTab(parametersTab);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Create or update error message
|
|
22
|
+
errorContainer = document.querySelector('#parametersError');
|
|
23
|
+
if (!errorContainer) {
|
|
24
|
+
errorContainer = document.createElement('div');
|
|
25
|
+
errorContainer.id = 'parametersError';
|
|
26
|
+
errorContainer.className = 'error-message';
|
|
27
|
+
const parametersSection = document.querySelector('#parametersTab .form-section');
|
|
28
|
+
if (parametersSection) {
|
|
29
|
+
parametersSection.insertBefore(errorContainer, parametersSection.firstChild);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
errorContainer.textContent = message;
|
|
33
|
+
errorContainer.style.display = 'block';
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
// Clear validation errors
|
|
38
|
+
clearValidationErrors: function() {
|
|
39
|
+
// Clear JSON validation status
|
|
40
|
+
const jsonStatus = document.querySelector('.validation-status');
|
|
41
|
+
if (jsonStatus) {
|
|
42
|
+
jsonStatus.textContent = '';
|
|
43
|
+
jsonStatus.className = 'validation-status';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Clear parameters error
|
|
47
|
+
const paramsError = document.querySelector('#parametersError');
|
|
48
|
+
if (paramsError) {
|
|
49
|
+
paramsError.style.display = 'none';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Clear all input validation states
|
|
53
|
+
document.querySelectorAll('.error').forEach(el => {
|
|
54
|
+
el.classList.remove('error');
|
|
55
|
+
});
|
|
56
|
+
document.querySelectorAll('.validation-message').forEach(msg => {
|
|
57
|
+
msg.style.display = 'none';
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// Form validation
|
|
62
|
+
validateInput: function(input) {
|
|
63
|
+
const isValid = input.value.trim() !== '';
|
|
64
|
+
const validationMessage = input.parentElement.querySelector('.validation-message');
|
|
65
|
+
|
|
66
|
+
if (!isValid) {
|
|
67
|
+
input.classList.add('error');
|
|
68
|
+
if (validationMessage) {
|
|
69
|
+
validationMessage.textContent = 'This field is required';
|
|
70
|
+
validationMessage.style.display = 'block';
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
input.classList.remove('error');
|
|
74
|
+
if (validationMessage) {
|
|
75
|
+
validationMessage.textContent = '';
|
|
76
|
+
validationMessage.style.display = 'none';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return isValid;
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// Build complete request data
|
|
84
|
+
buildRequestData: function() {
|
|
85
|
+
const baseUrl = document.getElementById('baseUrl')?.value || '';
|
|
86
|
+
const pathDisplay = document.querySelector('.path-display')?.textContent || '';
|
|
87
|
+
|
|
88
|
+
// Build full URL
|
|
89
|
+
let fullUrl = baseUrl + pathDisplay;
|
|
90
|
+
|
|
91
|
+
// Replace path parameters
|
|
92
|
+
const pathParams = {};
|
|
93
|
+
document.querySelectorAll('#pathParams input').forEach(input => {
|
|
94
|
+
const param = input.dataset.param;
|
|
95
|
+
if (param && input.value) {
|
|
96
|
+
pathParams[param] = input.value;
|
|
97
|
+
fullUrl = fullUrl.replace(`{${param}}`, encodeURIComponent(input.value));
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Collect query parameters
|
|
102
|
+
const queryParams = {};
|
|
103
|
+
document.querySelectorAll('#queryParams .parameter-item').forEach(item => {
|
|
104
|
+
const nameInput = item.querySelector('.name-input');
|
|
105
|
+
const valueInput = item.querySelector('.value-input');
|
|
106
|
+
if (nameInput?.value && valueInput?.value) {
|
|
107
|
+
queryParams[nameInput.value] = valueInput.value;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Add query parameters to URL
|
|
112
|
+
if (Object.keys(queryParams).length > 0) {
|
|
113
|
+
const queryString = new URLSearchParams(queryParams).toString();
|
|
114
|
+
fullUrl += (fullUrl.includes('?') ? '&' : '?') + queryString;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Collect headers
|
|
118
|
+
const headers = {};
|
|
119
|
+
document.querySelectorAll('#requestHeaders .header-item').forEach(item => {
|
|
120
|
+
const nameInput = item.querySelector('.name-input');
|
|
121
|
+
const valueInput = item.querySelector('.value-input');
|
|
122
|
+
if (nameInput?.value && valueInput?.value) {
|
|
123
|
+
headers[nameInput.value] = valueInput.value;
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Add default headers if not already set
|
|
128
|
+
if (!headers['Accept']) {
|
|
129
|
+
headers['Accept'] = '*/*';
|
|
130
|
+
}
|
|
131
|
+
if (!headers['User-Agent']) {
|
|
132
|
+
headers['User-Agent'] = navigator.userAgent;
|
|
133
|
+
}
|
|
134
|
+
if (!headers['Accept-Language']) {
|
|
135
|
+
headers['Accept-Language'] = navigator.language || 'en-US,en;q=0.9';
|
|
136
|
+
}
|
|
137
|
+
if (!headers['Accept-Encoding']) {
|
|
138
|
+
headers['Accept-Encoding'] = 'gzip, deflate, br';
|
|
139
|
+
}
|
|
140
|
+
if (!headers['Connection']) {
|
|
141
|
+
headers['Connection'] = 'keep-alive';
|
|
142
|
+
}
|
|
143
|
+
if (!headers['Cache-Control']) {
|
|
144
|
+
headers['Cache-Control'] = 'no-cache';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Add cookies if available
|
|
148
|
+
if (document.cookie) {
|
|
149
|
+
headers['Cookie'] = document.cookie;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Get request body
|
|
153
|
+
const bodyEditor = document.getElementById('requestBody');
|
|
154
|
+
let body = null;
|
|
155
|
+
if (bodyEditor?.value.trim()) {
|
|
156
|
+
try {
|
|
157
|
+
body = JSON.parse(bodyEditor.value);
|
|
158
|
+
} catch (e) {
|
|
159
|
+
body = bodyEditor.value;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Get method
|
|
164
|
+
const methodBadge = document.querySelector('.method-badge');
|
|
165
|
+
const method = methodBadge?.dataset.method || 'GET';
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
url: fullUrl,
|
|
169
|
+
method,
|
|
170
|
+
headers,
|
|
171
|
+
body,
|
|
172
|
+
pathParams,
|
|
173
|
+
queryParams
|
|
174
|
+
};
|
|
175
|
+
},
|
|
176
|
+
async executeRequest() {
|
|
177
|
+
const executeBtn = document.querySelector('[data-action="send"], .primary-button, .primary-btn, #executeBtn');
|
|
178
|
+
if (!executeBtn) {
|
|
179
|
+
console.warn('Execute button not found');
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Validate required fields
|
|
184
|
+
const requiredInputs = document.querySelectorAll('#pathParams input[required]');
|
|
185
|
+
let emptyFields = [];
|
|
186
|
+
|
|
187
|
+
requiredInputs.forEach(input => {
|
|
188
|
+
if (!input.value.trim()) {
|
|
189
|
+
const paramName = input.dataset.param || 'parameter';
|
|
190
|
+
emptyFields.push(paramName);
|
|
191
|
+
this.validateInput(input);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
if (emptyFields.length > 0) {
|
|
196
|
+
this.showValidationError(`Please fill in required fields: ${emptyFields.join(', ')}`, 'parameters');
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Show loading state
|
|
201
|
+
this.setLoadingState(executeBtn, true);
|
|
202
|
+
|
|
203
|
+
const startTime = Date.now();
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const requestData = this.buildRequestData();
|
|
207
|
+
|
|
208
|
+
const requestOptions = {
|
|
209
|
+
method: requestData.method.toUpperCase(),
|
|
210
|
+
headers: requestData.headers
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// Add body for non-GET requests
|
|
214
|
+
if (requestData.body && !['GET', 'HEAD'].includes(requestData.method.toUpperCase())) {
|
|
215
|
+
if (typeof requestData.body === 'string') {
|
|
216
|
+
requestOptions.body = requestData.body;
|
|
217
|
+
} else {
|
|
218
|
+
requestOptions.body = JSON.stringify(requestData.body);
|
|
219
|
+
if (!requestData.headers['Content-Type']) {
|
|
220
|
+
requestOptions.headers['Content-Type'] = 'application/json';
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Show response section
|
|
226
|
+
const responseSection = document.querySelector('.response-section');
|
|
227
|
+
if (responseSection) {
|
|
228
|
+
responseSection.hidden = false;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const response = await fetch(requestData.url, requestOptions);
|
|
232
|
+
const responseTime = Date.now() - startTime;
|
|
233
|
+
const responseText = await response.text();
|
|
234
|
+
|
|
235
|
+
// Convert response headers to object
|
|
236
|
+
const responseHeaders = {};
|
|
237
|
+
response.headers.forEach((value, key) => {
|
|
238
|
+
// Handle multiple headers with the same name (like Set-Cookie)
|
|
239
|
+
if (responseHeaders[key]) {
|
|
240
|
+
if (Array.isArray(responseHeaders[key])) {
|
|
241
|
+
responseHeaders[key].push(value);
|
|
242
|
+
} else {
|
|
243
|
+
responseHeaders[key] = [responseHeaders[key], value];
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
responseHeaders[key] = value;
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
ModalManager.showResponseModal(response.status, responseText, responseTime, responseHeaders, requestData.headers);
|
|
251
|
+
|
|
252
|
+
} catch (error) {
|
|
253
|
+
console.error('Request failed:', error);
|
|
254
|
+
const requestData = this.buildRequestData();
|
|
255
|
+
const errorTime = Date.now() - startTime;
|
|
256
|
+
ModalManager.showResponseModal('Error', error.message || 'Unknown error occurred', errorTime, null, requestData.headers);
|
|
257
|
+
} finally {
|
|
258
|
+
this.setLoadingState(executeBtn, false);
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
setLoadingState(button, loading) {
|
|
263
|
+
button.disabled = loading;
|
|
264
|
+
|
|
265
|
+
if (loading) {
|
|
266
|
+
button.classList.add('loading');
|
|
267
|
+
const spinner = button.querySelector('.loading-spinner');
|
|
268
|
+
if (spinner) {
|
|
269
|
+
spinner.style.display = 'inline-block';
|
|
270
|
+
}
|
|
271
|
+
} else {
|
|
272
|
+
button.classList.remove('loading');
|
|
273
|
+
const spinner = button.querySelector('.loading-spinner');
|
|
274
|
+
if (spinner) {
|
|
275
|
+
spinner.style.display = 'none';
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
// JSON formatting and validation
|
|
281
|
+
formatJson: function() {
|
|
282
|
+
const editor = document.getElementById('requestBody');
|
|
283
|
+
if (!editor) return;
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
const formatted = JSON.stringify(JSON.parse(editor.value), null, 2);
|
|
287
|
+
editor.value = formatted;
|
|
288
|
+
this.validateJson();
|
|
289
|
+
} catch (e) {
|
|
290
|
+
this.showValidationError('Invalid JSON format', 'json');
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
validateJson: function() {
|
|
295
|
+
const editor = document.getElementById('requestBody');
|
|
296
|
+
const status = document.querySelector('.validation-status');
|
|
297
|
+
|
|
298
|
+
if (!editor || !status) return true;
|
|
299
|
+
|
|
300
|
+
if (!editor.value.trim()) {
|
|
301
|
+
status.textContent = '';
|
|
302
|
+
status.className = 'validation-status';
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
JSON.parse(editor.value);
|
|
308
|
+
status.textContent = '✓ Valid JSON';
|
|
309
|
+
status.className = 'validation-status valid';
|
|
310
|
+
return true;
|
|
311
|
+
} catch (e) {
|
|
312
|
+
status.textContent = '✗ ' + e.message;
|
|
313
|
+
status.className = 'validation-status invalid';
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
|
|
318
|
+
// This method is now handled by the main showValidationError method above
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// Global functions for onclick handlers and backward compatibility
|
|
322
|
+
window.executeRequest = () => RequestExecutor.executeRequest();
|
|
323
|
+
window.formatJson = () => RequestExecutor.formatJson();
|
|
324
|
+
window.validateJson = () => RequestExecutor.validateJson();
|
|
325
|
+
|
|
326
|
+
// Export for global access
|
|
327
|
+
window.RequestExecutor = RequestExecutor;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// Response modal functionality
|
|
2
|
+
const ResponseModalManager = {
|
|
3
|
+
init: function() {
|
|
4
|
+
// Tab switching
|
|
5
|
+
const tabs = document.querySelectorAll('.response-tabs .tab');
|
|
6
|
+
tabs.forEach(tab => {
|
|
7
|
+
tab.addEventListener('click', () => this.switchTab(tab));
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// Initialize syntax highlighting
|
|
11
|
+
this.initializePrism();
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
switchTab: function(tab) {
|
|
15
|
+
if (!tab) return;
|
|
16
|
+
|
|
17
|
+
// Remove active class from all tabs and content
|
|
18
|
+
document.querySelectorAll('.response-tabs .tab').forEach(t => {
|
|
19
|
+
if (t && t.classList) {
|
|
20
|
+
t.classList.remove('active');
|
|
21
|
+
t.setAttribute('aria-selected', 'false');
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
document.querySelectorAll('.tab-content').forEach(c => {
|
|
25
|
+
if (c && c.classList) {
|
|
26
|
+
c.classList.remove('active');
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Add active class to clicked tab and its content
|
|
31
|
+
if (tab.classList) {
|
|
32
|
+
tab.classList.add('active');
|
|
33
|
+
tab.setAttribute('aria-selected', 'true');
|
|
34
|
+
}
|
|
35
|
+
const contentId = tab.getAttribute('aria-controls');
|
|
36
|
+
const contentElement = document.getElementById(contentId);
|
|
37
|
+
if (contentElement && contentElement.classList) {
|
|
38
|
+
contentElement.classList.add('active');
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
copyResponse: function() {
|
|
43
|
+
const responseBody = document.getElementById('modalResponseBody');
|
|
44
|
+
if (!responseBody) return;
|
|
45
|
+
|
|
46
|
+
const responseText = responseBody.textContent;
|
|
47
|
+
if (navigator.clipboard) {
|
|
48
|
+
navigator.clipboard.writeText(responseText).then(() => {
|
|
49
|
+
this.showToast('Response copied to clipboard');
|
|
50
|
+
}).catch(() => {
|
|
51
|
+
this.showToast('Failed to copy response');
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
this.showToast('Clipboard not supported');
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
downloadResponse: function() {
|
|
59
|
+
const responseBody = document.getElementById('modalResponseBody');
|
|
60
|
+
if (!responseBody) return;
|
|
61
|
+
|
|
62
|
+
const responseText = responseBody.textContent;
|
|
63
|
+
const blob = new Blob([responseText], { type: 'application/json' });
|
|
64
|
+
const url = URL.createObjectURL(blob);
|
|
65
|
+
const a = document.createElement('a');
|
|
66
|
+
a.href = url;
|
|
67
|
+
a.download = 'response.json';
|
|
68
|
+
document.body.appendChild(a);
|
|
69
|
+
a.click();
|
|
70
|
+
document.body.removeChild(a);
|
|
71
|
+
URL.revokeObjectURL(url);
|
|
72
|
+
this.showToast('Response downloaded');
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
formatResponse: function() {
|
|
76
|
+
try {
|
|
77
|
+
const responseBody = document.getElementById('modalResponseBody');
|
|
78
|
+
if (!responseBody) return;
|
|
79
|
+
|
|
80
|
+
const content = responseBody.textContent;
|
|
81
|
+
if (!content.trim()) return;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const parsed = JSON.parse(content);
|
|
85
|
+
responseBody.textContent = JSON.stringify(parsed, null, 2);
|
|
86
|
+
} catch (e) {
|
|
87
|
+
// If not JSON, just return as is
|
|
88
|
+
console.log('Response is not valid JSON, skipping formatting');
|
|
89
|
+
}
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error('Error formatting response:', error);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
collapseAll: function() {
|
|
96
|
+
const responseBody = document.getElementById('modalResponseBody');
|
|
97
|
+
if (!responseBody) return;
|
|
98
|
+
|
|
99
|
+
// Simple collapse - replace newlines with spaces for basic collapse
|
|
100
|
+
const content = responseBody.textContent;
|
|
101
|
+
if (content) {
|
|
102
|
+
responseBody.textContent = content.replace(/\n\s*/g, ' ').trim();
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
expandAll: function() {
|
|
107
|
+
const responseBody = document.getElementById('modalResponseBody');
|
|
108
|
+
if (!responseBody) return;
|
|
109
|
+
|
|
110
|
+
const content = responseBody.textContent;
|
|
111
|
+
if (!content.trim()) return;
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
// Try to format as JSON first
|
|
115
|
+
const parsed = JSON.parse(content);
|
|
116
|
+
responseBody.textContent = JSON.stringify(parsed, null, 2);
|
|
117
|
+
} catch (e) {
|
|
118
|
+
// If not JSON, just restore original formatting
|
|
119
|
+
responseBody.textContent = content;
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
showToast: function(message) {
|
|
124
|
+
// Create toast element
|
|
125
|
+
const toast = document.createElement('div');
|
|
126
|
+
toast.className = 'toast';
|
|
127
|
+
toast.textContent = message;
|
|
128
|
+
toast.style.cssText = `
|
|
129
|
+
position: fixed;
|
|
130
|
+
top: 20px;
|
|
131
|
+
right: 20px;
|
|
132
|
+
background: #333;
|
|
133
|
+
color: white;
|
|
134
|
+
padding: 12px 20px;
|
|
135
|
+
border-radius: 4px;
|
|
136
|
+
z-index: 10000;
|
|
137
|
+
font-size: 14px;
|
|
138
|
+
opacity: 0;
|
|
139
|
+
transition: opacity 0.3s ease;
|
|
140
|
+
`;
|
|
141
|
+
|
|
142
|
+
document.body.appendChild(toast);
|
|
143
|
+
|
|
144
|
+
// Show toast
|
|
145
|
+
setTimeout(() => {
|
|
146
|
+
toast.style.opacity = '1';
|
|
147
|
+
}, 10);
|
|
148
|
+
|
|
149
|
+
// Hide toast after 3 seconds
|
|
150
|
+
setTimeout(() => {
|
|
151
|
+
toast.style.opacity = '0';
|
|
152
|
+
setTimeout(() => {
|
|
153
|
+
if (toast.parentNode) {
|
|
154
|
+
toast.parentNode.removeChild(toast);
|
|
155
|
+
}
|
|
156
|
+
}, 300);
|
|
157
|
+
}, 3000);
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
initializePrism: function() {
|
|
161
|
+
// Basic syntax highlighting placeholder
|
|
162
|
+
// This would integrate with Prism.js if available
|
|
163
|
+
console.log('Syntax highlighting initialized');
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// Export for global access
|
|
168
|
+
window.ResponseModalManager = ResponseModalManager;
|
|
169
|
+
|
|
170
|
+
// Initialize when DOM is loaded
|
|
171
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
172
|
+
ResponseModalManager.init();
|
|
173
|
+
});
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// Query parameter suggestions functionality
|
|
2
|
+
const TryOutSuggestions = {
|
|
3
|
+
init: function() {
|
|
4
|
+
this.suggestions = this.getAvailableSuggestions();
|
|
5
|
+
this.updateDatalist();
|
|
6
|
+
|
|
7
|
+
// Re-initialize when window.queryParametersData changes
|
|
8
|
+
if (window.MutationObserver) {
|
|
9
|
+
this.setupDataObserver();
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
setupDataObserver: function() {
|
|
14
|
+
// Check for changes in queryParametersData every second
|
|
15
|
+
setInterval(() => {
|
|
16
|
+
if (window.queryParametersData && window.queryParametersData._lastUpdate !== this._lastDataUpdate) {
|
|
17
|
+
this._lastDataUpdate = window.queryParametersData._lastUpdate;
|
|
18
|
+
this.suggestions = this.getAvailableSuggestions();
|
|
19
|
+
this.updateDatalist();
|
|
20
|
+
}
|
|
21
|
+
}, 1000);
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
setupAutocomplete: function() {
|
|
25
|
+
// Using native datalist, just make sure all inputs are properly initialized
|
|
26
|
+
this.setupExistingInputs();
|
|
27
|
+
|
|
28
|
+
// Setup for the add button to mark new inputs as initialized
|
|
29
|
+
const addBtn = document.querySelector('.add-btn');
|
|
30
|
+
if (addBtn) {
|
|
31
|
+
addBtn.addEventListener('click', () => {
|
|
32
|
+
// Wait for DOM to update
|
|
33
|
+
setTimeout(() => {
|
|
34
|
+
this.setupExistingInputs();
|
|
35
|
+
}, 10);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
updateDatalist: function() {
|
|
41
|
+
// Update the datalist with our suggestions
|
|
42
|
+
const datalist = document.getElementById('paramSuggestions');
|
|
43
|
+
if (datalist && this.suggestions.length > 0) {
|
|
44
|
+
// Clear existing options
|
|
45
|
+
datalist.innerHTML = '';
|
|
46
|
+
|
|
47
|
+
// Add new options from our suggestions - without descriptions
|
|
48
|
+
this.suggestions.forEach(suggestion => {
|
|
49
|
+
const option = document.createElement('option');
|
|
50
|
+
option.value = suggestion.name;
|
|
51
|
+
// No description text as per requirement
|
|
52
|
+
datalist.appendChild(option);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
setupExistingInputs: function() {
|
|
58
|
+
// Find all parameter name inputs
|
|
59
|
+
const paramInputs = document.querySelectorAll('#queryParams .name-input');
|
|
60
|
+
paramInputs.forEach(input => {
|
|
61
|
+
// Skip if already initialized
|
|
62
|
+
if (input.dataset.autocompleteInitialized) return;
|
|
63
|
+
|
|
64
|
+
// Mark as initialized
|
|
65
|
+
input.dataset.autocompleteInitialized = 'true';
|
|
66
|
+
|
|
67
|
+
// We're using the native datalist for autocomplete
|
|
68
|
+
// No need for custom suggestions dropdown
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
getAvailableSuggestions: function() {
|
|
73
|
+
// Get query parameters only from window.queryParametersData
|
|
74
|
+
const suggestions = [];
|
|
75
|
+
|
|
76
|
+
if (window.queryParametersData) {
|
|
77
|
+
const data = window.queryParametersData;
|
|
78
|
+
|
|
79
|
+
// Add filter fields
|
|
80
|
+
if (data.filter_fields && data.filter_fields.length > 0) {
|
|
81
|
+
suggestions.push(...data.filter_fields.map(field => ({
|
|
82
|
+
name: field
|
|
83
|
+
})));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Add search fields - only add the 'search' key, not individual fields
|
|
87
|
+
// The 'search' key will be added via special_keys
|
|
88
|
+
|
|
89
|
+
// Add ordering fields - only add the 'ordering' key, not individual fields
|
|
90
|
+
// The 'ordering' key will be added via special_keys
|
|
91
|
+
|
|
92
|
+
// Add special keys
|
|
93
|
+
if (data.special_keys && data.special_keys.length > 0) {
|
|
94
|
+
suggestions.push(...data.special_keys.map(key => ({
|
|
95
|
+
name: key
|
|
96
|
+
})));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Add pagination fields
|
|
100
|
+
if (data.pagination_fields && data.pagination_fields.length > 0) {
|
|
101
|
+
suggestions.push(...data.pagination_fields.map(field => ({
|
|
102
|
+
name: field
|
|
103
|
+
})));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return suggestions;
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
selectSuggestion: function(input, suggestion) {
|
|
111
|
+
// Set input value
|
|
112
|
+
input.value = suggestion;
|
|
113
|
+
|
|
114
|
+
// Focus on value input
|
|
115
|
+
const valueInput = input.nextElementSibling;
|
|
116
|
+
if (valueInput) {
|
|
117
|
+
valueInput.focus();
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// Export for global access
|
|
123
|
+
window.TryOutSuggestions = TryOutSuggestions;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Tab management functionality
|
|
2
|
+
const TabManager = {
|
|
3
|
+
init: function() {
|
|
4
|
+
document.querySelectorAll('.try-out-form .tab, .smart-tabs .tab, .response-tabs .tab').forEach(tab => {
|
|
5
|
+
tab.addEventListener('click', () => {
|
|
6
|
+
this.switchTab(tab);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
// Add keyboard support
|
|
10
|
+
tab.addEventListener('keydown', (e) => {
|
|
11
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
12
|
+
e.preventDefault();
|
|
13
|
+
this.switchTab(tab);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
switchTab: function(activeTab) {
|
|
20
|
+
const tabContainer = activeTab.closest('.smart-tabs, .response-tabs, .try-out-form');
|
|
21
|
+
if (!tabContainer) return;
|
|
22
|
+
|
|
23
|
+
// Remove active class from all tabs in this container
|
|
24
|
+
tabContainer.querySelectorAll('.tab').forEach(t => {
|
|
25
|
+
t.classList.remove('active');
|
|
26
|
+
t.setAttribute('aria-selected', 'false');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Remove active class from all tab content
|
|
30
|
+
const contentContainer = tabContainer.parentElement || document;
|
|
31
|
+
contentContainer.querySelectorAll('.tab-content').forEach(c => {
|
|
32
|
+
c.classList.remove('active');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Add active class to clicked tab and its content
|
|
36
|
+
activeTab.classList.add('active');
|
|
37
|
+
activeTab.setAttribute('aria-selected', 'true');
|
|
38
|
+
|
|
39
|
+
// Show corresponding content
|
|
40
|
+
const contentId = activeTab.getAttribute('aria-controls') || activeTab.getAttribute('data-tab');
|
|
41
|
+
let content;
|
|
42
|
+
|
|
43
|
+
if (contentId) {
|
|
44
|
+
content = document.getElementById(contentId) || document.getElementById(contentId + 'Tab');
|
|
45
|
+
if (content) {
|
|
46
|
+
content.classList.add('active');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Debug logging
|
|
51
|
+
console.log('Tab switched to:', contentId, 'Content element:', content);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Initialize tabs when DOM is ready
|
|
56
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
57
|
+
TabManager.init();
|
|
58
|
+
|
|
59
|
+
// Also initialize when modal is shown (for dynamic content)
|
|
60
|
+
const modal = document.getElementById('tryOutModal');
|
|
61
|
+
if (modal) {
|
|
62
|
+
const observer = new MutationObserver(function(mutations) {
|
|
63
|
+
mutations.forEach(function(mutation) {
|
|
64
|
+
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
|
|
65
|
+
if (modal.classList.contains('show')) {
|
|
66
|
+
// Re-initialize tabs when modal is shown
|
|
67
|
+
setTimeout(() => TabManager.init(), 100);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
observer.observe(modal, { attributes: true });
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Export for global access
|
|
77
|
+
window.TabManager = TabManager;
|