Skip to content

Commit

Permalink
create media item in repository and database
Browse files Browse the repository at this point in the history
  • Loading branch information
thebino committed Oct 20, 2023
1 parent f1dbe92 commit 8a41d74
Show file tree
Hide file tree
Showing 15 changed files with 225 additions and 54 deletions.
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ pretty_assertions = "1.3.0"

rand = "0.8.5"
rstest = "0.18.2"
rumqttc = "0.22.0"
rumqttc = "0.23.0"
rsa = "0.9.2"
reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "stream", "multipart"] }

Expand All @@ -85,6 +85,7 @@ smallvec = "1.8.0"
sqlx = "0.7.1"

testdir = "0.8.0"
tempfile = "3.8.0"
thiserror = "1.0.40"
time = "0.3.27"
tokio = { version = "1.30.0", features = ["full"] }
Expand Down
4 changes: 3 additions & 1 deletion crates/common/src/auth/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ impl fmt::Display for User {
impl User {
pub(crate) fn new(email: String) -> User {
User {
uuid: Uuid::new_v4().hyphenated().to_string(),
uuid: Uuid::parse_str("808c78e4-34bc-486a-902f-929e8b146d20")
.unwrap()
.to_string(),
email,
password: Option::None,
lastname: Option::None,
Expand Down
1 change: 1 addition & 0 deletions crates/common/src/database/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub mod location;
pub mod media_item;
pub mod reference;
pub mod tag;
pub mod user;

#[async_trait]
pub trait Database {
Expand Down
2 changes: 1 addition & 1 deletion crates/common/src/database/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
use time::OffsetDateTime;

pub struct Reference {
pub uuid: &'static str,
pub uuid: String,
pub filepath: String,
pub filename: String,
pub size: u64,
Expand Down
31 changes: 21 additions & 10 deletions crates/database/src/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use sqlx::Row;
use sqlx::SqlitePool;
use std::error::Error;
use std::i64;
use tracing::error;
use tracing::info;
use uuid::Uuid;

Expand Down Expand Up @@ -155,35 +156,42 @@ impl Database for SqliteDatabase {

return match rows {
Some(r) => {
info!(
"found media item with same name and taken_at for current owner. uuid = `{}`.",
r.uuid.clone()
);
info!("Found media item with same 'name' and 'taken_at' for owner.");

Ok(r.uuid)
}
_ => {
let query = "INSERT INTO media (uuid, owner, name, is_sensitive, added_at, taken_at) VALUES ($1, $2, $3, $4, $5, $6)";
let id = Uuid::new_v4().hyphenated().to_string();
info!("create new media item with id `{}`.", id);

sqlx::query(query)
let db_result = sqlx::query(query)
.bind(id.clone())
.bind(&user_id)
.bind(&name)
.bind(&user_id.to_string())
.bind(&name.to_string())
.bind(false)
.bind(OffsetDateTime::now_utc())
.bind(date_taken)
.execute(&self.pool)
.await?;
.await;

match db_result {
Ok(_) => {
info!("New media item created with id {}.", id)
}
Err(e) => {
error!("Could not create new media item in database! {}", e);
}
}

Ok(id)
}
};
}

async fn get_media_item(&self, _media_id: &str) -> Result<MediaItem, Box<dyn Error>> {
Err("Not implemented".into())
}

async fn add_reference(
&self,
user_id: &str,
Expand Down Expand Up @@ -406,6 +414,7 @@ mod tests {
Ok(())
}

//noinspection DuplicatedCode
#[sqlx::test]
async fn create_media_item_should_succeed(pool: SqlitePool) -> sqlx::Result<()> {
// given
Expand Down Expand Up @@ -437,6 +446,7 @@ mod tests {
Ok(())
}

//noinspection DuplicatedCode
#[sqlx::test]
async fn create_media_item_should_return_existing_uuid(pool: SqlitePool) -> sqlx::Result<()> {
// given
Expand Down Expand Up @@ -480,6 +490,7 @@ mod tests {
Ok(())
}

//noinspection DuplicatedCode
#[sqlx::test]
async fn add_reference_should_succeed(pool: SqlitePool) -> sqlx::Result<()> {
// given
Expand Down Expand Up @@ -518,7 +529,7 @@ mod tests {
let metadata = std::fs::metadata(path.clone()).unwrap();

let reference = Reference {
uuid: reference_id,
uuid: reference_id.to_string(),
filepath,
filename: filename.to_string(),
size: metadata.len(),
Expand Down
6 changes: 4 additions & 2 deletions crates/media/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,28 @@ database.workspace = true
time.workspace = true

tracing.workspace = true
tokio = { workspace = true, features = ["full"] }

# serialization
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true

# Router
axum = { workspace = true, features = ["multipart"] }
hyper = { workspace = true, features = ["full"] }
tower-http.workspace = true
mime.workspace = true

# persistency
uuid = { workspace = true, features = ["serde"] }
sqlx.workspace = true
rand.workspace = true
tempfile.workspace = true


[dev-dependencies]
# testing
mockall.workspace = true
rstest.workspace = true
tokio.workspace = true
tower = { workspace = true, features = ["util"] }
hyper = { workspace = true, features = ["full"] }
testdir.workspace = true
7 changes: 3 additions & 4 deletions crates/media/src/api/routes/get_media.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ pub(crate) async fn get_media(
user: User,
Query(query): Query<MediaListQuery>,
) -> Result<Json<String>, StatusCode> {
let items: Result<Vec<MediaItem>, DataAccessError> =
repo.get_media_items_for_user(Uuid::parse_str(user.uuid.as_str()).unwrap()).await;
let items: Result<Vec<MediaItem>, DataAccessError> = repo
.get_media_items_for_user(Uuid::parse_str(user.uuid.as_str()).unwrap())
.await;
match items {
Ok(i) => {
error!("Found {} items for user.", i.len());
Expand All @@ -50,8 +51,6 @@ pub(crate) async fn get_media(
error!("Failed to get media items!");
}
}
//tracing::error!("GET /media user={}", user);
// TODO: check auth header
// TODO: read list from persistency
// TODO: return list
Ok(Json(
Expand Down
2 changes: 2 additions & 0 deletions crates/media/src/api/routes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ pub(crate) mod patch_media_id;
pub(crate) mod post_albums;
pub(crate) mod post_media;
pub(crate) mod post_media_id;

pub(crate) mod photo_details;
3 changes: 3 additions & 0 deletions crates/media/src/api/routes/photo_details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

//! Returns the details of a given media item
//!
use axum::http::StatusCode;

#[allow(dead_code)]
pub(crate) async fn photo_details() -> std::result::Result<String, StatusCode> {
Err(StatusCode::NOT_IMPLEMENTED)
}
56 changes: 41 additions & 15 deletions crates/media/src/api/routes/post_media.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,32 @@
use axum::{
extract::{Multipart, State},
http::StatusCode,
Json,
};
use common::auth::user::User;
use hyper::header::LOCATION;
use hyper::HeaderMap;
use serde::{Deserialize, Serialize};
use time::format_description::well_known::Rfc3339;
use time::OffsetDateTime;
use common::auth::user::User;
use tracing::debug;
use uuid::Uuid;

use crate::{data::error::DataAccessError, repository::MediaRepositoryState};

#[derive(Serialize, Deserialize)]
pub struct ResponseId {
pub id: String,
}

pub(crate) async fn post_media(
State(repo): State<MediaRepositoryState>,
user: User,
mut multipart: Multipart,
) -> Result<String, StatusCode> {
) -> Result<(StatusCode, Json<ResponseId>), StatusCode> {
let mut name = None;
let mut date_taken = None;
let mut headers = HeaderMap::new();

while let Some(field) = multipart.next_field().await.unwrap() {
if let Some(field_name) = field.name() {
Expand All @@ -53,14 +63,16 @@ pub(crate) async fn post_media(

let date = OffsetDateTime::parse(date_taken.unwrap().as_str(), &Rfc3339);
if date.is_err() {
return Err(StatusCode::CREATED);
return Err(StatusCode::BAD_REQUEST);
}

let result = repo.create_media_item_for_user(
Uuid::parse_str(user.uuid.as_str()).unwrap(),
name.clone().unwrap(),
date.unwrap(),
).await;
let result = repo
.create_media_item_for_user(
Uuid::parse_str(user.uuid.as_str()).unwrap(),
name.clone().unwrap(),
date.unwrap(),
)
.await;

match result {
Ok(uuid) => {
Expand All @@ -71,12 +83,20 @@ pub(crate) async fn post_media(
uuid.clone().hyphenated().to_string()
);

Ok(uuid.hyphenated().to_string())
Ok((
StatusCode::OK,
Json(ResponseId {
id: uuid.hyphenated().to_string(),
}),
))
}
Err(error) => {
match error {
DataAccessError::AlreadyExist => {
DataAccessError::AlreadyExist(id) => {
// TODO: use Redirect::permanent to add a Location header to the already existing item
let location = format!("/media/{}", id);
headers.insert(LOCATION, location.parse().unwrap());

return Err(StatusCode::SEE_OTHER);
}
_ => {
Expand All @@ -102,10 +122,10 @@ mod tests {
use tower::ServiceExt;

use crate::api::router::MediaApi;
use std::io::Write;
use std::path::PathBuf;
use axum::http::header::CONTENT_TYPE;
use hyper::header::CONNECTION;
use std::io::Write;
use std::path::PathBuf;
use testdir::testdir;
use tokio::io::AsyncReadExt;

Expand Down Expand Up @@ -161,7 +181,7 @@ mod tests {
.header(CONNECTION, "Keep-Alive")
.header(
CONTENT_TYPE,
format!("multipart/form-data; boundary={}", BOUNDARY)
format!("multipart/form-data; boundary={}", BOUNDARY),
)
// .header(CONTENT_TYPE, &*format!("multipart/form-data; boundary={}", BOUNDARY))
.body(data.into())
Expand All @@ -184,7 +204,10 @@ mod tests {
write!(data, "\r\n")?;

write!(data, "--{}\r\n", BOUNDARY)?;
write!(data, "Content-Disposition: form-data; name=\"date_taken\";\r\n")?;
write!(
data,
"Content-Disposition: form-data; name=\"date_taken\";\r\n"
)?;
write!(data, "\r\n")?;
write!(data, "1985-04-12T23:20:50.52Z")?;
write!(data, "\r\n")?;
Expand All @@ -202,7 +225,10 @@ mod tests {

let mut data: Vec<u8> = Vec::new();
write!(data, "--{}\r\n", BOUNDARY)?;
write!(data, "Content-Disposition: form-data; name=\"DSC_1234\"; filename=\"11.jpg\"\r\n")?;
write!(
data,
"Content-Disposition: form-data; name=\"DSC_1234\"; filename=\"11.jpg\"\r\n"
)?;
write!(data, "Content-Type: image/jpeg\r\n")?;
write!(data, "\r\n")?;

Expand Down
Loading

0 comments on commit 8a41d74

Please sign in to comment.