feat: implement device update POST route
parent
d6fb4573d0
commit
4d37ddb780
|
@ -18,8 +18,8 @@ on later, no guarantees.
|
|||
* Suggestions API
|
||||
- [ ] Retrieve Suggested Podcasts
|
||||
* Device API
|
||||
- [ ] Update Device Data
|
||||
- [ ] List Devices
|
||||
- [x] Update Device Data
|
||||
- [-] List Devices
|
||||
- [ ] Get Device Updates
|
||||
* Subscriptions API
|
||||
- [ ] Get Subscriptions of Device
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
pub mod models;
|
||||
mod schema;
|
||||
|
||||
pub use models::device::{Device, NewDevice};
|
||||
pub use models::device::{Device, DeviceType, NewDevice};
|
||||
pub use models::session::Session;
|
||||
pub use models::user::{NewUser, User};
|
||||
|
||||
|
|
|
@ -51,6 +51,52 @@ impl Device {
|
|||
.filter(devices::user_id.eq(user_id))
|
||||
.get_results(&mut pool.get()?)?)
|
||||
}
|
||||
|
||||
pub fn by_device_id(pool: &DbPool, user_id: i64, device_id: &str) -> DbResult<Option<Self>> {
|
||||
Ok(devices::dsl::devices
|
||||
.select(Self::as_select())
|
||||
.filter(
|
||||
devices::user_id
|
||||
.eq(user_id)
|
||||
.and(devices::device_id.eq(device_id)),
|
||||
)
|
||||
.get_result(&mut pool.get()?)
|
||||
.optional()?)
|
||||
}
|
||||
|
||||
pub fn update(&self, pool: &DbPool) -> DbResult<()> {
|
||||
Ok(diesel::update(
|
||||
devices::table.filter(
|
||||
devices::user_id
|
||||
.eq(self.user_id)
|
||||
.and(devices::device_id.eq(&self.device_id)),
|
||||
),
|
||||
)
|
||||
.set((
|
||||
devices::caption.eq(&self.caption),
|
||||
devices::type_.eq(&self.type_),
|
||||
))
|
||||
.execute(&mut pool.get()?)
|
||||
.map(|_| ())?)
|
||||
}
|
||||
}
|
||||
|
||||
impl NewDevice {
|
||||
pub fn new(user_id: i64, device_id: String, caption: String, type_: DeviceType) -> Self {
|
||||
Self {
|
||||
device_id,
|
||||
user_id,
|
||||
caption,
|
||||
type_,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(self, pool: &DbPool) -> DbResult<Device> {
|
||||
Ok(diesel::insert_into(devices::table)
|
||||
.values(&self)
|
||||
.returning(Device::as_returning())
|
||||
.get_result(&mut pool.get()?)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DeviceType {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use axum::{
|
||||
extract::{Path, State},
|
||||
middleware,
|
||||
routing::get,
|
||||
routing::{get, post},
|
||||
Extension, Json, Router,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
db::{self, User},
|
||||
|
@ -19,14 +19,49 @@ use super::auth::auth_middleware;
|
|||
pub fn router(ctx: Context) -> Router<Context> {
|
||||
Router::new()
|
||||
.route("/{username}", get(get_devices))
|
||||
.route("/{username}/{id}", post(post_device))
|
||||
.layer(middleware::from_fn_with_state(ctx.clone(), auth_middleware))
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum DeviceType {
|
||||
Desktop,
|
||||
Laptop,
|
||||
Mobile,
|
||||
Server,
|
||||
Other,
|
||||
}
|
||||
|
||||
impl From<DeviceType> for db::DeviceType {
|
||||
fn from(value: DeviceType) -> Self {
|
||||
match value {
|
||||
DeviceType::Desktop => Self::Desktop,
|
||||
DeviceType::Laptop => Self::Laptop,
|
||||
DeviceType::Mobile => Self::Mobile,
|
||||
DeviceType::Server => Self::Server,
|
||||
DeviceType::Other => Self::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<db::DeviceType> for DeviceType {
|
||||
fn from(value: db::DeviceType) -> Self {
|
||||
match value {
|
||||
db::DeviceType::Desktop => Self::Desktop,
|
||||
db::DeviceType::Laptop => Self::Laptop,
|
||||
db::DeviceType::Mobile => Self::Mobile,
|
||||
db::DeviceType::Server => Self::Server,
|
||||
db::DeviceType::Other => Self::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Device {
|
||||
id: String,
|
||||
caption: String,
|
||||
r#type: String,
|
||||
r#type: DeviceType,
|
||||
subscriptions: i64,
|
||||
}
|
||||
|
||||
|
@ -50,12 +85,56 @@ async fn get_devices(
|
|||
.map(|d| Device {
|
||||
id: d.device_id,
|
||||
caption: d.caption,
|
||||
r#type: d.type_.to_string(),
|
||||
r#type: d.type_.into(),
|
||||
// TODO implement subscription count
|
||||
subscriptions: 0,
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Json(devices))
|
||||
// let devices: Vec<Device> = devices.iter
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DevicePatch {
|
||||
caption: Option<String>,
|
||||
r#type: Option<DeviceType>,
|
||||
}
|
||||
|
||||
async fn post_device(
|
||||
State(ctx): State<Context>,
|
||||
Path((_username, id)): Path<(String, String)>,
|
||||
Extension(user): Extension<User>,
|
||||
Json(patch): Json<DevicePatch>,
|
||||
) -> AppResult<()> {
|
||||
let id = id
|
||||
.strip_suffix(".json")
|
||||
.ok_or(AppError::NotFound)?
|
||||
.to_string();
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
if let Some(mut device) = db::Device::by_device_id(&ctx.pool, user.id, &id)? {
|
||||
if let Some(caption) = patch.caption {
|
||||
device.caption = caption;
|
||||
}
|
||||
|
||||
if let Some(type_) = patch.r#type {
|
||||
device.type_ = type_.into();
|
||||
}
|
||||
|
||||
device.update(&ctx.pool)
|
||||
} else {
|
||||
db::NewDevice::new(
|
||||
user.id,
|
||||
id.to_string(),
|
||||
patch.caption.unwrap_or(String::new()),
|
||||
patch.r#type.unwrap_or(DeviceType::Other).into(),
|
||||
)
|
||||
.insert(&ctx.pool)
|
||||
.map(|_| ())
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue