Architecture¶
This document describes the internal architecture of jarkdown, explaining how components work together to export Jira issues.
Overview¶
jarkdown follows a modular architecture with clear separation of concerns:
User Input → CLI → API Client → Data Processing → File Output
↓
Attachment Handler
↓
Markdown Converter
Core Components¶
CLI Layer (jarkdown.py)¶
The entry point and orchestrator of the application.
Responsibilities:
Parse command-line arguments
Load environment configuration
Coordinate workflow between components
Handle all user-facing errors
Control exit codes
Key Functions:
main()- Entry point, argument parsingexport_issue()- Orchestrates the export workflow
Design Decisions:
All
sys.exit()calls happen here, not in component classesComponents raise exceptions; CLI handles them
Verbose output controlled at this layer
API Client (jira_api_client.py)¶
Manages all communication with the Jira REST API.
Class: JiraApiClient
Responsibilities:
Authenticate with Jira using Basic Auth
Fetch issue data via REST API
Handle API errors and rate limiting
Manage HTTP session for connection pooling
Key Methods:
__init__()- Initialize with credentialsget_issue()- Fetch complete issue data_make_request()- Internal method for API calls
Design Decisions:
Returns raw JSON data without transformation
Uses requests.Session for connection reuse
Raises specific exceptions for different failures
No retry logic (kept simple)
Attachment Handler (attachment_handler.py)¶
Downloads and manages file attachments.
Class: AttachmentHandler
Responsibilities:
Download attachments from Jira
Handle filename conflicts
Use streaming for memory efficiency
Track download progress (when verbose)
Key Methods:
__init__()- Initialize with auth sessiondownload_attachments()- Main download orchestrator_download_file()- Stream download implementation_get_unique_filename()- Handle naming conflicts
Design Decisions:
Streaming downloads for large files
Automatic retry on failure (up to 3 times)
Preserves original filenames when possible
Returns metadata about downloaded files
Markdown Converter (markdown_converter.py)¶
Converts Jira HTML content to clean Markdown.
Class: MarkdownConverter
Responsibilities:
Convert HTML to Markdown using markdownify
Update attachment URLs to local references
Format issue metadata
Structure the final document
Key Methods:
compose_markdown()- Main conversion methodreplace_attachment_links()- Replace Jira URLs with local paths_compose_comments_section()- Convert and format comments with ADF support_generate_metadata_dict()- Generate YAML frontmatter metadata
Design Decisions:
Uses markdownify for robust HTML conversion
Handles multiple Jira URL patterns
Preserves formatting and structure
Creates readable, well-organized output
Exception Hierarchy (exceptions.py)¶
Custom exceptions for clear error handling.
JarkdownError (base)
├── ConfigurationError # Missing/invalid config
├── JiraApiError # API communication issues
│ ├── AuthenticationError # 401 responses
│ └── IssueNotFoundError # 404 responses
└── AttachmentDownloadError # Download failures
Design Decisions:
Specific exceptions for different failures
Inherit from common base class
Include relevant context in messages
Allow graceful error handling
Data Flow¶
1. Initialization¶
# User runs: jarkdown PROJ-123 --output /path
1. CLI validates arguments
2. Load .env file with python-dotenv
3. Verify required environment variables
4. Create output directory if needed
2. API Communication¶
1. Create JiraApiClient with credentials
2. Build API URL: https://domain/rest/api/2/issue/PROJ-123
3. Make authenticated GET request
4. Receive JSON response with issue data
5. Extract attachments list from JSON
3. Attachment Processing¶
1. Create AttachmentHandler with auth session
2. For each attachment in issue:
a. Stream download from Jira
b. Generate unique filename if conflict
c. Save to output directory
d. Track downloaded files
3. Return mapping of URLs to local paths
4. Markdown Generation¶
1. Create MarkdownConverter
2. Extract issue fields (title, description, comments)
3. Convert HTML content to Markdown
4. Replace attachment URLs with local paths
5. Format metadata section
6. Combine into final document
5. File Output¶
1. Create issue directory (e.g., PROJ-123/)
2. Write markdown file (PROJ-123.md)
3. Attachments already saved by handler
4. Report success to user
Configuration Management¶
Environment variables are loaded using python-dotenv:
# Automatic loading from .env file
from dotenv import load_dotenv
load_dotenv()
# Access via os.environ
domain = os.environ.get('JIRA_DOMAIN')
Security: Credentials never logged or included in error messages.
Error Handling Strategy¶
Each layer handles errors appropriately:
Components - Raise specific exceptions
CLI - Catches and presents user-friendly messages
Exit Codes - Consistent codes for different failures
# Example error flow
try:
api_client.get_issue(issue_key)
except AuthenticationError:
print("Error: Invalid credentials")
sys.exit(1)
except IssueNotFoundError:
print(f"Error: Issue {issue_key} not found")
sys.exit(2)
Performance Considerations¶
Memory Efficiency¶
Streaming Downloads: Attachments downloaded in chunks
Lazy Processing: Data processed as needed
Session Reuse: Single HTTP session for all requests
Network Optimization¶
Connection Pooling: Via requests.Session
Minimal API Calls: Single call per issue
Parallel Downloads: Could be added for multiple attachments
Extension Points¶
The architecture supports future enhancements:
Potential Extensions¶
Bulk Export: Process multiple issues
Custom Templates: Different output formats
Plugin System: Custom processors
Caching: Store frequently accessed data
Resume Support: Continue interrupted downloads
Adding New Features¶
To add a new feature:
Create new module if needed
Raise specific exceptions
Integrate with CLI orchestrator
Update error handling
Add tests
Testing Architecture¶
Unit Tests¶
Each component tested in isolation:
# Mock external dependencies
@patch('requests.Session')
def test_api_client(mock_session):
# Test API client without network
Integration Tests¶
CLI tested with mocked components:
# Test full workflow with mocks
@patch('jarkdown.JiraApiClient')
def test_export_workflow(mock_client):
# Test complete export process
Design Principles¶
Single Responsibility: Each class has one job
Dependency Injection: Components receive dependencies
Fail Fast: Validate early, fail with clear messages
No Magic: Explicit over implicit behavior
Testability: Easy to mock and test
Security Considerations¶
No Credential Storage: Only in environment
No Credential Logging: Filtered from all output
HTTPS Only: All API communication encrypted
Token Authentication: No password storage
Future Improvements¶
Potential architectural enhancements:
Async Downloads: Use asyncio for parallel downloads
Progress Bars: Rich terminal UI for long operations
Incremental Updates: Only download changed content
Database Cache: SQLite for metadata caching
Plugin Architecture: Allow custom processors
Conclusion¶
The architecture prioritizes:
Simplicity: Easy to understand and maintain
Modularity: Components can be updated independently
Reliability: Clear error handling and recovery
Extensibility: Easy to add new features
This design makes jarkdown both powerful and maintainable.