working example with write and select
This commit is contained in:
21
Cargo.lock
generated
21
Cargo.lock
generated
@@ -460,6 +460,7 @@ dependencies = [
|
|||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"pq-sys",
|
"pq-sys",
|
||||||
|
"r2d2",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1088,6 +1089,17 @@ version = "5.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r2d2"
|
||||||
|
version = "0.8.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"parking_lot",
|
||||||
|
"scheduled-thread-pool",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.9.2"
|
version = "0.9.2"
|
||||||
@@ -1176,6 +1188,15 @@ version = "1.0.22"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984"
|
checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scheduled-thread-pool"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
|
||||||
|
dependencies = [
|
||||||
|
"parking_lot",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "schema"
|
name = "schema"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ serde = { version = "1.0", features = ["derive"] }
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
actix-web = "4.x.x"
|
actix-web = "4.x.x"
|
||||||
bigdecimal = {version = "0.4.10", features = ["serde"] }
|
bigdecimal = {version = "0.4.10", features = ["serde"] }
|
||||||
diesel = { version = "2.3.6", default-features = false, features = ["postgres", "serde_json", "numeric"] }
|
diesel = { version = "2.3.6", default-features = false, features = ["postgres", "serde_json", "numeric", "r2d2"] }
|
||||||
dotenvy = "0.15.7"
|
dotenvy = "0.15.7"
|
||||||
schema = "0.1.0"
|
schema = "0.1.0"
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
CREATE TYPE battery_status_enum AS ENUM ('unknown', 'unplugged', 'charging', 'full');
|
CREATE TYPE battery_status_enum AS ENUM ('unknown', 'unplugged', 'charging', 'full');
|
||||||
|
|
||||||
CREATE TABLE locations (
|
CREATE TABLE locations (
|
||||||
timestamp bigint PRIMARY KEY,
|
tst bigint PRIMARY KEY,
|
||||||
latitude numeric(9,6) NOT NULL,
|
lat numeric(9,6) NOT NULL,
|
||||||
longitude numeric(9,6) NOT NULL,
|
lon numeric(9,6) NOT NULL,
|
||||||
accuracy numeric(7,2) NOT NULL,
|
acc numeric(7,2) NOT NULL,
|
||||||
altitude numeric(7,2),
|
alt numeric(7,2),
|
||||||
velocity numeric(7,2),
|
vel numeric(7,2),
|
||||||
battery_level smallint NOT NULL,
|
batt smallint NOT NULL,
|
||||||
bearing numeric(6,3),
|
bear numeric(6,3),
|
||||||
battery_status battery_status_enum DEFAULT 'unknown' NOT NULL,
|
bs battery_status_enum DEFAULT 'unknown' NOT NULL,
|
||||||
CONSTRAINT Location_battery_level_check CHECK ((battery_level >= 0) AND (battery_level <= 100)),
|
CONSTRAINT Location_battery_level_check CHECK ((batt >= 0) AND (batt <= 100)),
|
||||||
CONSTRAINT Location_bearing_check CHECK ((bearing >= (0)::numeric) AND (bearing <= (360)::numeric))
|
CONSTRAINT Location_bearing_check CHECK ((bear >= (0)::numeric) AND (bear <= (360)::numeric))
|
||||||
)
|
)
|
||||||
WITH (oids = false);
|
WITH (oids = false);
|
||||||
|
|
||||||
CREATE INDEX locations_timestamp ON locations USING btree (timestamp);
|
CREATE INDEX locations_timestamp ON locations USING btree (tst);
|
||||||
66
src/handlers.rs
Normal file
66
src/handlers.rs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
use crate::DbPool;
|
||||||
|
use crate::{models::Location, schema::locations};
|
||||||
|
use actix_web::{Error, HttpResponse, web};
|
||||||
|
use diesel::pg::PgConnection;
|
||||||
|
use diesel::prelude::*;
|
||||||
|
use diesel::r2d2::{ConnectionManager, PooledConnection};
|
||||||
|
use diesel::result::Error as DieselError;
|
||||||
|
|
||||||
|
pub async fn index() -> Result<HttpResponse, Error> {
|
||||||
|
Ok(HttpResponse::Ok().json("Connection Successful"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_location(
|
||||||
|
pool: web::Data<DbPool>,
|
||||||
|
body: web::Json<Location>,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let location = body.into_inner();
|
||||||
|
|
||||||
|
web::block(move || {
|
||||||
|
let mut conn = get_connection(pool);
|
||||||
|
|
||||||
|
diesel::insert_into(locations::table)
|
||||||
|
.values(&location)
|
||||||
|
.execute(&mut conn)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
eprintln!("Block error: {:?}", e);
|
||||||
|
actix_web::error::ErrorInternalServerError("Block error")
|
||||||
|
})?
|
||||||
|
.map_err(|e: DieselError| {
|
||||||
|
eprintln!("Diesel error: {:?}", e);
|
||||||
|
if let DieselError::DatabaseError(kind, info) = &e {
|
||||||
|
eprintln!("DB Error Kind: {:?}", kind);
|
||||||
|
eprintln!("DB Error Info: {}", info.message());
|
||||||
|
if let Some(constraint) = info.constraint_name() {
|
||||||
|
eprintln!("Constraint: {}", constraint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actix_web::error::ErrorInternalServerError(format!("DB error: {}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(HttpResponse::Created().json("Location created successfully"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn select_locations(pool: web::Data<DbPool>) -> Result<HttpResponse, Error> {
|
||||||
|
|
||||||
|
let locations = web::block(move || {
|
||||||
|
let mut conn = get_connection(pool);
|
||||||
|
locations::table
|
||||||
|
.select(Location::as_select())
|
||||||
|
.load::<Location>(&mut conn)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|_| actix_web::error::ErrorInternalServerError("Block error"))?
|
||||||
|
.map_err(|e: DieselError| {
|
||||||
|
eprintln!("Diesel SELECT error: {:?}", e);
|
||||||
|
actix_web::error::ErrorInternalServerError("DB query failed")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().json(&locations))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_connection(pool: web::Data<DbPool>) -> PooledConnection<ConnectionManager<PgConnection>> {
|
||||||
|
pool.get().expect("Failed to get DB connection from pool")
|
||||||
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
use actix_web::web::Json;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
include!("../models.rs");
|
|
||||||
|
|
||||||
// async fn get_locations() -> impl Responder {
|
|
||||||
// let locations: Vec<Location> = vec![];
|
|
||||||
// Json(locations)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async fn create_location(Json(location): Json<Location>) -> impl Responder {
|
|
||||||
// Json(location)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async fn update_location(Json(location): Json<Location>) -> impl Responder {
|
|
||||||
// Json(location)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async fn delete_location(timestamp: i64) -> impl Responder {
|
|
||||||
// format!("Deleted location with timestamp: {}", timestamp)
|
|
||||||
// }
|
|
||||||
22
src/lib.rs
22
src/lib.rs
@@ -1,25 +1,23 @@
|
|||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use dotenvy::dotenv;
|
use dotenvy::dotenv;
|
||||||
use std::{env};
|
use std::{env};
|
||||||
use std::io::{stdout, Write};
|
use diesel::r2d2::{ConnectionManager, Pool};
|
||||||
|
|
||||||
pub mod models;
|
pub mod models;
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
|
pub mod routes;
|
||||||
|
pub mod handlers;
|
||||||
|
|
||||||
pub fn establish_connection() -> PgConnection {
|
pub type DbPool = Pool<ConnectionManager<PgConnection>>;
|
||||||
|
|
||||||
|
pub fn create_pool() -> DbPool {
|
||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
|
|
||||||
// let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
|
||||||
// let database_name = env::var("DATABASE_NAME").expect("DATABASE_NAME must be set");
|
|
||||||
// let database_user = env::var("DATABASE_USER").expect("DATABASE_USER must be set");
|
|
||||||
// let database_password = env::var("DATABASE_PASSWORD").expect("DATABASE_PASSWORD must be set");
|
|
||||||
|
|
||||||
// let connection_url= format!("postgres://{}:{}@{}/{}", database_user, database_password, database_url, database_name);
|
|
||||||
|
|
||||||
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||||
|
|
||||||
println!("{}", database_url);
|
let manager = ConnectionManager::<PgConnection>::new(database_url);
|
||||||
|
|
||||||
PgConnection::establish(&database_url)
|
Pool::builder()
|
||||||
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url))
|
.build(manager)
|
||||||
|
.expect("Failed to create DB pool")
|
||||||
}
|
}
|
||||||
39
src/main.rs
39
src/main.rs
@@ -1,42 +1,19 @@
|
|||||||
use actix_web::{web, App, HttpRequest, HttpServer, Responder};
|
use actix_web::{web, App, HttpServer};
|
||||||
use colota_backend::establish_connection;
|
use colota_backend::{create_pool, DbPool};
|
||||||
use diesel::prelude::*;
|
|
||||||
use dotenvy::dotenv;
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
pub use colota_backend::schema;
|
pub use colota_backend::schema;
|
||||||
pub use colota_backend::models;
|
pub use colota_backend::models;
|
||||||
|
pub use colota_backend::routes;
|
||||||
include!("handlers/locations.rs");
|
|
||||||
|
|
||||||
async fn index() -> impl Responder { "Connection successful" }
|
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
use self::schema::locations::dsl::*;
|
let pool: DbPool = create_pool();
|
||||||
|
|
||||||
|
HttpServer::new(move || {
|
||||||
let connection = &mut establish_connection();
|
|
||||||
let results = locations
|
|
||||||
.select(Location::as_select())
|
|
||||||
.load(connection)
|
|
||||||
.expect("Error loading locations");
|
|
||||||
|
|
||||||
println!("Displaying {} locations", results.len());
|
|
||||||
|
|
||||||
|
|
||||||
HttpServer::new(|| {
|
|
||||||
App::new()
|
App::new()
|
||||||
.route("/", web::get().to(index))
|
.app_data(web::Data::new(pool.clone()))
|
||||||
.service(
|
.configure(routes::config_routes)
|
||||||
web::scope("/api/v1")
|
|
||||||
// .service(web::resource("/locations").to(|| {
|
|
||||||
// // get_locations()
|
|
||||||
// }))
|
|
||||||
// .service(web::resource("/locations").route(web::post().to(create_location)))
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.bind("127.0.0.1:8080")?
|
.bind(("0.0.0.0", 8080))?
|
||||||
.run()
|
.run()
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -3,29 +3,30 @@ use diesel::deserialize::{self, FromSql, FromSqlRow};
|
|||||||
use diesel::pg::{Pg, PgValue};
|
use diesel::pg::{Pg, PgValue};
|
||||||
use diesel::serialize::{IsNull, Output, ToSql};
|
use diesel::serialize::{IsNull, Output, ToSql};
|
||||||
use diesel::*;
|
use diesel::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use diesel::expression::AsExpression;
|
use diesel::expression::AsExpression;
|
||||||
use crate::schema::sql_types::BatteryStatusEnum;
|
use crate::schema::sql_types::BatteryStatusEnum;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Queryable, Selectable)]
|
#[derive(Queryable, Selectable, Insertable, Serialize, Deserialize, Debug)]
|
||||||
#[diesel(table_name = crate::schema::locations)]
|
#[diesel(table_name = crate::schema::locations)]
|
||||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||||
struct Location {
|
pub struct Location {
|
||||||
latitude: BigDecimal,
|
lat: BigDecimal,
|
||||||
longitude: BigDecimal,
|
lon: BigDecimal,
|
||||||
accuracy: BigDecimal,
|
acc: BigDecimal,
|
||||||
altitude: Option<BigDecimal>,
|
alt: Option<BigDecimal>,
|
||||||
velocity: Option<BigDecimal>,
|
vel: Option<BigDecimal>,
|
||||||
battery_level: i16,
|
batt: i16,
|
||||||
battery_status: BatteryStatus,
|
bs: BatteryStatus,
|
||||||
timestamp: i64,
|
tst: i64,
|
||||||
bearing: Option<BigDecimal>,
|
bear: Option<BigDecimal>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, FromSqlRow, AsExpression)]
|
#[derive(Debug, FromSqlRow, AsExpression, Serialize, Deserialize)]
|
||||||
#[diesel(sql_type = BatteryStatusEnum)]
|
#[diesel(sql_type = BatteryStatusEnum)]
|
||||||
enum BatteryStatus {
|
pub enum BatteryStatus {
|
||||||
Unknown,
|
Unknown,
|
||||||
Unplugged,
|
Unplugged,
|
||||||
Charging,
|
Charging,
|
||||||
|
|||||||
8
src/routes.rs
Normal file
8
src/routes.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
use crate::handlers;
|
||||||
|
use actix_web::web;
|
||||||
|
|
||||||
|
pub fn config_routes(cfg: &mut web::ServiceConfig) {
|
||||||
|
cfg.service(web::resource("/").route(web::get().to(handlers::index)))
|
||||||
|
.service(web::resource("/api/location").route(web::post().to(handlers::create_location)))
|
||||||
|
.service(web::resource("/api/locations").route(web::get().to(handlers::select_locations)));
|
||||||
|
}
|
||||||
@@ -10,15 +10,15 @@ diesel::table! {
|
|||||||
use diesel::sql_types::*;
|
use diesel::sql_types::*;
|
||||||
use super::sql_types::BatteryStatusEnum;
|
use super::sql_types::BatteryStatusEnum;
|
||||||
|
|
||||||
locations (timestamp) {
|
locations (tst) {
|
||||||
timestamp -> Int8,
|
tst -> Int8,
|
||||||
latitude -> Numeric,
|
lat -> Numeric,
|
||||||
longitude -> Numeric,
|
lon -> Numeric,
|
||||||
accuracy -> Numeric,
|
acc -> Numeric,
|
||||||
altitude -> Nullable<Numeric>,
|
alt -> Nullable<Numeric>,
|
||||||
velocity -> Nullable<Numeric>,
|
vel -> Nullable<Numeric>,
|
||||||
battery_level -> Int2,
|
batt -> Int2,
|
||||||
bearing -> Nullable<Numeric>,
|
bear -> Nullable<Numeric>,
|
||||||
battery_status -> BatteryStatusEnum,
|
bs -> BatteryStatusEnum,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user