overload-cli 0.1.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.
Files changed (40) hide show
  1. overload/__init__.py +3 -0
  2. overload/__main__.py +5 -0
  3. overload/cli.py +393 -0
  4. overload/collection/__init__.py +1 -0
  5. overload/collection/environment.py +23 -0
  6. overload/collection/models.py +88 -0
  7. overload/collection/parser.py +220 -0
  8. overload/collection/variables.py +84 -0
  9. overload/config_file.py +73 -0
  10. overload/engine/__init__.py +1 -0
  11. overload/engine/assertions.py +151 -0
  12. overload/engine/auth.py +87 -0
  13. overload/engine/events.py +50 -0
  14. overload/engine/http_client.py +274 -0
  15. overload/engine/load_patterns.py +730 -0
  16. overload/engine/models.py +254 -0
  17. overload/engine/rate_limiter.py +124 -0
  18. overload/engine/runner.py +86 -0
  19. overload/report/__init__.py +1 -0
  20. overload/report/exporters.py +77 -0
  21. overload/report/generator.py +71 -0
  22. overload/report/templates/report.html +369 -0
  23. overload/utils/__init__.py +1 -0
  24. overload/utils/naming.py +26 -0
  25. overload/web/__init__.py +1 -0
  26. overload/web/app.py +38 -0
  27. overload/web/routes/__init__.py +1 -0
  28. overload/web/routes/api.py +461 -0
  29. overload/web/routes/ws.py +77 -0
  30. overload/web/static/css/app.css +242 -0
  31. overload/web/static/js/app.js +241 -0
  32. overload/web/static/js/charts.js +385 -0
  33. overload/web/static/js/collection.js +344 -0
  34. overload/web/static/js/runner.js +625 -0
  35. overload/web/templates/index.html +23 -0
  36. overload_cli-0.1.0.dist-info/METADATA +267 -0
  37. overload_cli-0.1.0.dist-info/RECORD +40 -0
  38. overload_cli-0.1.0.dist-info/WHEEL +4 -0
  39. overload_cli-0.1.0.dist-info/entry_points.txt +2 -0
  40. overload_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,344 @@
