230 lines
8.5 KiB
Markdown
230 lines
8.5 KiB
Markdown
# JIRA AI Fixer - Developer Guide
|
|
|
|
## Overview
|
|
|
|
JIRA AI Fixer is a universal AI-powered issue analysis engine that integrates with multiple issue tracking systems to automatically analyze support cases and suggest code fixes.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ JIRA AI Fixer │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
|
│ │TicketHub │ │ JIRA │ │ServiceNow│ │ Zendesk │ ... │
|
|
│ │ Webhook │ │ Webhook │ │ Webhook │ │ Webhook │ │
|
|
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
|
|
│ │ │ │ │ │
|
|
│ └─────────────┴──────┬──────┴─────────────┘ │
|
|
│ │ │
|
|
│ ┌───────▼───────┐ │
|
|
│ │ Normalizer │ (Adapter Pattern) │
|
|
│ └───────┬───────┘ │
|
|
│ │ │
|
|
│ ┌───────▼───────┐ │
|
|
│ │ Analyzer │ (LLM + Code Analysis) │
|
|
│ └───────┬───────┘ │
|
|
│ │ │
|
|
│ ┌─────────────┼─────────────┐ │
|
|
│ │ │ │ │
|
|
│ ┌──────▼─────┐ ┌─────▼─────┐ ┌────▼────┐ │
|
|
│ │ Database │ │ PR Gen │ │Callback │ │
|
|
│ │ PostgreSQL │ │ Gitea │ │ to Src │ │
|
|
│ └────────────┘ └───────────┘ └─────────┘ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Tech Stack
|
|
|
|
- **Language**: Python 3.11
|
|
- **Framework**: FastAPI (async)
|
|
- **Database**: PostgreSQL 15
|
|
- **LLM**: OpenRouter API (Llama 3.3 70B free tier)
|
|
- **Code Hosting**: Gitea (self-hosted)
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
jira-ai-fixer/
|
|
├── api/
|
|
│ └── main_v3.py # Main application (monolith)
|
|
├── docs/
|
|
│ ├── DEVELOPER_GUIDE.md
|
|
│ ├── USER_GUIDE.md
|
|
│ └── ARCHITECTURE.md
|
|
└── README.md
|
|
```
|
|
|
|
## Key Components
|
|
|
|
### 1. Webhook Adapters
|
|
|
|
Each supported system has a dedicated adapter that normalizes payloads:
|
|
|
|
```python
|
|
def normalize_jira(payload: dict) -> Optional[NormalizedIssue]:
|
|
"""Normalize JIRA webhook payload"""
|
|
issue = payload.get("issue", {})
|
|
fields = issue.get("fields", {})
|
|
return NormalizedIssue(
|
|
external_id=str(issue.get("id")),
|
|
external_key=issue.get("key"),
|
|
source="jira",
|
|
title=fields.get("summary"),
|
|
description=fields.get("description"),
|
|
callback_url=f"{base_url}/rest/api/2/issue/{issue.get('key')}/comment"
|
|
)
|
|
```
|
|
|
|
### 2. Analysis Pipeline
|
|
|
|
```python
|
|
async def analyze_issue(issue_id: int, issue: NormalizedIssue):
|
|
# 1. Fetch source code from repositories
|
|
cobol_files = await fetch_cobol_files()
|
|
|
|
# 2. Build LLM prompt
|
|
prompt = build_analysis_prompt(issue, cobol_files)
|
|
|
|
# 3. Call LLM API
|
|
analysis = await call_llm(prompt)
|
|
|
|
# 4. Parse response
|
|
result = parse_analysis(analysis)
|
|
|
|
# 5. Create fix branch and PR
|
|
pr_info = await create_fix_branch_and_pr(issue, result)
|
|
|
|
# 6. Post back to source system
|
|
await post_analysis_to_source(issue, result, pr_info)
|
|
```
|
|
|
|
### 3. Callback System
|
|
|
|
Results are posted back to the source system in their native format:
|
|
|
|
| System | Method | Format |
|
|
|--------|--------|--------|
|
|
| TicketHub | POST /tickets/{id}/comments | `{"author": "...", "content": "..."}` |
|
|
| JIRA | POST /rest/api/2/issue/{key}/comment | `{"body": "..."}` |
|
|
| ServiceNow | PATCH /api/now/table/incident/{sys_id} | `{"work_notes": "..."}` |
|
|
| Zendesk | PUT /api/v2/tickets/{id}.json | `{"ticket": {"comment": {...}}}` |
|
|
| Azure DevOps | POST /workitems/{id}/comments | `{"text": "..."}` |
|
|
| GitHub | POST /repos/.../issues/{n}/comments | `{"body": "..."}` |
|
|
|
|
## Adding a New Integration
|
|
|
|
1. Create normalizer function:
|
|
|
|
```python
|
|
def normalize_newsystem(payload: dict) -> Optional[NormalizedIssue]:
|
|
# Extract fields from payload
|
|
return NormalizedIssue(
|
|
external_id=...,
|
|
external_key=...,
|
|
source="newsystem",
|
|
title=...,
|
|
description=...,
|
|
callback_url=...
|
|
)
|
|
```
|
|
|
|
2. Add webhook endpoint:
|
|
|
|
```python
|
|
@app.post("/api/webhook/newsystem")
|
|
async def webhook_newsystem(payload: dict, background_tasks: BackgroundTasks):
|
|
issue = normalize_newsystem(payload)
|
|
if not issue:
|
|
return WebhookResponse(status="ignored", message="Event not handled")
|
|
issue_id = await save_and_queue_issue(issue, background_tasks)
|
|
return WebhookResponse(status="accepted", issue_id=issue_id)
|
|
```
|
|
|
|
3. Add callback format in `post_analysis_to_source()`:
|
|
|
|
```python
|
|
elif issue.source == "newsystem":
|
|
await client.post(issue.callback_url, json={...})
|
|
```
|
|
|
|
## Environment Variables
|
|
|
|
| Variable | Description | Default |
|
|
|----------|-------------|---------|
|
|
| `DATABASE_URL` | PostgreSQL connection string | `postgresql://jira:jira_secret_2026@postgres:5432/jira_fixer` |
|
|
| `OPENROUTER_API_KEY` | OpenRouter API key for LLM | (empty = mock mode) |
|
|
| `GITEA_URL` | Gitea instance URL | `https://gitea.startdata.com.br` |
|
|
| `GITEA_TOKEN` | Gitea API token | (empty) |
|
|
| `COBOL_REPO` | Default repository to analyze | `startdata/cobol-sample-app` |
|
|
|
|
## API Endpoints
|
|
|
|
### Webhooks
|
|
|
|
| Endpoint | Description |
|
|
|----------|-------------|
|
|
| `POST /api/webhook/tickethub` | TicketHub webhooks |
|
|
| `POST /api/webhook/jira` | JIRA webhooks |
|
|
| `POST /api/webhook/servicenow` | ServiceNow webhooks |
|
|
| `POST /api/webhook/zendesk` | Zendesk webhooks |
|
|
| `POST /api/webhook/azure-devops` | Azure DevOps webhooks |
|
|
| `POST /api/webhook/github` | GitHub Issues webhooks |
|
|
| `POST /api/webhook/gitlab` | GitLab Issues webhooks |
|
|
| `POST /api/webhook/generic` | Generic webhook format |
|
|
|
|
### Management
|
|
|
|
| Endpoint | Description |
|
|
|----------|-------------|
|
|
| `GET /api/health` | Health check |
|
|
| `GET /api/issues` | List issues |
|
|
| `GET /api/issues/{id}` | Get issue details |
|
|
| `GET /api/integrations` | List integrations |
|
|
| `GET /api/stats` | Dashboard statistics |
|
|
|
|
## Running Locally
|
|
|
|
```bash
|
|
# Install dependencies
|
|
pip install fastapi uvicorn httpx asyncpg pydantic
|
|
|
|
# Run with PostgreSQL
|
|
export DATABASE_URL="postgresql://user:pass@localhost:5432/jira_fixer"
|
|
uvicorn main:app --reload --port 8000
|
|
```
|
|
|
|
## Testing Webhooks
|
|
|
|
```bash
|
|
# Test TicketHub webhook
|
|
curl -X POST http://localhost:8000/api/webhook/tickethub \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"event": "ticket.created",
|
|
"timestamp": "2026-02-18T18:00:00Z",
|
|
"data": {
|
|
"id": 1,
|
|
"key": "SUPP-1",
|
|
"title": "Test issue",
|
|
"description": "Test description"
|
|
}
|
|
}'
|
|
|
|
# Test generic webhook
|
|
curl -X POST http://localhost:8000/api/webhook/generic \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"id": "123",
|
|
"key": "CUSTOM-1",
|
|
"title": "Custom issue",
|
|
"description": "From custom system",
|
|
"source": "my-system",
|
|
"callback_url": "https://my-system.com/api/issues/123/comments"
|
|
}'
|
|
```
|
|
|
|
## License
|
|
|
|
MIT License - StartData 2026
|