Skip to content

Commit 0f2f433

Browse files
committed
feat: implement HTTP API endpoints for payload operations
- Implemented create_payload endpoint with payload size validation - Implemented get_payload endpoint with proper error handling - Added placeholder for delete_payload endpoint - Fixed Redis persistence issue by disabling stop-writes-on-bgsave-error - Added API tester HTML page for easier testing - Updated progress in plan-progress.md
1 parent b7f61bf commit 0f2f433

File tree

8 files changed

+479
-10
lines changed

8 files changed

+479
-10
lines changed

api-tester.html

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Jump API Tester</title>
7+
<style>
8+
body {
9+
font-family: Arial, sans-serif;
10+
max-width: 800px;
11+
margin: 0 auto;
12+
padding: 20px;
13+
}
14+
.container {
15+
display: flex;
16+
flex-direction: column;
17+
gap: 20px;
18+
}
19+
.card {
20+
border: 1px solid #ccc;
21+
border-radius: 5px;
22+
padding: 15px;
23+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
24+
}
25+
.response {
26+
background-color: #f5f5f5;
27+
padding: 10px;
28+
border-radius: 5px;
29+
white-space: pre-wrap;
30+
max-height: 300px;
31+
overflow: auto;
32+
}
33+
button {
34+
padding: 8px 16px;
35+
background-color: #4CAF50;
36+
color: white;
37+
border: none;
38+
border-radius: 4px;
39+
cursor: pointer;
40+
margin-top: 10px;
41+
}
42+
button:hover {
43+
background-color: #45a049;
44+
}
45+
input, textarea {
46+
width: 100%;
47+
padding: 8px;
48+
margin: 5px 0;
49+
box-sizing: border-box;
50+
border: 1px solid #ccc;
51+
border-radius: 4px;
52+
}
53+
label {
54+
font-weight: bold;
55+
}
56+
</style>
57+
</head>
58+
<body>
59+
<h1>Jump API Tester</h1>
60+
<div class="container">
61+
<div class="card">
62+
<h2>Health Check</h2>
63+
<button id="healthCheck">Test Health Endpoint</button>
64+
<h3>Response:</h3>
65+
<div id="healthResponse" class="response"></div>
66+
</div>
67+
68+
<div class="card">
69+
<h2>Create Payload</h2>
70+
<div>
71+
<label for="content">Content:</label>
72+
<textarea id="content" rows="4">Test payload content</textarea>
73+
</div>
74+
<div>
75+
<label for="mimeType">MIME Type:</label>
76+
<input type="text" id="mimeType" value="text/plain">
77+
</div>
78+
<button id="createPayload">Create Payload</button>
79+
<h3>Response:</h3>
80+
<div id="createResponse" class="response"></div>
81+
</div>
82+
83+
<div class="card">
84+
<h2>Get Payload</h2>
85+
<div>
86+
<label for="payloadId">Payload ID:</label>
87+
<input type="text" id="payloadId">
88+
</div>
89+
<button id="getPayload">Get Payload</button>
90+
<h3>Response:</h3>
91+
<div id="getResponse" class="response"></div>
92+
</div>
93+
94+
<div class="card">
95+
<h2>Delete Payload</h2>
96+
<div>
97+
<label for="deletePayloadId">Payload ID:</label>
98+
<input type="text" id="deletePayloadId">
99+
</div>
100+
<button id="deletePayload">Delete Payload</button>
101+
<h3>Response:</h3>
102+
<div id="deleteResponse" class="response"></div>
103+
</div>
104+
</div>
105+
106+
<script>
107+
const API_BASE_URL = 'http://localhost:8080/api';
108+
109+
// Health Check
110+
document.getElementById('healthCheck').addEventListener('click', async () => {
111+
try {
112+
const response = await fetch(`${API_BASE_URL}/health`);
113+
const data = await response.json();
114+
document.getElementById('healthResponse').textContent = JSON.stringify(data, null, 2);
115+
} catch (error) {
116+
document.getElementById('healthResponse').textContent = `Error: ${error.message}`;
117+
}
118+
});
119+
120+
// Create Payload
121+
document.getElementById('createPayload').addEventListener('click', async () => {
122+
try {
123+
const content = document.getElementById('content').value;
124+
const mimeType = document.getElementById('mimeType').value;
125+
126+
const response = await fetch(`${API_BASE_URL}/v1/payloads`, {
127+
method: 'POST',
128+
headers: {
129+
'Content-Type': 'application/json'
130+
},
131+
body: JSON.stringify({
132+
content,
133+
mime_type: mimeType
134+
})
135+
});
136+
137+
const data = await response.json();
138+
document.getElementById('createResponse').textContent = JSON.stringify(data, null, 2);
139+
140+
// Auto-fill the payload ID for get and delete
141+
if (data.id) {
142+
document.getElementById('payloadId').value = data.id;
143+
document.getElementById('deletePayloadId').value = data.id;
144+
}
145+
} catch (error) {
146+
document.getElementById('createResponse').textContent = `Error: ${error.message}`;
147+
}
148+
});
149+
150+
// Get Payload
151+
document.getElementById('getPayload').addEventListener('click', async () => {
152+
try {
153+
const payloadId = document.getElementById('payloadId').value;
154+
if (!payloadId) {
155+
document.getElementById('getResponse').textContent = 'Error: Please enter a payload ID';
156+
return;
157+
}
158+
159+
const response = await fetch(`${API_BASE_URL}/v1/payloads/${payloadId}`);
160+
const data = await response.json();
161+
document.getElementById('getResponse').textContent = JSON.stringify(data, null, 2);
162+
} catch (error) {
163+
document.getElementById('getResponse').textContent = `Error: ${error.message}`;
164+
}
165+
});
166+
167+
// Delete Payload
168+
document.getElementById('deletePayload').addEventListener('click', async () => {
169+
try {
170+
const payloadId = document.getElementById('deletePayloadId').value;
171+
if (!payloadId) {
172+
document.getElementById('deleteResponse').textContent = 'Error: Please enter a payload ID';
173+
return;
174+
}
175+
176+
const response = await fetch(`${API_BASE_URL}/v1/payloads/${payloadId}`, {
177+
method: 'DELETE'
178+
});
179+
180+
const data = await response.json();
181+
document.getElementById('deleteResponse').textContent = JSON.stringify(data, null, 2);
182+
} catch (error) {
183+
document.getElementById('deleteResponse').textContent = `Error: ${error.message}`;
184+
}
185+
});
186+
</script>
187+
</body>
188+
</html>