1
+ window.CollectionPage = (function() {
2
+ var collection = null;
3
+ var selectedRequest = null;
4
+
5
+ function render(container) {
6
+ container.innerHTML =
7
+ '<h1 class="page-title">Collection</h1>' +
8
+ '<p class="page-desc">Upload a Postman Collection to get started</p>' +
9
+ '<div class="working-dir-banner" id="workingDirBanner" style="display:none"></div>' +
10
+ '<div id="detectedFiles"></div>' +
11
+ '<div class="card" id="uploadCard">' +
12
+ '<div class="drop-zone" id="dropZone">' +
13
+ '<div class="drop-zone-text">Drop your Postman Collection here</div>' +
14
+ '<div class="drop-zone-sub">.json file (v2.0 or v2.1)</div>' +
15
+ '<input type="file" id="collectionFile" accept=".json">' +
16
+ '</div>' +
17
+ '</div>' +
18
+ '<div class="card" style="display:none" id="envCard">' +
19
+ '<div class="card-title">Environment (optional)</div>' +
20
+ '<div class="drop-zone" id="envDropZone" style="padding:24px">' +
21
+ '<div class="drop-zone-sub">Drop Postman Environment file</div>' +
22
+ '<input type="file" id="envFile" accept=".json">' +
23
+ '</div>' +
24
+ '</div>' +
25
+ '<div id="collectionView" style="display:none"></div>';
26
+
27
+ detectLocalFiles();
28
+ setupDropZone();
29
+ }
30
+
31
+ function detectLocalFiles() {
32
+ fetch('/api/detect')
33
+ .then(function(r) { return r.json(); })
34
+ .then(function(data) {
35
+ if (data.status !== 'ok') return;
36
+
37
+ var banner = document.getElementById('workingDirBanner');
38
+ if (banner && data.working_dir) {
39
+ banner.style.display = 'flex';
40
+ banner.innerHTML = '&#128193; Working directory: <span class="dir-path">' + esc(data.working_dir) + '</span>';
41
+ }
42
+
43
+ var html = '';
44
+ var collections = data.collections || [];
45
+ var environments = data.environments || [];
46
+
47
+ if (collections.length) {
48
+ html += '<div class="detected-section"><div class="card">';
49
+ html += '<div class="card-title">Found in ' + esc(data.working_dir) + '</div>';
50
+ collections.forEach(function(c) {
51
+ html += '<div class="detected-item" data-path="' + esc(c.path) + '" data-type="collection">';
52
+ html += '<div class="detected-item-info">';
53
+ html += '<div class="detected-item-icon">&#128230;</div>';
54
+ html += '<div><div class="detected-item-name">' + esc(c.name) + '</div>';
55
+ html += '<div class="detected-item-meta">' + esc(c.filename) + ' &mdash; ' + c.request_count + ' requests</div></div>';
56
+ html += '</div>';
57
+ html += '<div class="detected-item-btn">Load</div>';
58
+ html += '</div>';
59
+ });
60
+ environments.forEach(function(e) {
61
+ html += '<div class="detected-item" data-path="' + esc(e.path) + '" data-type="environment">';
62
+ html += '<div class="detected-item-info">';
63
+ html += '<div class="detected-item-icon">&#9881;</div>';
64
+ html += '<div><div class="detected-item-name">' + esc(e.name) + '</div>';
65
+ html += '<div class="detected-item-meta">' + esc(e.filename) + ' &mdash; ' + e.variable_count + ' variables (environment)</div></div>';
66
+ html += '</div>';
67
+ html += '<div class="detected-item-btn">Load</div>';
68
+ html += '</div>';
69
+ });
70
+ html += '</div></div>';
71
+ }
72
+
73
+ var container = document.getElementById('detectedFiles');
74
+ if (container) {
75
+ container.innerHTML = html;
76
+ container.querySelectorAll('.detected-item').forEach(function(el) {
77
+ el.addEventListener('click', function() {
78
+ var path = el.dataset.path;
79
+ var type = el.dataset.type;
80
+ if (type === 'collection') loadLocalCollection(path);
81
+ else loadLocalEnvironment(path);
82
+ });
83
+ });
84
+ }
85
+ })
86
+ .catch(function() {});
87
+ }
88
+
89
+ function loadLocalCollection(path) {
90
+ fetch('/api/collection/load-local', {
91
+ method: 'POST',
92
+ headers: { 'Content-Type': 'application/json' },
93
+ body: JSON.stringify({ path: path })
94
+ })
95
+ .then(function(r) { return r.json(); })
96
+ .then(function(data) {
97
+ if (data.status === 'ok') {
98
+ collection = data.collection;
99
+ window.OverloadApp.setCollection(collection);
100
+ renderCollection();
101
+ App.toast('Collection loaded: ' + collection.name, 'success');
102
+ } else {
103
+ App.toast('Error: ' + data.message, 'error');
104
+ }
105
+ })
106
+ .catch(function(err) { App.toast('Failed to load: ' + err.message, 'error'); });
107
+ }
108
+
109
+ function loadLocalEnvironment(path) {
110
+ fetch('/api/environment/load-local', {
111
+ method: 'POST',
112
+ headers: { 'Content-Type': 'application/json' },
113
+ body: JSON.stringify({ path: path })
114
+ })
115
+ .then(function(r) { return r.json(); })
116
+ .then(function(data) {
117
+ if (data.status === 'ok') {
118
+ App.toast('Environment loaded: ' + Object.keys(data.variables).length + ' variables', 'success');
119
+ if (collection) renderCollection();
120
+ } else {
121
+ App.toast('Error: ' + data.message, 'error');
122
+ }
123
+ })
124
+ .catch(function(err) { App.toast('Failed to load: ' + err.message, 'error'); });
125
+ }
126
+
127
+ function setupDropZone() {
128
+ var zone = document.getElementById('dropZone');
129
+ var input = document.getElementById('collectionFile');
130
+ var envZone = document.getElementById('envDropZone');
131
+ var envInput = document.getElementById('envFile');
132
+
133
+ zone.addEventListener('click', function() { input.click(); });
134
+ zone.addEventListener('dragover', function(e) { e.preventDefault(); zone.classList.add('dragover'); });
135
+ zone.addEventListener('dragleave', function() { zone.classList.remove('dragover'); });
136
+ zone.addEventListener('drop', function(e) {
137
+ e.preventDefault();
138
+ zone.classList.remove('dragover');
139
+ if (e.dataTransfer.files.length) uploadCollection(e.dataTransfer.files[0]);
140
+ });
141
+ input.addEventListener('change', function() { if (input.files.length) uploadCollection(input.files[0]); });
142
+
143
+ envZone.addEventListener('click', function() { envInput.click(); });
144
+ envZone.addEventListener('dragover', function(e) { e.preventDefault(); envZone.classList.add('dragover'); });
145
+ envZone.addEventListener('dragleave', function() { envZone.classList.remove('dragover'); });
146
+ envZone.addEventListener('drop', function(e) {
147
+ e.preventDefault();
148
+ envZone.classList.remove('dragover');
149
+ if (e.dataTransfer.files.length) uploadEnvironment(e.dataTransfer.files[0]);
150
+ });
151
+ envInput.addEventListener('change', function() { if (envInput.files.length) uploadEnvironment(envInput.files[0]); });
152
+ }
153
+
154
+ function uploadCollection(file) {
155
+ var formData = new FormData();
156
+ formData.append('file', file);
157
+
158
+ fetch('/api/collection/upload', { method: 'POST', body: formData })
159
+ .then(function(r) { return r.json(); })
160
+ .then(function(data) {
161
+ if (data.status === 'ok') {
162
+ collection = data.collection;
163
+ window.OverloadApp.setCollection(collection);
164
+ renderCollection();
165
+ App.toast('Collection loaded: ' + collection.name, 'success');
166
+ } else {
167
+ App.toast('Error: ' + data.message, 'error');
168
+ }
169
+ })
170
+ .catch(function(err) { App.toast('Upload failed: ' + err.message, 'error'); });
171
+ }
172
+
173
+ function uploadEnvironment(file) {
174
+ var formData = new FormData();
175
+ formData.append('file', file);
176
+
177
+ fetch('/api/environment/upload', { method: 'POST', body: formData })
178
+ .then(function(r) { return r.json(); })
179
+ .then(function(data) {
180
+ if (data.status === 'ok') {
181
+ App.toast('Environment loaded: ' + Object.keys(data.variables).length + ' variables', 'success');
182
+ if (collection) renderCollection();
183
+ } else {
184
+ App.toast('Error: ' + data.message, 'error');
185
+ }
186
+ })
187
+ .catch(function(err) { App.toast('Upload failed: ' + err.message, 'error'); });
188
+ }
189
+
190
+ function renderCollection() {
191
+ document.getElementById('uploadCard').style.display = 'none';
192
+ document.getElementById('envCard').style.display = 'block';
193
+ var detected = document.getElementById('detectedFiles');
194
+ if (detected) detected.style.display = 'none';
195
+
196
+ var view = document.getElementById('collectionView');
197
+ view.style.display = 'block';
198
+
199
+ var html =
200
+ '<div class="card">' +
201
+ '<div class="card-title">' + esc(collection.name) + ' (' + collection.requests.length + ' requests)</div>' +
202
+ '<div class="tree" id="collectionTree">' + renderTree(collection.requests) + '</div>' +
203
+ '</div>';
204
+
205
+ if (collection.variables && collection.variables.length) {
206
+ html +=
207
+ '<div class="card">' +
208
+ '<div class="card-title">Variables</div>' +
209
+ '<table class="var-table">' +
210
+ '<thead><tr><th>Name</th><th>Value</th></tr></thead>' +
211
+ '<tbody>' +
212
+ collection.variables.map(function(v, i) {
213
+ return '<tr><td>' + esc(v.key) + '</td><td><input type="text" value="' + esc(v.value) + '" data-var-key="' + esc(v.key) + '" class="var-input"></td></tr>';
214
+ }).join('') +
215
+ '</tbody>' +
216
+ '</table>' +
217
+ '</div>';
218
+ }
219
+
220
+ html += '<div style="display:flex;gap:8px;margin-top:12px;justify-content:space-between;align-items:center">';
221
+ html += '<button class="btn btn-secondary" id="resetBtn">Change Collection</button>';
222
+ html += '<button class="btn btn-primary" id="continueBtn">Continue to Test Runner &rarr;</button>';
223
+ html += '</div>';
224
+
225
+ html += '<div id="requestDetail"></div>';
226
+
227
+ view.innerHTML = html;
228
+
229
+ view.querySelectorAll('.tree-request').forEach(function(el) {
230
+ el.addEventListener('click', function() {
231
+ var idx = parseInt(el.dataset.idx);
232
+ showRequestDetail(idx);
233
+ });
234
+ });
235
+
236
+ view.querySelectorAll('.var-input').forEach(function(input) {
237
+ input.addEventListener('change', function() {
238
+ var key = input.dataset.varKey;
239
+ var vars = {};
240
+ vars[key] = input.value;
241
+ fetch('/api/variables/update', {
242
+ method: 'POST',
243
+ headers: { 'Content-Type': 'application/json' },
244
+ body: JSON.stringify({ variables: vars })
245
+ });
246
+ });
247
+ });
248
+
249
+ document.getElementById('continueBtn').addEventListener('click', function() {
250
+ window.OverloadApp.navigate('runner');
251
+ });
252
+
253
+ document.getElementById('resetBtn').addEventListener('click', function() {
254
+ collection = null;
255
+ window.OverloadApp.setCollection(null);
256
+ render(document.getElementById('content'));
257
+ });
258
+ }
259
+
260
+ function renderTree(requests) {
261
+ var folders = {};
262
+ var rootRequests = [];
263
+
264
+ requests.forEach(function(req, idx) {
265
+ if (req.folder_path && req.folder_path.length) {
266
+ var key = req.folder_path.join('/');
267
+ if (!folders[key]) folders[key] = { path: req.folder_path, requests: [] };
268
+ folders[key].requests.push({ req: req, idx: idx });
269
+ } else {
270
+ rootRequests.push({ req: req, idx: idx });
271
+ }
272
+ });
273
+
274
+ var html = '';
275
+
276
+ Object.keys(folders).forEach(function(key) {
277
+ var folder = folders[key];
278
+ var name = folder.path[folder.path.length - 1];
279
+ html += '<div class="tree-folder">';
280
+ html += '<div class="tree-folder-name"><span class="tree-folder-icon">&#9660;</span> ' + esc(name) + '</div>';
281
+ html += '<div class="tree-children">';
282
+ folder.requests.forEach(function(item) {
283
+ html += renderRequestItem(item.req, item.idx);
284
+ });
285
+ html += '</div></div>';
286
+ });
287
+
288
+ rootRequests.forEach(function(item) {
289
+ html += renderRequestItem(item.req, item.idx);
290
+ });
291
+
292
+ return html;
293
+ }
294
+
295
+ function renderRequestItem(req, idx) {
296
+ return '<div class="tree-request" data-idx="' + idx + '">' +
297
+ '<span class="method-badge method-' + req.method + '">' + req.method + '</span>' +
298
+ '<span>' + esc(req.name) + '</span>' +
299
+ '</div>';
300
+ }
301
+
302
+ function showRequestDetail(idx) {
303
+ var req = collection.requests[idx];
304
+ selectedRequest = idx;
305
+
306
+ document.querySelectorAll('.tree-request').forEach(function(el) {
307
+ el.classList.toggle('selected', parseInt(el.dataset.idx) === idx);
308
+ });
309
+
310
+ var html = '<div class="request-detail">';
311
+ html += '<div class="request-detail-header">';
312
+ html += '<span class="method-badge method-' + req.method + '">' + req.method + '</span>';
313
+ html += '<strong>' + esc(req.name) + '</strong>';
314
+ html += '</div>';
315
+ html += '<pre>' + esc(req.url_raw) + '</pre>';
316
+
317
+ if (req.headers && Object.keys(req.headers).length) {
318
+ html += '<div class="card-title" style="margin-top:12px">Headers</div>';
319
+ html += '<pre>' + Object.keys(req.headers).map(function(k) { return esc(k) + ': ' + esc(req.headers[k]); }).join('\n') + '</pre>';
320
+ }
321
+
322
+ if (req.body && req.body.mode !== 'none') {
323
+ html += '<div class="card-title" style="margin-top:12px">Body (' + req.body.mode + ')</div>';
324
+ var bodyContent = typeof req.body.content === 'string' ? req.body.content : JSON.stringify(req.body.content, null, 2);
325
+ html += '<pre>' + esc(bodyContent) + '</pre>';
326
+ }
327
+
328
+ if (req.auth) {
329
+ html += '<div class="card-title" style="margin-top:12px">Auth (' + req.auth.type + ')</div>';
330
+ html += '<pre>' + esc(JSON.stringify(req.auth.params, null, 2)) + '</pre>';
331
+ }
332
+
333
+ html += '</div>';
334
+ document.getElementById('requestDetail').innerHTML = html;
335
+ }
336
+
337
+ function esc(s) {
338
+ return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
339
+ }
340
+
341
+ function getCollection() { return collection; }
342
+
343
+ return { render: render, getCollection: getCollection };
344
+ })();