Skip to content

Commit 4927798

Browse files
authored
1 parent 5322f55 commit 4927798

File tree

1 file changed

+361
-0
lines changed

1 file changed

+361
-0
lines changed

swagger-subset.html

+361
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
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>Swagger Subset</title>
7+
<style>
8+
* {
9+
box-sizing: border-box;
10+
}
11+
body {
12+
font-family: Helvetica, Arial, sans-serif;
13+
margin: 0;
14+
padding: 20px;
15+
line-height: 1.6;
16+
}
17+
h1, h2 {
18+
margin-top: 0;
19+
}
20+
.container {
21+
max-width: 1200px;
22+
margin: 0 auto;
23+
}
24+
textarea {
25+
width: 100%;
26+
min-height: 200px;
27+
padding: 10px;
28+
margin-bottom: 15px;
29+
border: 1px solid #ccc;
30+
border-radius: 4px;
31+
font-size: 16px;
32+
font-family: monospace;
33+
}
34+
button {
35+
background-color: #4CAF50;
36+
color: white;
37+
border: none;
38+
padding: 10px 15px;
39+
text-align: center;
40+
text-decoration: none;
41+
display: inline-block;
42+
font-size: 16px;
43+
margin: 4px 2px;
44+
cursor: pointer;
45+
border-radius: 4px;
46+
}
47+
button:hover {
48+
background-color: #45a049;
49+
}
50+
.error {
51+
color: red;
52+
margin-bottom: 15px;
53+
}
54+
#pathList {
55+
margin: 20px 0;
56+
max-height: 400px;
57+
overflow-y: auto;
58+
border: 1px solid #ddd;
59+
padding: 10px;
60+
border-radius: 4px;
61+
}
62+
.path-item {
63+
margin-bottom: 8px;
64+
padding-bottom: 8px;
65+
border-bottom: 1px solid #eee;
66+
}
67+
.method {
68+
margin-left: 20px;
69+
display: block;
70+
padding: 4px 0;
71+
}
72+
.method-get {
73+
color: #61affe;
74+
}
75+
.method-post {
76+
color: #49cc90;
77+
}
78+
.method-put {
79+
color: #fca130;
80+
}
81+
.method-delete {
82+
color: #f93e3e;
83+
}
84+
.controls {
85+
margin: 10px 0;
86+
}
87+
.path-tag {
88+
display: inline-block;
89+
background-color: #eee;
90+
padding: 2px 6px;
91+
border-radius: 4px;
92+
margin-left: 8px;
93+
font-size: 12px;
94+
}
95+
#outputContainer {
96+
display: none;
97+
margin-top: 20px;
98+
}
99+
.copy-btn {
100+
background-color: #2196F3;
101+
}
102+
.copy-btn:hover {
103+
background-color: #0b7dda;
104+
}
105+
</style>
106+
</head>
107+
<body>
108+
<div class="container">
109+
<h1>Swagger Subset</h1>
110+
111+
<div>
112+
<h2>Paste your Swagger JSON</h2>
113+
<textarea id="swaggerInput" placeholder="Paste your Swagger JSON here..."></textarea>
114+
<button id="parseBtn">Parse Swagger</button>
115+
<div id="error" class="error"></div>
116+
</div>
117+
118+
<div id="pathsContainer" style="display: none;">
119+
<h2>Select paths to include</h2>
120+
121+
<div class="controls">
122+
<button id="selectAllBtn">Select All</button>
123+
<button id="deselectAllBtn">Deselect All</button>
124+
</div>
125+
126+
<div id="pathList"></div>
127+
128+
<button id="generateBtn">Generate JSON</button>
129+
</div>
130+
131+
<div id="outputContainer">
132+
<h2>Output JSON</h2>
133+
<div class="controls">
134+
<button id="copyBtn" class="copy-btn">Copy to Clipboard</button>
135+
</div>
136+
<textarea id="outputJson" readonly></textarea>
137+
</div>
138+
</div>
139+
140+
<script type="module">
141+
// Parse Swagger JSON and display paths as checkboxes
142+
document.getElementById('parseBtn').addEventListener('click', parseSwagger);
143+
document.getElementById('selectAllBtn').addEventListener('click', selectAll);
144+
document.getElementById('deselectAllBtn').addEventListener('click', deselectAll);
145+
document.getElementById('generateBtn').addEventListener('click', generateJson);
146+
document.getElementById('copyBtn').addEventListener('click', copyToClipboard);
147+
148+
let swaggerData = null;
149+
150+
function parseSwagger() {
151+
const input = document.getElementById('swaggerInput').value;
152+
const errorElem = document.getElementById('error');
153+
errorElem.textContent = '';
154+
155+
try {
156+
swaggerData = JSON.parse(input);
157+
if (!swaggerData.paths) {
158+
throw new Error("No 'paths' property found in the Swagger JSON");
159+
}
160+
161+
displayPaths(swaggerData.paths);
162+
document.getElementById('pathsContainer').style.display = 'block';
163+
document.getElementById('outputContainer').style.display = 'none';
164+
} catch (err) {
165+
errorElem.textContent = `Error parsing JSON: ${err.message}`;
166+
}
167+
}
168+
169+
function displayPaths(paths) {
170+
const pathListElem = document.getElementById('pathList');
171+
pathListElem.innerHTML = '';
172+
173+
// Group paths by tag
174+
const pathsByTag = {};
175+
176+
Object.keys(paths).sort().forEach(path => {
177+
const pathItem = document.createElement('div');
178+
pathItem.className = 'path-item';
179+
180+
const pathLabel = document.createElement('label');
181+
pathLabel.innerHTML = `<strong>${path}</strong>`;
182+
pathItem.appendChild(pathLabel);
183+
184+
Object.keys(paths[path]).forEach(method => {
185+
const endpoint = paths[path][method];
186+
const methodId = `${path}-${method}`;
187+
188+
const methodElem = document.createElement('label');
189+
methodElem.className = `method method-${method}`;
190+
191+
// Create checkbox
192+
const checkbox = document.createElement('input');
193+
checkbox.type = 'checkbox';
194+
checkbox.id = methodId;
195+
checkbox.dataset.path = path;
196+
checkbox.dataset.method = method;
197+
198+
// Create method label
199+
methodElem.appendChild(checkbox);
200+
methodElem.appendChild(document.createTextNode(` ${method.toUpperCase()}`));
201+
202+
// Add tags if available
203+
if (endpoint.tags && endpoint.tags.length > 0) {
204+
const tagSpan = document.createElement('span');
205+
tagSpan.className = 'path-tag';
206+
tagSpan.textContent = endpoint.tags[0];
207+
methodElem.appendChild(tagSpan);
208+
209+
// Group by first tag
210+
const tag = endpoint.tags[0];
211+
if (!pathsByTag[tag]) {
212+
pathsByTag[tag] = [];
213+
}
214+
pathsByTag[tag].push(methodElem);
215+
}
216+
217+
pathItem.appendChild(methodElem);
218+
});
219+
220+
pathListElem.appendChild(pathItem);
221+
});
222+
}
223+
224+
function selectAll() {
225+
const checkboxes = document.querySelectorAll('#pathList input[type="checkbox"]');
226+
checkboxes.forEach(checkbox => {
227+
checkbox.checked = true;
228+
});
229+
}
230+
231+
function deselectAll() {
232+
const checkboxes = document.querySelectorAll('#pathList input[type="checkbox"]');
233+
checkboxes.forEach(checkbox => {
234+
checkbox.checked = false;
235+
});
236+
}
237+
238+
function getSelectedEndpoints() {
239+
const selected = [];
240+
const checkboxes = document.querySelectorAll('#pathList input[type="checkbox"]:checked');
241+
242+
checkboxes.forEach(checkbox => {
243+
selected.push({
244+
path: checkbox.dataset.path,
245+
method: checkbox.dataset.method
246+
});
247+
});
248+
249+
return selected;
250+
}
251+
252+
function findReferencedDefinitions(obj, referencedDefs = new Set()) {
253+
if (!obj) return referencedDefs;
254+
255+
if (typeof obj === 'object') {
256+
if (Array.isArray(obj)) {
257+
obj.forEach(item => findReferencedDefinitions(item, referencedDefs));
258+
} else {
259+
// Check for $ref property
260+
if (obj['$ref'] && typeof obj['$ref'] === 'string') {
261+
const refParts = obj['$ref'].split('/');
262+
if (refParts[1] === 'definitions' && refParts.length > 2) {
263+
referencedDefs.add(refParts[2]);
264+
}
265+
}
266+
267+
// Recursively check all properties
268+
Object.values(obj).forEach(value => {
269+
findReferencedDefinitions(value, referencedDefs);
270+
});
271+
}
272+
}
273+
274+
return referencedDefs;
275+
}
276+
277+
function addDependentDefinitions(definitions, defName, allDefs, processedDefs = new Set()) {
278+
if (processedDefs.has(defName)) return;
279+
processedDefs.add(defName);
280+
281+
const def = allDefs[defName];
282+
if (!def) return;
283+
284+
// Find references in this definition
285+
const referencedDefs = findReferencedDefinitions(def);
286+
287+
// Add this definition to our result
288+
definitions[defName] = def;
289+
290+
// Recursively process any referenced definitions
291+
referencedDefs.forEach(refDef => {
292+
addDependentDefinitions(definitions, refDef, allDefs, processedDefs);
293+
});
294+
}
295+
296+
function generateJson() {
297+
const selectedEndpoints = getSelectedEndpoints();
298+
299+
if (selectedEndpoints.length === 0) {
300+
document.getElementById('error').textContent = 'Please select at least one endpoint';
301+
return;
302+
}
303+
304+
// Create a new paths object with only selected endpoints
305+
const filteredPaths = {};
306+
const referencedDefs = new Set();
307+
308+
selectedEndpoints.forEach(endpoint => {
309+
const { path, method } = endpoint;
310+
311+
if (!filteredPaths[path]) {
312+
filteredPaths[path] = {};
313+
}
314+
315+
filteredPaths[path][method] = swaggerData.paths[path][method];
316+
317+
// Find all referenced definitions in this endpoint
318+
findReferencedDefinitions(swaggerData.paths[path][method]).forEach(def => {
319+
referencedDefs.add(def);
320+
});
321+
});
322+
323+
// Create a filtered definitions object
324+
const filteredDefinitions = {};
325+
if (swaggerData.definitions) {
326+
referencedDefs.forEach(defName => {
327+
addDependentDefinitions(filteredDefinitions, defName, swaggerData.definitions);
328+
});
329+
}
330+
331+
// Create the output JSON with only paths and definitions
332+
const output = {
333+
paths: filteredPaths
334+
};
335+
336+
// Only include definitions if there are any
337+
if (Object.keys(filteredDefinitions).length > 0) {
338+
output.definitions = filteredDefinitions;
339+
}
340+
341+
// Display the output
342+
document.getElementById('outputJson').value = JSON.stringify(output, null, 2);
343+
document.getElementById('outputContainer').style.display = 'block';
344+
}
345+
346+
function copyToClipboard() {
347+
const outputElem = document.getElementById('outputJson');
348+
outputElem.select();
349+
document.execCommand('copy');
350+
351+
const copyBtn = document.getElementById('copyBtn');
352+
const originalText = copyBtn.textContent;
353+
354+
copyBtn.textContent = 'Copied!';
355+
setTimeout(() => {
356+
copyBtn.textContent = originalText;
357+
}, 2000);
358+
}
359+
</script>
360+
</body>
361+
</html>

0 commit comments

Comments
 (0)