Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add query strings #48

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ rsa = "0.9.2"
reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "stream", "multipart"] }

serde = "1.0.183"
serde_qs = "0.12.0"
serde_json = { version = "1.0.104", features = ["raw_value"] }
serde_with = "3.3.0"
serde_urlencoded = "0.7.1"
Expand Down
7 changes: 6 additions & 1 deletion crates/common/src/database/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@ pub trait Database {
async fn disable_user(&self, user_id: &str) -> Result<()>;
async fn enable_user(&self, user_id: &str) -> Result<()>;

async fn get_media_items(&self, user_id: &str) -> Result<Vec<MediaItem>>;
async fn get_media_items(
&self,
user_id: &str,
years: Vec<i32>,
months: Vec<i32>,
) -> Result<Vec<MediaItem>>;
async fn create_media_item(
&self,
user_id: &str,
Expand Down
7 changes: 6 additions & 1 deletion crates/database/src/postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,12 @@ impl Database for PostgresDatabase {
unimplemented!()
}

async fn get_media_items(&self, _user_id: &str) -> Result<Vec<MediaItem>> {
async fn get_media_items(
&self,
_user_id: &str,
_years: Vec<i32>,
_months: Vec<i32>,
) -> Result<Vec<MediaItem>> {
unimplemented!()
}

Expand Down
9 changes: 7 additions & 2 deletions crates/database/src/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,13 @@
unimplemented!()
}

async fn get_media_items(&self, _user_id: &str) -> Result<Vec<MediaItem>> {
unimplemented!()
async fn get_media_items(
&self,
_user_id: &str,
years: Vec<i32>,

Check warning on line 128 in crates/database/src/sqlite.rs

View workflow job for this annotation

GitHub Actions / Check

unused variable: `years`

Check failure on line 128 in crates/database/src/sqlite.rs

View workflow job for this annotation

GitHub Actions / Lint checks

unused variable: `years`

Check warning on line 128 in crates/database/src/sqlite.rs

View workflow job for this annotation

GitHub Actions / Tests

unused variable: `years`
months: Vec<i32>,

Check warning on line 129 in crates/database/src/sqlite.rs

View workflow job for this annotation

GitHub Actions / Check

unused variable: `months`

Check failure on line 129 in crates/database/src/sqlite.rs

View workflow job for this annotation

GitHub Actions / Lint checks

unused variable: `months`

Check warning on line 129 in crates/database/src/sqlite.rs

View workflow job for this annotation

GitHub Actions / Tests

unused variable: `months`
) -> Result<Vec<MediaItem>> {
Ok(vec![])
}
async fn create_media_item(
&self,
Expand Down
1 change: 1 addition & 0 deletions crates/media/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ hyper = { workspace = true, features = ["full"] }
tower-http.workspace = true
mime.workspace = true
bytes.workspace = true
serde_qs = { workspace = true, features = ["axum"] }

# persistency
uuid = { workspace = true, features = ["serde"] }
Expand Down
6 changes: 3 additions & 3 deletions crates/media/src/api/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
let mut mock_db = MockDatabase::new();
mock_db
.expect_get_media_items()
.return_once(|_| Ok(Vec::new()));

Check failure on line 110 in crates/media/src/api/router.rs

View workflow job for this annotation

GitHub Actions / Tests

closure is expected to take 3 arguments, but it takes 1 argument
let state: ApplicationState = ApplicationState {
config: Configuration::empty().into(),
plugins: HashMap::new(),
Expand All @@ -120,7 +120,7 @@
let response = app
.oneshot(
Request::builder()
.uri("/media?limit=100000&offset=1")
.uri("/media")
.method("GET")
.header("Authorization", "FakeAuth")
.body(Body::empty())
Expand All @@ -135,7 +135,7 @@
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
let body: String = serde_json::from_slice(&body).unwrap();

assert_eq!(body, "list media items. limit=100000, offset=1");
assert_eq!(body, "list media items. limit=, offset=");
}

#[tokio::test]
Expand All @@ -144,7 +144,7 @@
let mut mock_db = MockDatabase::new();
mock_db
.expect_get_media_items()
.return_once(|_| Ok(Vec::new()));

Check failure on line 147 in crates/media/src/api/router.rs

View workflow job for this annotation

GitHub Actions / Tests

closure is expected to take 3 arguments, but it takes 1 argument
let state: ApplicationState = ApplicationState {
config: Configuration::empty().into(),
plugins: HashMap::new(),
Expand Down Expand Up @@ -172,7 +172,7 @@
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
let body: String = serde_json::from_slice(&body).unwrap();

assert_eq!(body, "list media items. limit=1000, offset=0");
assert_eq!(body, "list media items. limit=, offset=");
}

#[tokio::test]
Expand Down
145 changes: 134 additions & 11 deletions crates/media/src/api/routes/get_media.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@
//! Returns a list of owned media items for current user
//!
use axum::extract::State;
use axum::{extract::Query, http::StatusCode, Json};
use axum::{http::StatusCode, Json};
use common::auth::user::User;
use serde::{Deserialize, Serialize};
use serde_qs::axum::QsQuery;
use std::result::Result;
use tracing::error;
use uuid::Uuid;
Expand All @@ -31,17 +32,21 @@ use crate::repository::MediaRepositoryState;

#[derive(Serialize, Deserialize)]
pub(crate) struct MediaListQuery {
offset: Option<i32>,
limit: Option<i32>,
years: Option<Vec<i32>>,
months: Option<Vec<i32>>,
}

pub(crate) async fn get_media(
State(repo): State<MediaRepositoryState>,
user: User,
Query(query): Query<MediaListQuery>,
QsQuery(query): QsQuery<MediaListQuery>, //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())
.get_media_items_for_user(
Uuid::parse_str(user.uuid.as_str()).unwrap(),
query.years.unwrap_or(vec![]),
query.months.unwrap_or(vec![]),
)
.await;
match items {
Ok(i) => {
Expand All @@ -51,15 +56,11 @@ pub(crate) async fn get_media(
error!("Failed to get media items!");
}
}

// TODO: read list from persistency
// TODO: return list
Ok(Json(
format!(
"list media items. limit={}, offset={}",
query.limit.unwrap_or(1000),
query.offset.unwrap_or(0)
)
.to_owned(),
format!("list media items. limit=, offset=").to_owned(),
))
}

Expand Down Expand Up @@ -105,4 +106,126 @@ mod tests {
// then
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}

#[sqlx::test]
async fn get_media_with_multiple_years_only_should_return_only_requested_years(
pool: SqlitePool,
) {
// given
let state: ApplicationState = ApplicationState {
config: Configuration::empty().into(),
plugins: HashMap::new(),
router: None,
database: Arc::new(SqliteDatabase { pool }),
};

let app = Router::new().nest("/", MediaApi::routes(&state).await);

// when
let response = app
.oneshot(
Request::builder()
.method("GET")
.header(hyper::header::AUTHORIZATION, "FakeAuth")
.uri(format!("/media?years=[1990,1991]"))
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();

// then
assert_eq!(response.status(), StatusCode::OK);
}

#[sqlx::test]
async fn get_media_with_multiple_years_and_months_should_return_requested_years_and_months(
pool: SqlitePool,
) {
// given
let state: ApplicationState = ApplicationState {
config: Configuration::empty().into(),
plugins: HashMap::new(),
router: None,
database: Arc::new(SqliteDatabase { pool }),
};

let app = Router::new().nest("/", MediaApi::routes(&state).await);

// when
let response = app
.oneshot(
Request::builder()
.method("GET")
.header(hyper::header::AUTHORIZATION, "FakeAuth")
.uri(format!("/media?years=[1990,1991]&months=[3,4]"))
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();

// then
assert_eq!(response.status(), StatusCode::OK);
}

#[sqlx::test]
async fn get_media_with_multiple_month_only_should_return_requested_months_for_all_years(
pool: SqlitePool,
) {
// given
let state: ApplicationState = ApplicationState {
config: Configuration::empty().into(),
plugins: HashMap::new(),
router: None,
database: Arc::new(SqliteDatabase { pool }),
};

let app = Router::new().nest("/", MediaApi::routes(&state).await);

// when
let response = app
.oneshot(
Request::builder()
.method("GET")
.header(hyper::header::AUTHORIZATION, "FakeAuth")
.uri(format!("/media?years=[1990,1991]"))
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();

// then
assert_eq!(response.status(), StatusCode::OK);
}

#[sqlx::test]
async fn get_media_without_parameters_should_return_all(pool: SqlitePool) {
// given
let state: ApplicationState = ApplicationState {
config: Configuration::empty().into(),
plugins: HashMap::new(),
router: None,
database: Arc::new(SqliteDatabase { pool }),
};

let app = Router::new().nest("/", MediaApi::routes(&state).await);

// when
let response = app
.oneshot(
Request::builder()
.method("GET")
.header(hyper::header::AUTHORIZATION, "FakeAuth")
.uri(format!("/media"))
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();

// then
assert_eq!(response.status(), StatusCode::OK);
}
}
13 changes: 10 additions & 3 deletions crates/media/src/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
async fn get_media_items_for_user(
&self,
user_id: Uuid,
years: Vec<i32>,
months: Vec<i32>,
) -> Result<Vec<MediaItem>, DataAccessError>;

/// Create a new media item for the given user
Expand Down Expand Up @@ -76,12 +78,17 @@
async fn get_media_items_for_user(
&self,
user_id: Uuid,
years: Vec<i32>,
months: Vec<i32>,
) -> Result<Vec<MediaItem>, DataAccessError> {
info!("get items for user {}", user_id);
info!(
"get items for user:{}, years: {:?}, month: {:?}",
user_id, years, months
);

let items_result = &self
.database
.get_media_items(user_id.hyphenated().to_string().as_str())
.get_media_items(user_id.hyphenated().to_string().as_str(), years, months)
.await;
return match items_result {
Ok(items) => {
Expand Down Expand Up @@ -202,13 +209,13 @@
let mut mock_db = MockDatabase::new();
mock_db
.expect_get_media_items()
.return_once(|_| Ok(Vec::new()));

Check failure on line 212 in crates/media/src/repository.rs

View workflow job for this annotation

GitHub Actions / Tests

closure is expected to take 3 arguments, but it takes 1 argument
let repository =
MediaRepository::new(Arc::new(mock_db), Configuration::empty().into()).await;

// when
let result = repository
.get_media_items_for_user(Uuid::parse_str(user_id).unwrap())
.get_media_items_for_user(Uuid::parse_str(user_id).unwrap(), Vec::new(), Vec::new())
.await;

// then
Expand Down
Loading