Add API_DOCUMENTATION.md
Signed-off-by: Thomas Scott <tombomb122@noreply.localhost>
This commit is contained in:
parent
bcd9ede458
commit
86db9c9341
|
|
@ -0,0 +1,933 @@
|
||||||
|
# API Documentation
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document describes the APIs utilized by the Redmine-Gitea Sync Server, including both external APIs (Redmine and Gitea) and the server's own endpoints.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
- [Server Endpoints](#server-endpoints)
|
||||||
|
- [Redmine API Integration](#redmine-api-integration)
|
||||||
|
- [Gitea API Integration](#gitea-api-integration)
|
||||||
|
- [Webhook Payloads](#webhook-payloads)
|
||||||
|
- [Data Mapping](#data-mapping)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Server Endpoints
|
||||||
|
|
||||||
|
### Health Check
|
||||||
|
|
||||||
|
**Endpoint:** `GET /health`
|
||||||
|
|
||||||
|
**Description:** Returns server health status and configuration information.
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"uptime": 12345.67,
|
||||||
|
"timestamp": "2025-12-17T01:49:47Z",
|
||||||
|
"cache": {
|
||||||
|
"size": 5,
|
||||||
|
"ttl": 30000
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"redmine": {
|
||||||
|
"url": "https://redmine.example.com",
|
||||||
|
"connected": true
|
||||||
|
},
|
||||||
|
"gitea": {
|
||||||
|
"url": "https://gitea.example.com",
|
||||||
|
"owner": "username",
|
||||||
|
"connected": true
|
||||||
|
},
|
||||||
|
"sync": {
|
||||||
|
"enableLabels": true,
|
||||||
|
"enableMilestones": true,
|
||||||
|
"enableAttachments": false,
|
||||||
|
"enableCustomFields": true,
|
||||||
|
"retryAttempts": 3,
|
||||||
|
"retryDelay": 1000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Status Check
|
||||||
|
|
||||||
|
**Endpoint:** `GET /status`
|
||||||
|
|
||||||
|
**Description:** Tests connectivity to both Redmine and Gitea platforms.
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"server": "running",
|
||||||
|
"uptime": 12345.67,
|
||||||
|
"cache": {
|
||||||
|
"size": 5,
|
||||||
|
"ttl": 30000
|
||||||
|
},
|
||||||
|
"connections": {
|
||||||
|
"redmine": "connected",
|
||||||
|
"gitea": "connected"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Possible Connection States:**
|
||||||
|
- `connected`: Successfully connected to platform
|
||||||
|
- `disconnected`: Failed to connect to platform
|
||||||
|
- `unknown`: Connection not yet tested
|
||||||
|
|
||||||
|
### Clear Cache
|
||||||
|
|
||||||
|
**Endpoint:** `POST /cache/clear`
|
||||||
|
|
||||||
|
**Description:** Manually clears the sync cache. Useful for troubleshooting sync issues.
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "Cache cleared",
|
||||||
|
"timestamp": "2025-12-17T01:49:47Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Redmine Webhook
|
||||||
|
|
||||||
|
**Endpoint:** `POST /webhook/redmine`
|
||||||
|
|
||||||
|
**Description:** Receives webhook notifications from Redmine for issue updates.
|
||||||
|
|
||||||
|
**Expected Payload:** See [Redmine Webhook Payload](#redmine-webhook-payload)
|
||||||
|
|
||||||
|
**Response:** `200 OK` (always returns immediately)
|
||||||
|
|
||||||
|
**Processing:**
|
||||||
|
1. Validates payload structure
|
||||||
|
2. Checks sync cache to prevent loops
|
||||||
|
3. Retrieves or creates corresponding Gitea issue
|
||||||
|
4. Syncs metadata (status, assignee, labels, milestone)
|
||||||
|
5. Syncs journals as comments
|
||||||
|
6. Updates cache
|
||||||
|
|
||||||
|
### Gitea Webhook
|
||||||
|
|
||||||
|
**Endpoint:** `POST /webhook/gitea`
|
||||||
|
|
||||||
|
**Description:** Receives webhook notifications from Gitea for issue updates.
|
||||||
|
|
||||||
|
**Expected Payload:** See [Gitea Webhook Payload](#gitea-webhook-payload)
|
||||||
|
|
||||||
|
**Response:** `200 OK` (always returns immediately)
|
||||||
|
|
||||||
|
**Processing:**
|
||||||
|
1. Validates payload structure
|
||||||
|
2. Checks sync cache to prevent loops
|
||||||
|
3. Retrieves or creates corresponding Redmine issue
|
||||||
|
4. Syncs metadata (status, assignee, due date)
|
||||||
|
5. Syncs comments as journals
|
||||||
|
6. Updates cache
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Redmine API Integration
|
||||||
|
|
||||||
|
### Base Configuration
|
||||||
|
|
||||||
|
**Base URL:** Configured via `REDMINE_API_URL`
|
||||||
|
**Authentication:** API Key via `X-Redmine-API-Key` header
|
||||||
|
**Format:** JSON
|
||||||
|
|
||||||
|
### Endpoints Used
|
||||||
|
|
||||||
|
#### Get Issue
|
||||||
|
|
||||||
|
**Method:** `GET /issues/{id}.json`
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `include`: `attachments,journals,watchers`
|
||||||
|
|
||||||
|
**Response Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"issue": {
|
||||||
|
"id": 31,
|
||||||
|
"project": {
|
||||||
|
"id": 2,
|
||||||
|
"identifier": "ai-smuggling",
|
||||||
|
"name": "AI Smuggling"
|
||||||
|
},
|
||||||
|
"tracker": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Bug"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"id": 2,
|
||||||
|
"name": "In Progress"
|
||||||
|
},
|
||||||
|
"priority": {
|
||||||
|
"id": 2,
|
||||||
|
"name": "Normal"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"id": 1,
|
||||||
|
"login": "admin"
|
||||||
|
},
|
||||||
|
"assignee": {
|
||||||
|
"id": 1,
|
||||||
|
"login": "admin"
|
||||||
|
},
|
||||||
|
"subject": "Issue title",
|
||||||
|
"description": "Issue description",
|
||||||
|
"start_date": "2025-12-16",
|
||||||
|
"due_date": "2025-12-19",
|
||||||
|
"estimated_hours": 5.0,
|
||||||
|
"custom_fields": [],
|
||||||
|
"journals": [],
|
||||||
|
"attachments": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Create Issue
|
||||||
|
|
||||||
|
**Method:** `POST /issues.json`
|
||||||
|
|
||||||
|
**Payload:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"issue": {
|
||||||
|
"project_id": "ai-smuggling",
|
||||||
|
"tracker_id": 1,
|
||||||
|
"status_id": 2,
|
||||||
|
"priority_id": 2,
|
||||||
|
"subject": "Issue title",
|
||||||
|
"description": "Issue description",
|
||||||
|
"assigned_to_id": 1,
|
||||||
|
"start_date": "2025-12-16",
|
||||||
|
"due_date": "2025-12-19",
|
||||||
|
"estimated_hours": 5.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"issue": {
|
||||||
|
"id": 32,
|
||||||
|
"project": {...},
|
||||||
|
"subject": "Issue title",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Update Issue
|
||||||
|
|
||||||
|
**Method:** `PUT /issues/{id}.json`
|
||||||
|
|
||||||
|
**Payload:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"issue": {
|
||||||
|
"subject": "Updated title",
|
||||||
|
"description": "Updated description",
|
||||||
|
"status_id": 5,
|
||||||
|
"assigned_to_id": 2,
|
||||||
|
"due_date": "2025-12-20",
|
||||||
|
"notes": "Adding a comment"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Search Issues
|
||||||
|
|
||||||
|
**Method:** `GET /issues.json`
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `project_id`: Project identifier
|
||||||
|
- `subject`: Subject search (supports `~` for partial match)
|
||||||
|
- `limit`: Maximum results (default: 100)
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"issues": [
|
||||||
|
{
|
||||||
|
"id": 31,
|
||||||
|
"subject": "Issue title",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total_count": 1,
|
||||||
|
"offset": 0,
|
||||||
|
"limit": 100
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get Project
|
||||||
|
|
||||||
|
**Method:** `GET /projects/{id}.json`
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"project": {
|
||||||
|
"id": 2,
|
||||||
|
"identifier": "ai-smuggling",
|
||||||
|
"name": "AI Smuggling",
|
||||||
|
"description": "Project description",
|
||||||
|
"created_on": "2025-12-15T08:37:45.000Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get Trackers
|
||||||
|
|
||||||
|
**Method:** `GET /trackers.json`
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"trackers": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Bug"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Feature"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get Issue Statuses
|
||||||
|
|
||||||
|
**Method:** `GET /issue_statuses.json`
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"issue_statuses": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "New",
|
||||||
|
"is_closed": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": "Closed",
|
||||||
|
"is_closed": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get Priorities
|
||||||
|
|
||||||
|
**Method:** `GET /enumerations/issue_priorities.json`
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"issue_priorities": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Low"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Normal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get Versions (Milestones)
|
||||||
|
|
||||||
|
**Method:** `GET /projects/{id}/versions.json`
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"versions": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Version 1.0",
|
||||||
|
"description": "First release",
|
||||||
|
"status": "open",
|
||||||
|
"due_date": "2025-12-31"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Gitea API Integration
|
||||||
|
|
||||||
|
### Base Configuration
|
||||||
|
|
||||||
|
**Base URL:** Configured via `GITEA_API_URL`
|
||||||
|
**Authentication:** Token via `Authorization: token {TOKEN}` header
|
||||||
|
**Format:** JSON
|
||||||
|
|
||||||
|
### Endpoints Used
|
||||||
|
|
||||||
|
#### Get Issue
|
||||||
|
|
||||||
|
**Method:** `GET /api/v1/repos/{owner}/{repo}/issues/{index}`
|
||||||
|
|
||||||
|
**Response Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 27,
|
||||||
|
"number": 27,
|
||||||
|
"title": "Issue title",
|
||||||
|
"body": "Issue description",
|
||||||
|
"state": "open",
|
||||||
|
"user": {
|
||||||
|
"id": 1,
|
||||||
|
"login": "tombomb122",
|
||||||
|
"full_name": "Thomas Scott"
|
||||||
|
},
|
||||||
|
"assignee": {
|
||||||
|
"id": 1,
|
||||||
|
"login": "tombomb122"
|
||||||
|
},
|
||||||
|
"labels": [],
|
||||||
|
"milestone": null,
|
||||||
|
"created_at": "2025-12-17T01:49:47Z",
|
||||||
|
"updated_at": "2025-12-17T01:49:47Z",
|
||||||
|
"due_date": null,
|
||||||
|
"comments": 0
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Search Issues
|
||||||
|
|
||||||
|
**Method:** `GET /api/v1/repos/{owner}/{repo}/issues`
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `state`: `open`, `closed`, or `all`
|
||||||
|
- `labels`: Comma-separated label names
|
||||||
|
- `q`: Search query
|
||||||
|
- `limit`: Maximum results
|
||||||
|
- `page`: Page number
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 27,
|
||||||
|
"number": 27,
|
||||||
|
"title": "Issue title",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Create Issue
|
||||||
|
|
||||||
|
**Method:** `POST /api/v1/repos/{owner}/{repo}/issues`
|
||||||
|
|
||||||
|
**Payload:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "Issue title",
|
||||||
|
"body": "Issue description",
|
||||||
|
"assignee": "username",
|
||||||
|
"milestone": 1,
|
||||||
|
"labels": [1, 2, 3],
|
||||||
|
"due_date": "2025-12-19T00:00:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 28,
|
||||||
|
"number": 28,
|
||||||
|
"title": "Issue title",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Update Issue
|
||||||
|
|
||||||
|
**Method:** `PATCH /api/v1/repos/{owner}/{repo}/issues/{index}`
|
||||||
|
|
||||||
|
**Payload:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "Updated title",
|
||||||
|
"body": "Updated description",
|
||||||
|
"state": "closed",
|
||||||
|
"assignee": "username",
|
||||||
|
"milestone": 2,
|
||||||
|
"due_date": "2025-12-20T00:00:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get Comments
|
||||||
|
|
||||||
|
**Method:** `GET /api/v1/repos/{owner}/{repo}/issues/{index}/comments`
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"body": "Comment text",
|
||||||
|
"user": {
|
||||||
|
"login": "username"
|
||||||
|
},
|
||||||
|
"created_at": "2025-12-17T01:49:47Z",
|
||||||
|
"updated_at": "2025-12-17T01:49:47Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Add Comment
|
||||||
|
|
||||||
|
**Method:** `POST /api/v1/repos/{owner}/{repo}/issues/{index}/comments`
|
||||||
|
|
||||||
|
**Payload:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"body": "Comment text"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"body": "Comment text",
|
||||||
|
"user": {...},
|
||||||
|
"created_at": "2025-12-17T02:00:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get Labels
|
||||||
|
|
||||||
|
**Method:** `GET /api/v1/repos/{owner}/{repo}/labels`
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "bug",
|
||||||
|
"color": "#ff0000",
|
||||||
|
"description": "Bug label"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Create Label
|
||||||
|
|
||||||
|
**Method:** `POST /api/v1/repos/{owner}/{repo}/labels`
|
||||||
|
|
||||||
|
**Payload:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "bug",
|
||||||
|
"color": "#ff0000",
|
||||||
|
"description": "Bug label"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Set Issue Labels
|
||||||
|
|
||||||
|
**Method:** `PUT /api/v1/repos/{owner}/{repo}/issues/{index}/labels`
|
||||||
|
|
||||||
|
**Payload:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"labels": [1, 2, 3]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get Milestones
|
||||||
|
|
||||||
|
**Method:** `GET /api/v1/repos/{owner}/{repo}/milestones`
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `state`: `open`, `closed`, or `all`
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"title": "Version 1.0",
|
||||||
|
"description": "First release",
|
||||||
|
"state": "open",
|
||||||
|
"due_on": "2025-12-31T00:00:00Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Create Milestone
|
||||||
|
|
||||||
|
**Method:** `POST /api/v1/repos/{owner}/{repo}/milestones`
|
||||||
|
|
||||||
|
**Payload:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "Version 1.0",
|
||||||
|
"description": "First release",
|
||||||
|
"due_on": "2025-12-31T00:00:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get Repository
|
||||||
|
|
||||||
|
**Method:** `GET /api/v1/repos/{owner}/{repo}`
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "AI_Smuggling",
|
||||||
|
"full_name": "tombomb122/AI_Smuggling",
|
||||||
|
"description": "Repository description",
|
||||||
|
"owner": {...},
|
||||||
|
"created_at": "2025-12-15T08:40:06Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Webhook Payloads
|
||||||
|
|
||||||
|
### Redmine Webhook Payload
|
||||||
|
|
||||||
|
**Event:** Issue Update
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"payload": {
|
||||||
|
"action": "updated",
|
||||||
|
"issue": {
|
||||||
|
"id": 31,
|
||||||
|
"subject": "Issue title",
|
||||||
|
"description": "Issue description",
|
||||||
|
"created_on": "2025-12-16T14:06:41.000Z",
|
||||||
|
"updated_on": "2025-12-16T14:07:37.000Z",
|
||||||
|
"start_date": "2025-12-16",
|
||||||
|
"due_date": "2025-12-19",
|
||||||
|
"estimated_hours": 5.0,
|
||||||
|
"project": {
|
||||||
|
"id": 2,
|
||||||
|
"identifier": "ai-smuggling",
|
||||||
|
"name": "AI Smuggling"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"id": 2,
|
||||||
|
"name": "In Progress"
|
||||||
|
},
|
||||||
|
"tracker": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Bug"
|
||||||
|
},
|
||||||
|
"priority": {
|
||||||
|
"id": 2,
|
||||||
|
"name": "Normal"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"id": 1,
|
||||||
|
"login": "admin"
|
||||||
|
},
|
||||||
|
"assignee": {
|
||||||
|
"id": 1,
|
||||||
|
"login": "admin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"journal": {
|
||||||
|
"id": 14,
|
||||||
|
"notes": "Comment text",
|
||||||
|
"created_on": "2025-12-16T14:07:37.000Z",
|
||||||
|
"author": {
|
||||||
|
"id": 1,
|
||||||
|
"login": "admin"
|
||||||
|
},
|
||||||
|
"details": [
|
||||||
|
{
|
||||||
|
"property": "attr",
|
||||||
|
"prop_key": "status_id",
|
||||||
|
"old_value": "1",
|
||||||
|
"value": "2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": "https://redmine.example.com/issues/31"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Gitea Webhook Payload
|
||||||
|
|
||||||
|
**Event:** Issue Update
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"action": "opened",
|
||||||
|
"number": 27,
|
||||||
|
"issue": {
|
||||||
|
"id": 27,
|
||||||
|
"number": 27,
|
||||||
|
"title": "Issue title",
|
||||||
|
"body": "Issue description",
|
||||||
|
"state": "open",
|
||||||
|
"user": {
|
||||||
|
"id": 1,
|
||||||
|
"login": "tombomb122",
|
||||||
|
"full_name": "Thomas Scott"
|
||||||
|
},
|
||||||
|
"assignee": {
|
||||||
|
"id": 1,
|
||||||
|
"login": "tombomb122"
|
||||||
|
},
|
||||||
|
"labels": [],
|
||||||
|
"milestone": null,
|
||||||
|
"created_at": "2025-12-17T01:49:47Z",
|
||||||
|
"updated_at": "2025-12-17T01:49:47Z",
|
||||||
|
"due_date": null
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "AI_Smuggling",
|
||||||
|
"full_name": "tombomb122/AI_Smuggling",
|
||||||
|
"owner": {
|
||||||
|
"login": "tombomb122"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sender": {
|
||||||
|
"id": 1,
|
||||||
|
"login": "tombomb122"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Event:** Issue Comment
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"action": "created",
|
||||||
|
"issue": {...},
|
||||||
|
"comment": {
|
||||||
|
"id": 1,
|
||||||
|
"body": "Comment text",
|
||||||
|
"user": {
|
||||||
|
"login": "tombomb122"
|
||||||
|
},
|
||||||
|
"created_at": "2025-12-17T02:00:00Z",
|
||||||
|
"updated_at": "2025-12-17T02:00:00Z"
|
||||||
|
},
|
||||||
|
"repository": {...},
|
||||||
|
"sender": {...}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Mapping
|
||||||
|
|
||||||
|
### Status Mapping
|
||||||
|
|
||||||
|
#### Redmine → Gitea
|
||||||
|
|
||||||
|
| Redmine Status ID | Redmine Status Name | Gitea State |
|
||||||
|
|-------------------|---------------------|-------------|
|
||||||
|
| 1 | New | open |
|
||||||
|
| 2 | In Progress | open |
|
||||||
|
| 3 | Resolved | open |
|
||||||
|
| 4 | Feedback | open |
|
||||||
|
| 5 | Closed | closed |
|
||||||
|
| 6 | Rejected | closed |
|
||||||
|
|
||||||
|
#### Gitea → Redmine
|
||||||
|
|
||||||
|
| Gitea State | Redmine Status ID | Redmine Status Name |
|
||||||
|
|-------------|-------------------|---------------------|
|
||||||
|
| open | 2 | In Progress |
|
||||||
|
| closed | 5 | Closed |
|
||||||
|
|
||||||
|
### Priority Mapping
|
||||||
|
|
||||||
|
| Redmine Priority ID | Redmine Priority Name | Gitea Label |
|
||||||
|
|---------------------|----------------------|-------------|
|
||||||
|
| 1 | Low | Low Priority |
|
||||||
|
| 2 | Normal | Normal Priority |
|
||||||
|
| 3 | High | High Priority |
|
||||||
|
| 4 | Urgent | Urgent |
|
||||||
|
| 5 | Immediate | Immediate |
|
||||||
|
|
||||||
|
### Tracker Mapping
|
||||||
|
|
||||||
|
| Redmine Tracker ID | Redmine Tracker Name | Gitea Label |
|
||||||
|
|--------------------|---------------------|-------------|
|
||||||
|
| 1 | Bug | bug |
|
||||||
|
| 2 | Feature | feature |
|
||||||
|
| 3 | Support | support |
|
||||||
|
|
||||||
|
### Issue Title Format
|
||||||
|
|
||||||
|
**Redmine → Gitea:**
|
||||||
|
```
|
||||||
|
Redmine #{id}: {subject}
|
||||||
|
```
|
||||||
|
Example: `Redmine #31: Fix login bug`
|
||||||
|
|
||||||
|
**Gitea → Redmine:**
|
||||||
|
The original Gitea title is used, but when synced back to Gitea, it gets the Redmine prefix.
|
||||||
|
|
||||||
|
### Issue Description Format
|
||||||
|
|
||||||
|
**Gitea → Redmine:**
|
||||||
|
```
|
||||||
|
[Gitea Issue #{number}]
|
||||||
|
Author: {username}
|
||||||
|
Created at: {timestamp}
|
||||||
|
|
||||||
|
{original body}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Redmine → Gitea:**
|
||||||
|
```
|
||||||
|
{original description}
|
||||||
|
|
||||||
|
---
|
||||||
|
**Custom Fields:**
|
||||||
|
- Field1: Value1
|
||||||
|
- Field2: Value2
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comment/Journal Format
|
||||||
|
|
||||||
|
**Redmine Journal → Gitea Comment:**
|
||||||
|
```
|
||||||
|
[Redmine Journal #{id}]
|
||||||
|
**{author}** commented:
|
||||||
|
|
||||||
|
{notes}
|
||||||
|
|
||||||
|
**Changes:**
|
||||||
|
- field_name: old_value → new_value
|
||||||
|
```
|
||||||
|
|
||||||
|
**Gitea Comment → Redmine Journal:**
|
||||||
|
```
|
||||||
|
[Gitea Comment #{id}]
|
||||||
|
{username} commented:
|
||||||
|
|
||||||
|
{body}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error Codes
|
||||||
|
|
||||||
|
### HTTP Status Codes
|
||||||
|
|
||||||
|
- `200 OK`: Request successful
|
||||||
|
- `400 Bad Request`: Invalid payload or parameters
|
||||||
|
- `401 Unauthorized`: Invalid or missing API credentials
|
||||||
|
- `403 Forbidden`: Insufficient permissions
|
||||||
|
- `404 Not Found`: Resource not found
|
||||||
|
- `422 Unprocessable Entity`: Validation errors
|
||||||
|
- `500 Internal Server Error`: Server error
|
||||||
|
|
||||||
|
### Redmine Errors
|
||||||
|
|
||||||
|
Common Redmine API errors:
|
||||||
|
- Invalid API key
|
||||||
|
- Project not found
|
||||||
|
- Issue not found
|
||||||
|
- Insufficient permissions
|
||||||
|
- Validation errors
|
||||||
|
|
||||||
|
### Gitea Errors
|
||||||
|
|
||||||
|
Common Gitea API errors:
|
||||||
|
- Invalid token
|
||||||
|
- Repository not found
|
||||||
|
- Issue not found
|
||||||
|
- Label already exists
|
||||||
|
- Rate limit exceeded
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rate Limiting
|
||||||
|
|
||||||
|
### Redmine
|
||||||
|
- No explicit rate limit in API
|
||||||
|
- Server configuration may impose limits
|
||||||
|
- Consider implementing client-side throttling
|
||||||
|
|
||||||
|
### Gitea
|
||||||
|
- Rate limits depend on instance configuration
|
||||||
|
- Default: Varies by instance
|
||||||
|
- The sync server implements retry logic with exponential backoff
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Authentication**: Securely store API keys and tokens
|
||||||
|
2. **Error Handling**: Always check response status codes
|
||||||
|
3. **Retry Logic**: Implement exponential backoff for transient failures
|
||||||
|
4. **Webhooks**: Validate webhook signatures in production
|
||||||
|
5. **Logging**: Log all API interactions for debugging
|
||||||
|
6. **Testing**: Test sync in development environment first
|
||||||
|
7. **Monitoring**: Monitor API usage and error rates
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common API Issues
|
||||||
|
|
||||||
|
**Issue: 401 Unauthorized**
|
||||||
|
- Verify API key/token is correct
|
||||||
|
- Check token has required permissions
|
||||||
|
- Ensure token hasn't expired
|
||||||
|
|
||||||
|
**Issue: 404 Not Found**
|
||||||
|
- Verify resource exists (project, issue, repository)
|
||||||
|
- Check project/repository identifiers are correct
|
||||||
|
- Ensure proper URL formatting
|
||||||
|
|
||||||
|
**Issue: 422 Validation Error**
|
||||||
|
- Review required fields
|
||||||
|
- Check data types match API expectations
|
||||||
|
- Validate date formats
|
||||||
|
|
||||||
|
**Issue: Rate Limited**
|
||||||
|
- Implement retry with backoff
|
||||||
|
- Reduce sync frequency
|
||||||
|
- Contact administrator for increased limits
|
||||||
|
|
||||||
|
### Debug Tips
|
||||||
|
|
||||||
|
1. Enable verbose logging: `LOG_VERBOSE=true`
|
||||||
|
2. Use API testing tools (Postman, curl) to verify credentials
|
||||||
|
3. Check server logs for detailed error messages
|
||||||
|
4. Verify network connectivity to both platforms
|
||||||
|
5. Test individual API endpoints before full sync
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [Redmine API Documentation](https://www.redmine.org/projects/redmine/wiki/Rest_api)
|
||||||
|
- [Gitea API Documentation](https://docs.gitea.com/api/1.20/)
|
||||||
|
- [Redmine Webhooks](https://www.redmine.org/projects/redmine/wiki/Webhooks)
|
||||||
|
- [Gitea Webhooks](https://docs.gitea.com/usage/webhooks)
|
||||||
Loading…
Reference in New Issue