drf-to-mkdoc 0.2.3__py3-none-any.whl → 0.3.0__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/conf/settings.py +1 -0
- drf_to_mkdoc/management/commands/build_model_docs.py +10 -1
- 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 +307 -14
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/main.js +39 -11
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/modal.js +298 -18
- 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 +278 -62
- 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 +59 -152
- drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/tabs.js +52 -9
- 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/form.css +539 -0
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/modal.css +239 -17
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/response.css +503 -43
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/tabs.css +71 -19
- drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/variables.css +71 -15
- drf_to_mkdoc/templates/endpoints/detail/request_body.html +2 -0
- drf_to_mkdoc/templates/er_diagrams/app.html +26 -0
- drf_to_mkdoc/templates/er_diagrams/index.html +14 -0
- drf_to_mkdoc/templates/er_diagrams/main.html +22 -0
- drf_to_mkdoc/templates/try-out/fab.html +67 -3
- drf_to_mkdoc/templates/try-out/form.html +221 -74
- drf_to_mkdoc/templates/try-out/modal.html +75 -7
- drf_to_mkdoc/templates/try-out/response-modal.html +138 -9
- drf_to_mkdoc/utils/endpoint_detail_generator.py +1 -0
- drf_to_mkdoc/utils/er_diagram_generator.py +230 -0
- {drf_to_mkdoc-0.2.3.dist-info → drf_to_mkdoc-0.3.0.dist-info}/METADATA +89 -10
- {drf_to_mkdoc-0.2.3.dist-info → drf_to_mkdoc-0.3.0.dist-info}/RECORD +40 -27
- {drf_to_mkdoc-0.2.3.dist-info → drf_to_mkdoc-0.3.0.dist-info}/WHEEL +0 -0
- {drf_to_mkdoc-0.2.3.dist-info → drf_to_mkdoc-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {drf_to_mkdoc-0.2.3.dist-info → drf_to_mkdoc-0.3.0.dist-info}/top_level.txt +0 -0
drf_to_mkdoc/conf/defaults.py
CHANGED
|
@@ -2,6 +2,7 @@ DEFAULTS = {
|
|
|
2
2
|
# Path configurations with defaults
|
|
3
3
|
"DOCS_DIR": "docs", # Directory where docs will be generated
|
|
4
4
|
"CONFIG_DIR": "docs/configs", # Directory for configuration files
|
|
5
|
+
"ER_DIAGRAMS_DIR": "er_diagrams", # Directory for ER diagrams (relative to DOCS_DIR)
|
|
5
6
|
"MODEL_DOCS_FILE": "docs/model-docs.json", # Path to model documentation JSON file
|
|
6
7
|
"DOC_CONFIG_FILE": "docs/configs/doc_config.json", # Path to documentation configuration file
|
|
7
8
|
"CUSTOM_SCHEMA_FILE": "docs/configs/custom_schema.json", # Path to custom schema file
|
drf_to_mkdoc/conf/settings.py
CHANGED
|
@@ -4,6 +4,7 @@ from django.core.management.base import BaseCommand
|
|
|
4
4
|
|
|
5
5
|
from drf_to_mkdoc.conf.settings import drf_to_mkdoc_settings
|
|
6
6
|
from drf_to_mkdoc.utils.commons.file_utils import load_json_data
|
|
7
|
+
from drf_to_mkdoc.utils.er_diagram_generator import generate_er_diagrams
|
|
7
8
|
from drf_to_mkdoc.utils.model_detail_generator import generate_model_docs
|
|
8
9
|
from drf_to_mkdoc.utils.model_list_generator import create_models_index
|
|
9
10
|
|
|
@@ -42,9 +43,17 @@ class Command(BaseCommand):
|
|
|
42
43
|
self.stdout.write("📋 Generating model documentation...")
|
|
43
44
|
|
|
44
45
|
try:
|
|
46
|
+
# Generate model detail pages
|
|
45
47
|
generate_model_docs(models_data)
|
|
48
|
+
self.stdout.write(self.style.SUCCESS("✅ Model detail pages generated"))
|
|
49
|
+
|
|
50
|
+
# Generate ER diagrams
|
|
51
|
+
generate_er_diagrams(models_data, docs_dir)
|
|
52
|
+
self.stdout.write(self.style.SUCCESS("✅ ER diagrams generated"))
|
|
53
|
+
|
|
54
|
+
# Create models index page
|
|
46
55
|
create_models_index(models_data, docs_dir)
|
|
47
|
-
self.stdout.write(self.style.SUCCESS("✅
|
|
56
|
+
self.stdout.write(self.style.SUCCESS("✅ Models index page generated"))
|
|
48
57
|
except Exception as e:
|
|
49
58
|
self.stdout.write(self.style.WARNING(f"⚠️ Failed to generate model docs: {e}"))
|
|
50
59
|
raise
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Loader script for field sections functionality
|
|
2
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
3
|
+
// Load CSS
|
|
4
|
+
function loadCSS(url) {
|
|
5
|
+
const link = document.createElement('link');
|
|
6
|
+
link.rel = 'stylesheet';
|
|
7
|
+
link.href = url;
|
|
8
|
+
document.head.appendChild(link);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Load JavaScript
|
|
12
|
+
function loadScript(url, callback) {
|
|
13
|
+
const script = document.createElement('script');
|
|
14
|
+
script.src = url;
|
|
15
|
+
script.onload = callback || function() {};
|
|
16
|
+
document.body.appendChild(script);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Base URL for static files
|
|
20
|
+
const baseUrl = document.querySelector('meta[name="static-url"]')?.getAttribute('content') || '';
|
|
21
|
+
|
|
22
|
+
// Load CSS file
|
|
23
|
+
loadCSS(baseUrl + '/static/drf-to-mkdoc/stylesheets/field-sections.css');
|
|
24
|
+
|
|
25
|
+
// Load field extractor script
|
|
26
|
+
loadScript(baseUrl + '/static/drf-to-mkdoc/javascripts/try-out/field-extractor.js');
|
|
27
|
+
|
|
28
|
+
console.log('Field sections functionality loaded');
|
|
29
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Query parameters loader script
|
|
2
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
3
|
+
// Load JavaScript
|
|
4
|
+
function loadScript(url, callback) {
|
|
5
|
+
const script = document.createElement('script');
|
|
6
|
+
script.src = url;
|
|
7
|
+
script.onload = callback || function() {};
|
|
8
|
+
document.body.appendChild(script);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Base URL for static files
|
|
12
|
+
const baseUrl = document.querySelector('meta[name="static-url"]')?.getAttribute('content') || '';
|
|
13
|
+
|
|
14
|
+
// Load query parameters extractor script
|
|
15
|
+
loadScript(baseUrl + '/static/drf-to-mkdoc/javascripts/try-out/query-parameters-extractor.js');
|
|
16
|
+
});
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// Field extractor for documentation content
|
|
2
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
3
|
+
// Extract filter fields and pagination fields from documentation
|
|
4
|
+
const FieldExtractor = {
|
|
5
|
+
init: function() {
|
|
6
|
+
// Extract fields from documentation
|
|
7
|
+
const fields = this.extractFieldsFromDocumentation();
|
|
8
|
+
|
|
9
|
+
// Make fields available for suggestions
|
|
10
|
+
if (fields && (fields.filter_fields.length > 0 || fields.pagination_fields.length > 0)) {
|
|
11
|
+
window.queryParametersData = window.queryParametersData || {};
|
|
12
|
+
|
|
13
|
+
// Add filter fields
|
|
14
|
+
if (fields.filter_fields.length > 0) {
|
|
15
|
+
window.queryParametersData.filter_fields = fields.filter_fields;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Add pagination fields
|
|
19
|
+
if (fields.pagination_fields.length > 0) {
|
|
20
|
+
window.queryParametersData.pagination_fields = fields.pagination_fields;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Initialize suggestions if TryOutSuggestions is available
|
|
24
|
+
if (window.TryOutSuggestions) {
|
|
25
|
+
window.TryOutSuggestions.init();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
extractFieldsFromDocumentation: function() {
|
|
31
|
+
const result = {
|
|
32
|
+
filter_fields: [],
|
|
33
|
+
pagination_fields: []
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Look for filter fields section
|
|
37
|
+
const filterFieldsHeading = document.querySelector('h3#filter-fields');
|
|
38
|
+
if (filterFieldsHeading) {
|
|
39
|
+
const filterFieldsList = filterFieldsHeading.nextElementSibling;
|
|
40
|
+
if (filterFieldsList && filterFieldsList.tagName === 'UL') {
|
|
41
|
+
const filterFields = Array.from(filterFieldsList.querySelectorAll('li code'))
|
|
42
|
+
.map(code => code.textContent.trim());
|
|
43
|
+
|
|
44
|
+
if (filterFields.length > 0) {
|
|
45
|
+
result.filter_fields = filterFields;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Look for pagination fields section
|
|
51
|
+
const paginationFieldsHeading = document.querySelector('h3#pagination-fields');
|
|
52
|
+
if (paginationFieldsHeading) {
|
|
53
|
+
const paginationFieldsList = paginationFieldsHeading.nextElementSibling;
|
|
54
|
+
if (paginationFieldsList && paginationFieldsList.tagName === 'UL') {
|
|
55
|
+
const paginationFields = Array.from(paginationFieldsList.querySelectorAll('li code'))
|
|
56
|
+
.map(code => code.textContent.trim());
|
|
57
|
+
|
|
58
|
+
if (paginationFields.length > 0) {
|
|
59
|
+
result.pagination_fields = paginationFields;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return result;
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
formatDocumentation: function() {
|
|
68
|
+
// Format filter fields section
|
|
69
|
+
this.formatSection('filter-fields', 'Filter Fields');
|
|
70
|
+
|
|
71
|
+
// Format pagination fields section
|
|
72
|
+
this.formatSection('pagination-fields', 'Pagination Fields');
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
formatSection: function(sectionId, title) {
|
|
76
|
+
const heading = document.querySelector(`h3#${sectionId}`);
|
|
77
|
+
if (!heading) return;
|
|
78
|
+
|
|
79
|
+
const list = heading.nextElementSibling;
|
|
80
|
+
if (!list || list.tagName !== 'UL') return;
|
|
81
|
+
|
|
82
|
+
// Create a new container div
|
|
83
|
+
const container = document.createElement('div');
|
|
84
|
+
container.className = 'api-parameters-section';
|
|
85
|
+
container.id = `${sectionId}-container`;
|
|
86
|
+
|
|
87
|
+
// Create a new heading
|
|
88
|
+
const newHeading = document.createElement('h3');
|
|
89
|
+
newHeading.id = sectionId;
|
|
90
|
+
newHeading.innerHTML = heading.innerHTML;
|
|
91
|
+
|
|
92
|
+
// Create a new list container
|
|
93
|
+
const listContainer = document.createElement('div');
|
|
94
|
+
listContainer.className = 'parameters-list';
|
|
95
|
+
|
|
96
|
+
// Create a description paragraph
|
|
97
|
+
const description = document.createElement('p');
|
|
98
|
+
description.className = 'parameters-description';
|
|
99
|
+
|
|
100
|
+
if (sectionId === 'filter-fields') {
|
|
101
|
+
description.textContent = 'These fields can be used to filter the API results. Add them as query parameters to your request.';
|
|
102
|
+
} else if (sectionId === 'pagination-fields') {
|
|
103
|
+
description.textContent = 'These fields control pagination of the API results.';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Process the list items to enhance them
|
|
107
|
+
const enhancedList = this.enhanceParametersList(list);
|
|
108
|
+
|
|
109
|
+
// Move the list into the container
|
|
110
|
+
listContainer.appendChild(enhancedList);
|
|
111
|
+
|
|
112
|
+
// Add elements to the container
|
|
113
|
+
container.appendChild(newHeading);
|
|
114
|
+
container.appendChild(description);
|
|
115
|
+
container.appendChild(listContainer);
|
|
116
|
+
|
|
117
|
+
// Replace the old elements with the new container
|
|
118
|
+
heading.parentNode.insertBefore(container, heading);
|
|
119
|
+
list.parentNode.removeChild(list);
|
|
120
|
+
heading.parentNode.removeChild(heading);
|
|
121
|
+
|
|
122
|
+
// Add try-it buttons
|
|
123
|
+
this.addTryItButtons(container, sectionId);
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
enhanceParametersList: function(list) {
|
|
127
|
+
// Clone the list
|
|
128
|
+
const enhancedList = list.cloneNode(true);
|
|
129
|
+
|
|
130
|
+
// Process each list item
|
|
131
|
+
Array.from(enhancedList.querySelectorAll('li')).forEach(li => {
|
|
132
|
+
const code = li.querySelector('code');
|
|
133
|
+
if (code) {
|
|
134
|
+
// Create a button to try this parameter
|
|
135
|
+
const tryButton = document.createElement('button');
|
|
136
|
+
tryButton.className = 'try-param-btn';
|
|
137
|
+
tryButton.textContent = 'Try it';
|
|
138
|
+
tryButton.dataset.param = code.textContent.trim();
|
|
139
|
+
tryButton.addEventListener('click', (e) => {
|
|
140
|
+
e.preventDefault();
|
|
141
|
+
this.addParameterToTryOut(e.target.dataset.param);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
li.appendChild(tryButton);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
return enhancedList;
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
addTryItButtons: function(container, sectionId) {
|
|
152
|
+
// Create a button to try all parameters in this section
|
|
153
|
+
const tryAllButton = document.createElement('button');
|
|
154
|
+
tryAllButton.className = 'try-all-params-btn';
|
|
155
|
+
tryAllButton.textContent = 'Try All Parameters';
|
|
156
|
+
|
|
157
|
+
tryAllButton.addEventListener('click', (e) => {
|
|
158
|
+
e.preventDefault();
|
|
159
|
+
|
|
160
|
+
// Get all parameters in this section
|
|
161
|
+
const params = Array.from(container.querySelectorAll('code'))
|
|
162
|
+
.map(code => code.textContent.trim());
|
|
163
|
+
|
|
164
|
+
// Add all parameters to try-out form
|
|
165
|
+
params.forEach(param => this.addParameterToTryOut(param));
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Add the button to the container
|
|
169
|
+
container.appendChild(tryAllButton);
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
addParameterToTryOut: function(paramName) {
|
|
173
|
+
// Find the try-out modal or form
|
|
174
|
+
const tryOutModal = document.getElementById('tryOutModal');
|
|
175
|
+
if (!tryOutModal) return;
|
|
176
|
+
|
|
177
|
+
// Show the modal if it's not already visible
|
|
178
|
+
if (tryOutModal.style.display !== 'flex') {
|
|
179
|
+
if (window.ModalManager && window.ModalManager.openTryOut) {
|
|
180
|
+
window.ModalManager.openTryOut();
|
|
181
|
+
} else {
|
|
182
|
+
tryOutModal.style.display = 'flex';
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Add the parameter to the query parameters
|
|
187
|
+
if (window.TryOutSidebar && window.TryOutSidebar.addQueryParam) {
|
|
188
|
+
window.TryOutSidebar.addQueryParam(paramName);
|
|
189
|
+
} else if (window.FormManager && window.FormManager.addQueryParam) {
|
|
190
|
+
window.FormManager.addQueryParam(paramName);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Initialize field extractor
|
|
196
|
+
FieldExtractor.init();
|
|
197
|
+
|
|
198
|
+
// Format documentation sections
|
|
199
|
+
FieldExtractor.formatDocumentation();
|
|
200
|
+
});
|
|
@@ -1,33 +1,231 @@
|
|
|
1
1
|
// Form management functionality
|
|
2
2
|
const FormManager = {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
// Initialize form functionality
|
|
4
|
+
init: function() {
|
|
5
|
+
this.setupEventListeners();
|
|
6
|
+
this.setupFormValidation();
|
|
7
|
+
this.initializeRequestBody();
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
initializeRequestBody: function() {
|
|
11
|
+
const requestExample = document.querySelector('.request-example');
|
|
12
|
+
const requestBody = document.getElementById('requestBody');
|
|
13
|
+
let example = null;
|
|
14
|
+
if (requestExample && requestBody) {
|
|
15
|
+
try {
|
|
16
|
+
example = requestExample.getAttribute('data-example');
|
|
17
|
+
if (example) {
|
|
18
|
+
// Remove markdown code block syntax if present
|
|
19
|
+
example = example.replace(/^```json\n/, '').replace(/```$/, '');
|
|
20
|
+
// Remove any leading/trailing whitespace
|
|
21
|
+
example = example.trim();
|
|
22
|
+
|
|
23
|
+
// Try to parse and format the JSON
|
|
24
|
+
const formattedJson = JSON.stringify(JSON.parse(example), null, 2);
|
|
25
|
+
requestBody.value = formattedJson;
|
|
26
|
+
|
|
27
|
+
// Validate the JSON after setting it
|
|
28
|
+
if (window.RequestExecutor) {
|
|
29
|
+
window.RequestExecutor.validateJson();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
} catch (e) {
|
|
33
|
+
console.warn('Failed to parse request example:', e);
|
|
34
|
+
// If parsing fails, try to at least show the raw example
|
|
35
|
+
if (example) {
|
|
36
|
+
requestBody.value = example;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
setupEventListeners: function() {
|
|
43
|
+
// Form reset functionality
|
|
44
|
+
const resetButtons = document.querySelectorAll('[data-action="reset"], .secondary-btn, .secondary-button');
|
|
45
|
+
resetButtons.forEach(btn => {
|
|
46
|
+
if (btn.textContent.toLowerCase().includes('reset')) {
|
|
47
|
+
btn.addEventListener('click', (e) => {
|
|
48
|
+
e.preventDefault();
|
|
49
|
+
this.resetForm();
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Parameter filtering
|
|
55
|
+
const searchInput = document.querySelector('.parameter-search');
|
|
56
|
+
if (searchInput) {
|
|
57
|
+
searchInput.addEventListener('input', this.debounce((e) => {
|
|
58
|
+
this.filterParameters(e.target.value);
|
|
59
|
+
}, 300));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Copy URL functionality
|
|
63
|
+
const copyBtn = document.querySelector('.copy-btn');
|
|
64
|
+
if (copyBtn) {
|
|
65
|
+
copyBtn.addEventListener('click', () => this.copyToClipboard());
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// JSON validation on input
|
|
69
|
+
const editor = document.getElementById('requestBody');
|
|
70
|
+
if (editor) {
|
|
71
|
+
editor.addEventListener('input', this.debounce(() => {
|
|
72
|
+
if (window.RequestExecutor) {
|
|
73
|
+
window.RequestExecutor.validateJson();
|
|
74
|
+
}
|
|
75
|
+
}, 500));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Format and validate buttons
|
|
79
|
+
const formatBtn = document.querySelector('.format-btn');
|
|
80
|
+
const validateBtn = document.querySelector('.validate-btn');
|
|
81
|
+
|
|
82
|
+
if (formatBtn) {
|
|
83
|
+
formatBtn.addEventListener('click', () => {
|
|
84
|
+
if (window.RequestExecutor) {
|
|
85
|
+
window.RequestExecutor.formatJson();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (validateBtn) {
|
|
90
|
+
validateBtn.addEventListener('click', () => {
|
|
91
|
+
if (window.RequestExecutor) {
|
|
92
|
+
window.RequestExecutor.validateJson();
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
setupFormValidation: function() {
|
|
99
|
+
// Add validation to required inputs
|
|
100
|
+
const requiredInputs = document.querySelectorAll('input[required]');
|
|
101
|
+
requiredInputs.forEach(input => {
|
|
102
|
+
input.addEventListener('blur', () => {
|
|
103
|
+
if (window.RequestExecutor) {
|
|
104
|
+
window.RequestExecutor.validateInput(input);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
input.addEventListener('input', () => this.clearValidationError(input));
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
clearValidationError: function(input) {
|
|
112
|
+
input.classList.remove('error');
|
|
113
|
+
const validationMessage = input.parentElement.querySelector('.validation-message');
|
|
114
|
+
if (validationMessage) {
|
|
115
|
+
validationMessage.textContent = '';
|
|
116
|
+
validationMessage.style.display = 'none';
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
// Debounce utility function
|
|
121
|
+
debounce: function(func, wait) {
|
|
122
|
+
let timeout;
|
|
123
|
+
return function executedFunction(...args) {
|
|
124
|
+
const later = () => {
|
|
125
|
+
clearTimeout(timeout);
|
|
126
|
+
func(...args);
|
|
127
|
+
};
|
|
128
|
+
clearTimeout(timeout);
|
|
129
|
+
timeout = setTimeout(later, wait);
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
addQueryParam: function(paramName) {
|
|
133
|
+
const container = document.querySelector('#queryParams .parameter-list');
|
|
5
134
|
if (!container) return;
|
|
6
135
|
|
|
7
|
-
const
|
|
8
|
-
container.appendChild(
|
|
136
|
+
const paramItem = this.createParameterItem();
|
|
137
|
+
container.appendChild(paramItem);
|
|
9
138
|
|
|
10
|
-
//
|
|
11
|
-
const
|
|
12
|
-
if (
|
|
13
|
-
|
|
139
|
+
// If a parameter name was provided, set it
|
|
140
|
+
const nameInput = paramItem.querySelector('.name-input');
|
|
141
|
+
if (nameInput && paramName) {
|
|
142
|
+
nameInput.value = paramName;
|
|
143
|
+
|
|
144
|
+
// Focus on the value input instead
|
|
145
|
+
const valueInput = paramItem.querySelector('.value-input');
|
|
146
|
+
if (valueInput) {
|
|
147
|
+
valueInput.focus();
|
|
148
|
+
}
|
|
149
|
+
} else if (nameInput) {
|
|
150
|
+
// Otherwise focus on the name input
|
|
151
|
+
nameInput.focus();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Setup suggestions for new input if available
|
|
155
|
+
if (window.TryOutSuggestions) {
|
|
156
|
+
setTimeout(() => {
|
|
157
|
+
window.TryOutSuggestions.setupExistingInputs();
|
|
158
|
+
}, 10);
|
|
14
159
|
}
|
|
160
|
+
|
|
161
|
+
return paramItem;
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
createParameterItem: function() {
|
|
165
|
+
const paramItem = document.createElement('div');
|
|
166
|
+
paramItem.className = 'parameter-item';
|
|
167
|
+
|
|
168
|
+
paramItem.innerHTML = `
|
|
169
|
+
<div class="parameter-inputs">
|
|
170
|
+
<input type="text"
|
|
171
|
+
class="modern-input name-input"
|
|
172
|
+
placeholder="Parameter name"
|
|
173
|
+
list="paramSuggestions">
|
|
174
|
+
<input type="text"
|
|
175
|
+
class="modern-input value-input"
|
|
176
|
+
placeholder="Value">
|
|
177
|
+
<button class="remove-btn"
|
|
178
|
+
onclick="FormManager.removeKvItem(this)"
|
|
179
|
+
aria-label="Remove parameter">
|
|
180
|
+
<span class="icon">✕</span>
|
|
181
|
+
</button>
|
|
182
|
+
</div>
|
|
183
|
+
`;
|
|
184
|
+
|
|
185
|
+
return paramItem;
|
|
15
186
|
},
|
|
16
187
|
|
|
17
188
|
addHeader: function() {
|
|
18
|
-
const container = document.
|
|
189
|
+
const container = document.querySelector('#requestHeaders .header-list');
|
|
19
190
|
if (!container) return;
|
|
20
191
|
|
|
21
|
-
const
|
|
22
|
-
container.appendChild(
|
|
192
|
+
const headerItem = this.createHeaderItem();
|
|
193
|
+
container.appendChild(headerItem);
|
|
23
194
|
|
|
24
195
|
// Focus on the first input
|
|
25
|
-
const firstInput =
|
|
196
|
+
const firstInput = headerItem.querySelector('.name-input');
|
|
26
197
|
if (firstInput) {
|
|
27
198
|
firstInput.focus();
|
|
28
199
|
}
|
|
29
200
|
},
|
|
30
201
|
|
|
202
|
+
createHeaderItem: function() {
|
|
203
|
+
const headerItem = document.createElement('div');
|
|
204
|
+
headerItem.className = 'header-item';
|
|
205
|
+
|
|
206
|
+
headerItem.innerHTML = `
|
|
207
|
+
<div class="header-inputs">
|
|
208
|
+
<input type="text"
|
|
209
|
+
class="modern-input name-input"
|
|
210
|
+
placeholder="Header name"
|
|
211
|
+
list="headerSuggestions">
|
|
212
|
+
<input type="text"
|
|
213
|
+
class="modern-input value-input"
|
|
214
|
+
placeholder="Header value">
|
|
215
|
+
<button class="remove-btn"
|
|
216
|
+
aria-label="Remove header">
|
|
217
|
+
<span class="icon">✕</span>
|
|
218
|
+
</button>
|
|
219
|
+
</div>
|
|
220
|
+
`;
|
|
221
|
+
|
|
222
|
+
// Attach the removal handler programmatically
|
|
223
|
+
const removeBtn = headerItem.querySelector('.remove-btn');
|
|
224
|
+
removeBtn.addEventListener('click', (e) => FormManager.removeKvItem(e.currentTarget));
|
|
225
|
+
|
|
226
|
+
return headerItem;
|
|
227
|
+
},
|
|
228
|
+
|
|
31
229
|
createKvItem: function(namePlaceholder, valuePlaceholder, removable = true) {
|
|
32
230
|
const kvItem = document.createElement('div');
|
|
33
231
|
kvItem.className = 'kv-item';
|
|
@@ -55,8 +253,8 @@ const FormManager = {
|
|
|
55
253
|
},
|
|
56
254
|
|
|
57
255
|
removeKvItem: function(button) {
|
|
58
|
-
if (button && button.
|
|
59
|
-
button.
|
|
256
|
+
if (button && button.closest('.parameter-item, .header-item')) {
|
|
257
|
+
button.closest('.parameter-item, .header-item').remove();
|
|
60
258
|
}
|
|
61
259
|
},
|
|
62
260
|
|
|
@@ -165,8 +363,103 @@ const FormManager = {
|
|
|
165
363
|
}
|
|
166
364
|
}
|
|
167
365
|
return null;
|
|
366
|
+
},
|
|
367
|
+
|
|
368
|
+
// Form reset functionality
|
|
369
|
+
resetForm: function() {
|
|
370
|
+
const form = document.querySelector('.try-out-form');
|
|
371
|
+
if (form) {
|
|
372
|
+
// Reset text inputs except base URL
|
|
373
|
+
form.querySelectorAll('input[type="text"], textarea').forEach(input => {
|
|
374
|
+
if (!input.id || input.id !== 'baseUrl') {
|
|
375
|
+
input.value = '';
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// Reset validation states
|
|
380
|
+
form.querySelectorAll('.error').forEach(el => {
|
|
381
|
+
el.classList.remove('error');
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
form.querySelectorAll('.validation-message').forEach(msg => {
|
|
385
|
+
msg.textContent = '';
|
|
386
|
+
msg.style.display = 'none';
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// Reset JSON editor
|
|
390
|
+
const editor = document.getElementById('requestBody');
|
|
391
|
+
if (editor) {
|
|
392
|
+
editor.value = '';
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Reset validation status
|
|
396
|
+
const status = document.querySelector('.validation-status');
|
|
397
|
+
if (status) {
|
|
398
|
+
status.textContent = '';
|
|
399
|
+
status.className = 'validation-status';
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Reset to first tab
|
|
403
|
+
const firstTab = document.querySelector('.tab');
|
|
404
|
+
if (firstTab && window.TabManager) {
|
|
405
|
+
window.TabManager.switchTab(firstTab);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Clear any error messages
|
|
409
|
+
if (window.RequestExecutor) {
|
|
410
|
+
window.RequestExecutor.clearValidationErrors();
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
|
|
415
|
+
// Parameter filtering
|
|
416
|
+
filterParameters: function(query) {
|
|
417
|
+
const items = document.querySelectorAll('.parameter-item');
|
|
418
|
+
query = query.toLowerCase();
|
|
419
|
+
|
|
420
|
+
items.forEach(item => {
|
|
421
|
+
const nameInput = item.querySelector('.name-input');
|
|
422
|
+
const name = nameInput?.value.toLowerCase() || '';
|
|
423
|
+
|
|
424
|
+
if (name.includes(query) || query === '') {
|
|
425
|
+
item.style.display = '';
|
|
426
|
+
} else {
|
|
427
|
+
item.style.display = 'none';
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
},
|
|
431
|
+
|
|
432
|
+
// Copy URL to clipboard
|
|
433
|
+
copyToClipboard: function() {
|
|
434
|
+
const baseUrl = document.getElementById('baseUrl')?.value || '';
|
|
435
|
+
const pathDisplay = document.querySelector('.path-display')?.textContent || '';
|
|
436
|
+
const url = baseUrl + pathDisplay;
|
|
437
|
+
|
|
438
|
+
navigator.clipboard.writeText(url).then(() => {
|
|
439
|
+
// Show copy success in the URL preview
|
|
440
|
+
const copyBtn = document.querySelector('.copy-btn');
|
|
441
|
+
if (copyBtn) {
|
|
442
|
+
const originalText = copyBtn.innerHTML;
|
|
443
|
+
copyBtn.innerHTML = '<span class="icon">✓</span>';
|
|
444
|
+
setTimeout(() => {
|
|
445
|
+
copyBtn.innerHTML = originalText;
|
|
446
|
+
}, 2000);
|
|
447
|
+
}
|
|
448
|
+
}).catch(() => {
|
|
449
|
+
console.error('Failed to copy URL');
|
|
450
|
+
});
|
|
168
451
|
}
|
|
169
452
|
};
|
|
170
453
|
|
|
454
|
+
// Initialize when DOM is loaded
|
|
455
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
456
|
+
FormManager.init();
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
// Global functions for backward compatibility
|
|
460
|
+
window.resetForm = () => FormManager.resetForm();
|
|
461
|
+
window.filterParameters = (query) => FormManager.filterParameters(query);
|
|
462
|
+
window.copyToClipboard = () => FormManager.copyToClipboard();
|
|
463
|
+
|
|
171
464
|
// Export for global access
|
|
172
465
|
window.FormManager = FormManager;
|