plan-progress.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@
5353
- [x] Connection pool
5454
- [x] CRUD operations
5555
- [x] Expiry handling
56-
- [ ] HTTP API endpoints
57-
- [ ] API versioning structure (v1)
58-
- [ ] Payload size validation
59-
- [ ] Routes and handlers
60-
- [ ] Error handling middleware
56+
- [x] HTTP API endpoints
57+
- [x] API versioning structure (v1)
58+
- [x] Payload size validation
59+
- [x] Routes and handlers
60+
- [x] Error handling middleware
6161

6262
### Phase 4: Testing and Documentation
6363
- [ ] Unit tests
@@ -89,4 +89,4 @@
8989
- [ ] Logging configuration
9090

9191
## Current Status
92-
🚀 Infrastructure Layer in Progress - Redis repository and logging infrastructure complete. Next: HTTP API endpoints
92+
🚀 HTTP API endpoints implementation complete. Create and Get payload endpoints are fully functional. Delete endpoint has a placeholder implementation. Next: Unit and integration tests.

src/api/health.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//! Health check endpoints.
2+
//!
3+
//! This module provides health check endpoints for the API.
4+
5+
use actix_web::{web, HttpResponse, Responder};
6+
use tracing::info;
7+
8+
/// Health check endpoint.
9+
///
10+
/// Returns a simple OK response to indicate that the service is running.
11+
#[tracing::instrument(name = "Health check")]
12+
pub async fn health_check() -> impl Responder {
13+
info!("Health check request received");
14+
HttpResponse::Ok().json(serde_json::json!({
15+
"status": "ok",
16+
"version": env!("CARGO_PKG_VERSION"),
17+
}))
18+
}
19+
20+
/// Configure health check routes.
21+
pub fn configure() -> impl Fn(&mut web::ServiceConfig) {
22+
|cfg: &mut web::ServiceConfig| {
23+
cfg.service(
24+
web::scope("/health")
25+
.route("", web::get().to(health_check))
26+
);
27+
}
28+
}

src/api/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,21 @@
66
//! - Middleware
77
//! - Response types
88
9+
use actix_web::web;
10+
911
pub mod v1;
1012
pub mod middleware;
13+
pub mod health;
14+
15+
/// Configure all API routes.
16+
///
17+
/// This function sets up all the routes for the API, including all versions.
18+
pub fn configure() -> impl Fn(&mut web::ServiceConfig) {
19+
|cfg: &mut web::ServiceConfig| {
20+
cfg.service(
21+
web::scope("/api")
22+
.configure(v1::configure())
23+
.configure(health::configure())
24+
);
25+
}
26+
}

src/api/v1/mod.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,26 @@
55
//! - Request handlers
66
//! - Middleware
77
//! - Response types
8+
//! - Payload endpoints
9+
10+
use actix_web::web;
11+
12+
mod payload;
13+
14+
/// Configure v1 API routes.
15+
///
16+
/// This function sets up all the routes for the v1 API.
17+
pub fn configure() -> impl Fn(&mut web::ServiceConfig) {
18+
|cfg: &mut web::ServiceConfig| {
19+
cfg.service(
20+
web::scope("/v1")
21+
// Payload routes
22+
.route("/payloads", web::post().to(payload::create_payload))
23+
.route("/payloads/{id}", web::get().to(payload::get_payload))
24+
.route("/payloads/{id}", web::delete().to(payload::delete_payload))
25+
);
26+
}
27+
}
28+
29+
// Re-export handlers for testing
30+
pub use payload::{create_payload, get_payload, delete_payload};

0 commit comments

Comments
 (0)