Roster is a modern HTTP client application for GNOME, similar to Postman, built with GTK 4 and libadwaita. Features: - Send HTTP requests (GET, POST, PUT, DELETE) - Configure custom headers with add/remove functionality - Request body editor for POST/PUT/DELETE requests - View response headers and bodies in separate tabs - Track request history with JSON persistence - Load previous requests from history with confirmation dialog - Beautiful GNOME-native UI with libadwaita components - HTTPie backend for reliable HTTP communication Technical implementation: - Python 3 with GTK 4 and libadwaita 1 - Meson build system with Flatpak support - Custom widgets (HeaderRow, HistoryItem) with GObject signals - Background threading for non-blocking HTTP requests - AdwTabView for modern tabbed interface - History persistence to ~/.config/roster/history.json - Comprehensive error handling and user feedback
87 lines
2.6 KiB
Python
87 lines
2.6 KiB
Python
# models.py
|
|
#
|
|
# Copyright 2025 Pavel Baksy
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
#
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
from dataclasses import dataclass, asdict
|
|
from typing import Dict, Optional
|
|
|
|
|
|
@dataclass
|
|
class HttpRequest:
|
|
"""Represents an HTTP request."""
|
|
method: str # "GET", "POST", "PUT", "DELETE"
|
|
url: str
|
|
headers: Dict[str, str] # Key-value pairs
|
|
body: str # Raw text body
|
|
|
|
def to_dict(self):
|
|
"""Convert to dictionary for JSON serialization."""
|
|
return asdict(self)
|
|
|
|
@classmethod
|
|
def from_dict(cls, data):
|
|
"""Create instance from dictionary."""
|
|
return cls(**data)
|
|
|
|
|
|
@dataclass
|
|
class HttpResponse:
|
|
"""Represents an HTTP response."""
|
|
status_code: int
|
|
status_text: str # e.g., "OK"
|
|
headers: str # Raw header text from HTTPie
|
|
body: str # Raw body text
|
|
response_time_ms: float
|
|
|
|
def to_dict(self):
|
|
"""Convert to dictionary for JSON serialization."""
|
|
return asdict(self)
|
|
|
|
@classmethod
|
|
def from_dict(cls, data):
|
|
"""Create instance from dictionary."""
|
|
return cls(**data)
|
|
|
|
|
|
@dataclass
|
|
class HistoryEntry:
|
|
"""Represents a request/response pair in history."""
|
|
timestamp: str # ISO format datetime
|
|
request: HttpRequest
|
|
response: Optional[HttpResponse]
|
|
error: Optional[str] # Error message if request failed
|
|
|
|
def to_dict(self):
|
|
"""Convert to dictionary for JSON serialization."""
|
|
return {
|
|
'timestamp': self.timestamp,
|
|
'request': self.request.to_dict(),
|
|
'response': self.response.to_dict() if self.response else None,
|
|
'error': self.error
|
|
}
|
|
|
|
@classmethod
|
|
def from_dict(cls, data):
|
|
"""Create instance from dictionary."""
|
|
return cls(
|
|
timestamp=data['timestamp'],
|
|
request=HttpRequest.from_dict(data['request']),
|
|
response=HttpResponse.from_dict(data['response']) if data.get('response') else None,
|
|
error=data.get('error')
|
|
)
|