diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index d7bc2a4..b241043 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -2,4 +2,6 @@ FROM rust:1-bookworm RUN apt update && apt upgrade -y && apt install fish iputils-ping -y RUN rustup component add clippy rustfmt -RUN useradd -ms /bin/fish vscode \ No newline at end of file +RUN useradd -ms /bin/fish vscode +USER vscode +RUN cargo install sqlx-cli \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 5863edb..510997c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] actix-web = "4.4.0" -chrono = "0.4.31" +chrono = { version = "0.4.31", features = ["serde"] } env_logger = "0.10.0" log = "0.4.20" serde = "1.0.188" diff --git a/migrations/2_telemetry.sql b/migrations/2_telemetry.sql index 10fec21..84bd4d9 100644 --- a/migrations/2_telemetry.sql +++ b/migrations/2_telemetry.sql @@ -1,10 +1,10 @@ -- Add migration script here CREATE TABLE Telemetry ( timestamp TIMESTAMP NOT NULL, - Software_Version INT, + Software_Version INT NOT NULL, Voltage FLOAT, Temperature FLOAT, - uptime INT, - device_id CHAR(32), + uptime INT NOT NULL, + device_id CHAR(32) NOT NULL, FOREIGN KEY (device_id) REFERENCES Devices(ID) ); \ No newline at end of file diff --git a/src/database.rs b/src/database.rs index f560176..9bf1791 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,10 +1,10 @@ use actix_web::{cookie::time::error, web}; use log::{error, info}; -use sqlx::{pool, postgres::PgPoolOptions, query, PgPool, Pool, Postgres, migrate}; +use sqlx::{pool, postgres::PgPoolOptions, query, PgPool, Pool, Postgres, migrate, query_as}; use thiserror::Error; use chrono::Utc; -use crate::schemas::TelemetryMessage; +use crate::schemas::{TelemetryMessage, TelemetryMessageFromDevice}; #[derive(Clone)] pub struct Database { @@ -78,13 +78,25 @@ impl Database { } } - pub async fn add_telemetry(&self, msg: &web::Json, device_id: &str) -> Result<(), DatabaseError> { + pub async fn add_telemetry(&self, msg: &web::Json, device_id: &str) -> Result<(), DatabaseError> { info!("Adding telemetry message to DB"); let current_timestamp = Utc::now().naive_utc(); query!("INSERT INTO Telemetry (timestamp, software_version, voltage, temperature, uptime, device_id) VALUES ($1, $2, $3, $4, $5, $6);", - current_timestamp, msg.version, msg.voltage, msg.temperature, msg.uptime, device_id + current_timestamp, msg.software_version, msg.voltage, msg.temperature, msg.uptime, device_id ).execute(&self.conn_pool).await?; Ok(()) } + + pub async fn get_telemetry_for_id(&self, device_id: &str) -> Result, DatabaseError> { + info!("Getting telemetry messages for {} from DB", &device_id); + let messages = query_as!(TelemetryMessage, + "SELECT timestamp, software_version, voltage, temperature, uptime + FROM Telemetry + WHERE device_id = $1 ORDER BY timestamp DESC;", &device_id) + .fetch_all(&self.conn_pool).await?; + + + Ok(messages) + } } diff --git a/src/main.rs b/src/main.rs index 3d1109f..e6d3010 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ -use actix_web::{post, web, App, HttpServer, Responder, http::StatusCode, HttpResponse}; +use actix_web::{post, web, App, HttpServer, Responder, http::StatusCode, HttpResponse, get}; use database::Database; use log::{info, error, debug}; -use crate::schemas::TelemetryMessage; +use crate::schemas::TelemetryMessageFromDevice; mod database; mod schemas; @@ -14,7 +14,7 @@ struct AppState { async fn receive_telemetry( device_id: web::Path, data: web::Data, - telemetry_message: web::Json + telemetry_message: web::Json ) -> impl Responder { info!("POST - telementry - Processing device id {}", device_id); match data.db.create_device_if_not_exists(&device_id).await{ @@ -24,10 +24,33 @@ async fn receive_telemetry( return HttpResponse::InternalServerError(); } }; - debug!("{:?}", telemetry_message); - data.db.add_telemetry(&telemetry_message, &device_id).await; - HttpResponse::Ok() + match data.db.add_telemetry(&telemetry_message, &device_id).await{ + Ok(_) => HttpResponse::Created(), + Err(e) => { + error!("adding Telemetry message to DB failed \n{}", e); + HttpResponse::InternalServerError() + } + } +} + +#[get("/telemetry/{device_id}")] +async fn get_telemetry( + device_id: web::Path, + data: web::Data +) -> impl Responder { + info!("GET - telementry - Processing device id {}", device_id); + let messages = match data.db.get_telemetry_for_id(&device_id).await{ + Ok(msgs) => msgs, + Err(e) => { + error!("Getting Telemetry Messages from DB failed \n{}", e); + return HttpResponse::InternalServerError().finish() + } + }; + + + HttpResponse::Ok().json(messages) + } #[actix_web::main] @@ -43,6 +66,7 @@ async fn main() -> std::io::Result<()> { App::new() .app_data(web::Data::new(AppState { db: db.clone() })) .service(receive_telemetry) + .service(get_telemetry) }) .bind(("127.0.0.1", 8080))? .run() diff --git a/src/schemas.rs b/src/schemas.rs index 39ef08b..bcb53d1 100644 --- a/src/schemas.rs +++ b/src/schemas.rs @@ -1,9 +1,19 @@ -use serde::Deserialize; +use chrono::NaiveDateTime; +use serde::{Deserialize, Serialize}; -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, Serialize)] pub struct TelemetryMessage { pub uptime: i32, pub voltage: Option, pub temperature: Option, - pub version: i32 + pub software_version: i32, + pub timestamp: NaiveDateTime +} + +#[derive(Deserialize, Debug, Serialize)] +pub struct TelemetryMessageFromDevice { + pub uptime: i32, + pub voltage: Option, + pub temperature: Option, + pub software_version: i32, } \ No newline at end of file