Initial release — APIClient - Agent v2.0.0
AI-first API testing desktop client built with Python + PyQt6. Features: - Multi-tab HTTP request editor with params/headers/body/auth/tests - KeyValueTable with per-row enable/disable checkboxes and 36px rows - Format JSON button, syntax highlighting, pre-request & test scripts - Collections, environments, history, import/export (Postman v2.1, cURL) - OpenAPI 3.x / Swagger 2.0 local parser (no AI tokens) - EKIKA Odoo API Framework generator — JSON-API, REST JSON, GraphQL, Custom REST JSON with all auth types (instant, no AI tokens) - Persistent AI chat sidebar (Claude-powered co-pilot) with streaming, context-aware suggestions, and one-click Apply to request editor - AI collection generator from any docs URL or pasted spec - WebSocket client, Mock server, Collection runner, Code generator - Dark/light theme engine (global QSS, object-name selectors) - SSL error detection with actionable hints - MIT License Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
138
app/ui/import_dialog.py
Normal file
138
app/ui/import_dialog.py
Normal file
@@ -0,0 +1,138 @@
|
||||
"""APIClient - Agent — Import Dialog."""
|
||||
from PyQt6.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QHBoxLayout, QTabWidget, QWidget,
|
||||
QTextEdit, QPushButton, QLabel, QFileDialog, QMessageBox
|
||||
)
|
||||
from PyQt6.QtGui import QFont
|
||||
|
||||
from app.ui.theme import Colors
|
||||
from app.core import importer, storage
|
||||
from app.models import HttpRequest
|
||||
|
||||
|
||||
class ImportDialog(QDialog):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle("Import")
|
||||
self.setMinimumSize(640, 460)
|
||||
self.imported_req: HttpRequest | None = None
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(0)
|
||||
|
||||
# Header
|
||||
header = QWidget()
|
||||
header.setObjectName("panelHeader")
|
||||
header.setFixedHeight(48)
|
||||
hl = QHBoxLayout(header)
|
||||
hl.setContentsMargins(16, 0, 16, 0)
|
||||
title = QLabel("Import")
|
||||
title.setObjectName("panelTitle")
|
||||
hl.addWidget(title)
|
||||
layout.addWidget(header)
|
||||
|
||||
self.tabs = QTabWidget()
|
||||
self.tabs.addTab(self._make_postman_tab(), "Postman Collection")
|
||||
self.tabs.addTab(self._make_curl_tab(), "cURL Command")
|
||||
layout.addWidget(self.tabs, 1)
|
||||
|
||||
# ── Tab builders ──────────────────────────────────────────────────────────
|
||||
|
||||
def _make_postman_tab(self):
|
||||
w = QWidget()
|
||||
layout = QVBoxLayout(w)
|
||||
layout.setContentsMargins(16, 12, 16, 12)
|
||||
layout.setSpacing(8)
|
||||
|
||||
hint = QLabel("Paste a Postman Collection v2.1 JSON or load from file:")
|
||||
hint.setObjectName("hintText")
|
||||
layout.addWidget(hint)
|
||||
|
||||
self.postman_editor = QTextEdit()
|
||||
self.postman_editor.setFont(
|
||||
QFont("JetBrains Mono, Fira Code, Consolas, monospace", 10)
|
||||
)
|
||||
self.postman_editor.setPlaceholderText(
|
||||
'{\n "info": {"name": "My Collection"},\n "item": [...]\n}'
|
||||
)
|
||||
layout.addWidget(self.postman_editor)
|
||||
|
||||
btn_row = QHBoxLayout()
|
||||
load_btn = QPushButton("Load File…")
|
||||
load_btn.clicked.connect(self._load_postman_file)
|
||||
import_btn = QPushButton("Import Collection")
|
||||
import_btn.setObjectName("accent")
|
||||
import_btn.clicked.connect(self._import_postman)
|
||||
btn_row.addWidget(load_btn)
|
||||
btn_row.addStretch()
|
||||
btn_row.addWidget(import_btn)
|
||||
layout.addLayout(btn_row)
|
||||
return w
|
||||
|
||||
def _make_curl_tab(self):
|
||||
w = QWidget()
|
||||
layout = QVBoxLayout(w)
|
||||
layout.setContentsMargins(16, 12, 16, 12)
|
||||
layout.setSpacing(8)
|
||||
|
||||
hint = QLabel("Paste a cURL command — it will open as a new request tab:")
|
||||
hint.setObjectName("hintText")
|
||||
layout.addWidget(hint)
|
||||
|
||||
self.curl_editor = QTextEdit()
|
||||
self.curl_editor.setFont(
|
||||
QFont("JetBrains Mono, Fira Code, Consolas, monospace", 10)
|
||||
)
|
||||
self.curl_editor.setPlaceholderText(
|
||||
"curl -X POST https://api.example.com/data \\\n"
|
||||
" -H 'Content-Type: application/json' \\\n"
|
||||
" -d '{\"key\": \"value\"}'"
|
||||
)
|
||||
layout.addWidget(self.curl_editor)
|
||||
|
||||
btn_row = QHBoxLayout()
|
||||
import_btn = QPushButton("Import as Request")
|
||||
import_btn.setObjectName("accent")
|
||||
import_btn.clicked.connect(self._import_curl)
|
||||
btn_row.addStretch()
|
||||
btn_row.addWidget(import_btn)
|
||||
layout.addLayout(btn_row)
|
||||
return w
|
||||
|
||||
# ── Import actions ────────────────────────────────────────────────────────
|
||||
|
||||
def _load_postman_file(self):
|
||||
path, _ = QFileDialog.getOpenFileName(
|
||||
self, "Open Collection", "", "JSON Files (*.json);;All Files (*)"
|
||||
)
|
||||
if path:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
self.postman_editor.setPlainText(f.read())
|
||||
|
||||
def _import_postman(self):
|
||||
text = self.postman_editor.toPlainText().strip()
|
||||
if not text:
|
||||
return
|
||||
try:
|
||||
col_name, requests = importer.from_postman_collection(text)
|
||||
col_id = storage.add_collection(col_name)
|
||||
for req in requests:
|
||||
storage.save_request(col_id, req)
|
||||
QMessageBox.information(
|
||||
self, "Import Successful",
|
||||
f"Imported collection '{col_name}' with {len(requests)} request(s)."
|
||||
)
|
||||
self.accept()
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Import Error", str(e))
|
||||
|
||||
def _import_curl(self):
|
||||
text = self.curl_editor.toPlainText().strip()
|
||||
if not text:
|
||||
return
|
||||
try:
|
||||
self.imported_req = importer.from_curl(text)
|
||||
self.accept()
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Import Error", str(e))
|
||||
Reference in New Issue
Block a user