Skip to content

Commit

Permalink
docker-compose.api got merged out
Browse files Browse the repository at this point in the history
  • Loading branch information
jhheider committed Oct 21, 2024
1 parent 163c2fa commit bdee691
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 8 deletions.
2 changes: 2 additions & 0 deletions api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ license = "MIT"
repository = "https://github.com/teaxyz/chai-oss"

[dependencies]
uuid = { version = "1.11.0", features = ["serde", "v4"] }
actix-web = "4.3"
dotenv = "0.15"
tokio = { version = "1", features = ["full"] }
Expand All @@ -20,4 +21,5 @@ chrono = { version = "0.4", features = ["serde"] }
tokio-postgres = { version = "0.7", features = [
"with-serde_json-1",
"with-chrono-0_4",
"with-uuid-1",
] }
46 changes: 46 additions & 0 deletions api/src/handlers.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use actix_web::{get, web, HttpResponse, Responder};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use tokio_postgres::error::SqlState;
use uuid::Uuid;

use crate::app_state::AppState;
use crate::utils::{get_column_names, rows_to_json};
Expand Down Expand Up @@ -88,3 +90,47 @@ pub async fn get_table(
}
}
}

#[get("/{table}/{id}")]
pub async fn get_table_row(
path: web::Path<(String, Uuid)>,
data: web::Data<AppState>,
) -> impl Responder {
let (table_name, id) = path.into_inner();

if !data.tables.contains(&table_name) {
return HttpResponse::NotFound().json(json!({
"error": format!("Table '{}' not found", table_name)
}));
}

let query = format!("SELECT * FROM {} WHERE id = $1", table_name);

match data.client.query_one(&query, &[&id]).await {
Ok(row) => {
let json = rows_to_json(&[row]);
let value = json.first().unwrap();
HttpResponse::Ok().json(value)
}
Err(e) => {
if e.as_db_error()
.map_or(false, |e| e.code() == &SqlState::UNDEFINED_TABLE)
{
HttpResponse::NotFound().json(json!({
"error": format!("Table '{}' not found", table_name)
}))
} else if e
.as_db_error()
.map_or(false, |e| e.code() == &SqlState::NO_DATA_FOUND)
{
HttpResponse::NotFound().json(json!({
"error": format!("No row found with id '{}' in table '{}'", id, table_name)
}))
} else {
HttpResponse::InternalServerError().json(json!({
"error": format!("Database error: {}", e)
}))
}
}
}
}
3 changes: 2 additions & 1 deletion api/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::sync::Arc;

use crate::app_state::AppState;
use crate::db::create_db_client;
use crate::handlers::{get_table, heartbeat, list_tables};
use crate::handlers::{get_table, get_table_row, heartbeat, list_tables};
use crate::logging::setup_logger;

#[actix_web::main]
Expand Down Expand Up @@ -40,6 +40,7 @@ async fn main() -> std::io::Result<()> {
.service(list_tables)
.service(heartbeat)
.service(get_table)
.service(get_table_row)
})
.bind(&bind_address)?
.run()
Expand Down
19 changes: 12 additions & 7 deletions api/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use chrono::{DateTime, NaiveDate, NaiveDateTime, Utc};
use serde_json::{json, Value};
use tokio_postgres::types::{Json, Type};
use tokio_postgres::Row;
use tokio_postgres::{types::Type, Row};
use uuid::Uuid;

pub fn get_column_names(rows: &[Row]) -> Vec<String> {
if let Some(row) = rows.first() {
Expand All @@ -27,20 +28,24 @@ pub fn rows_to_json(rows: &[Row]) -> Vec<Value> {
Type::BOOL => json!(row.get::<_, bool>(i)),
Type::VARCHAR | Type::TEXT | Type::BPCHAR => json!(row.get::<_, String>(i)),
Type::TIMESTAMP => {
let ts: chrono::NaiveDateTime = row.get(i);
let ts: NaiveDateTime = row.get(i);
json!(ts.to_string())
}
Type::TIMESTAMPTZ => {
let ts: chrono::DateTime<chrono::Utc> = row.get(i);
let ts: DateTime<Utc> = row.get(i);
json!(ts.to_rfc3339())
}
Type::DATE => {
let date: chrono::NaiveDate = row.get(i);
let date: NaiveDate = row.get(i);
json!(date.to_string())
}
Type::JSON | Type::JSONB => {
let json_value: Json<Value> = row.get(i);
json_value.0
let json_value: serde_json::Value = row.get(i);
json_value
}
Type::UUID => {
let uuid: Uuid = row.get(i);
json!(uuid.to_string())
}
_ => Value::Null,
};
Expand Down
23 changes: 23 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,29 @@ services:
alembic:
condition: service_completed_successfully

api:
build:
context: ./api
dockerfile: Dockerfile
environment:
- DATABASE_URL=postgresql://postgres:s3cr3t@db:5432/chai
- HOST=0.0.0.0
- PORT=8080
ports:
- "8080:8080"
depends_on:
db:
condition: service_healthy
alembic:
condition: service_completed_successfully
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/heartbeat"]
interval: 30s
timeout: 10s
retries: 3
start_period: 5s

monitor:
build:
context: ./monitor
Expand Down

0 comments on commit bdee691

Please sign in to comment.