>(
+ id: I,
+ parent_id: Option,
+ ) -> project::Model {
+ project::Model {
+ id: id.as_ref().to_string(),
+ name: "project_name".into(),
+ extra: None,
+ description: Some("description".into()),
+ enabled: Some(true),
+ domain_id: "domain_id".into(),
+ parent_id: parent_id.as_ref().map(|val| val.as_ref().into()),
+ is_domain: parent_id.as_ref().is_some(),
+ }
+ }
+
+ #[tokio::test]
+ async fn test_get_project_parents() {
+ // Create MockDatabase with mock query results
+ let db = MockDatabase::new(DatabaseBackend::Postgres)
+ .append_query_results([vec![get_mock("3", Some("2"))]])
+ .append_query_results([vec![get_mock("2", Some("1"))]])
+ .append_query_results([vec![get_mock("1", Some("domain_id"))]])
+ .append_query_results([vec![get_mock("domain_id", None::<&str>)]])
+ .into_connection();
+ assert_eq!(
+ get_project_parents(&db, "3").await.unwrap().unwrap(),
+ vec![
+ Project {
+ id: "2".into(),
+ parent_id: Some("1".into()),
+ name: "project_name".into(),
+ domain_id: "domain_id".into(),
+ enabled: true,
+ description: Some("description".into()),
+ extra: None
+ },
+ Project {
+ id: "1".into(),
+ parent_id: Some("domain_id".into()),
+ name: "project_name".into(),
+ domain_id: "domain_id".into(),
+ enabled: true,
+ description: Some("description".into()),
+ extra: None
+ },
+ Project {
+ id: "domain_id".into(),
+ parent_id: None,
+ name: "project_name".into(),
+ domain_id: "domain_id".into(),
+ enabled: true,
+ description: Some("description".into()),
+ extra: None
+ }
+ ]
+ );
+ // Checking transaction log
+ assert_eq!(
+ db.into_transaction_log(),
+ [
+ Transaction::from_sql_and_values(
+ DatabaseBackend::Postgres,
+ r#"SELECT "project"."id", "project"."name", "project"."extra", "project"."description", "project"."enabled", "project"."domain_id", "project"."parent_id", "project"."is_domain" FROM "project" WHERE "project"."id" = $1 LIMIT $2"#,
+ ["3".into(), 1u64.into()]
+ ),
+ Transaction::from_sql_and_values(
+ DatabaseBackend::Postgres,
+ r#"SELECT "project"."id", "project"."name", "project"."extra", "project"."description", "project"."enabled", "project"."domain_id", "project"."parent_id", "project"."is_domain" FROM "project" WHERE "project"."id" = $1 LIMIT $2"#,
+ ["2".into(), 1u64.into()]
+ ),
+ Transaction::from_sql_and_values(
+ DatabaseBackend::Postgres,
+ r#"SELECT "project"."id", "project"."name", "project"."extra", "project"."description", "project"."enabled", "project"."domain_id", "project"."parent_id", "project"."is_domain" FROM "project" WHERE "project"."id" = $1 LIMIT $2"#,
+ ["1".into(), 1u64.into()]
+ ),
+ Transaction::from_sql_and_values(
+ DatabaseBackend::Postgres,
+ r#"SELECT "project"."id", "project"."name", "project"."extra", "project"."description", "project"."enabled", "project"."domain_id", "project"."parent_id", "project"."is_domain" FROM "project" WHERE "project"."id" = $1 LIMIT $2"#,
+ ["domain_id".into(), 1u64.into()]
+ ),
+ ]
+ );
+ }
+}
diff --git a/src/resource/error.rs b/src/resource/error.rs
index ee6458f5..c5473558 100644
--- a/src/resource/error.rs
+++ b/src/resource/error.rs
@@ -14,7 +14,7 @@
use thiserror::Error;
-use crate::resource::backends::error::*;
+use crate::resource::backend::error::*;
use crate::resource::types::DomainBuilderError;
#[derive(Error, Debug)]
diff --git a/src/resource/mod.rs b/src/resource/mod.rs
index 75419277..47e0ed1f 100644
--- a/src/resource/mod.rs
+++ b/src/resource/mod.rs
@@ -16,14 +16,14 @@ use async_trait::async_trait;
#[cfg(test)]
use mockall::mock;
-pub mod backends;
+pub mod backend;
pub mod error;
pub(crate) mod types;
use crate::config::Config;
use crate::keystone::ServiceState;
use crate::plugin_manager::PluginManager;
-use crate::resource::backends::sql::SqlBackend;
+use crate::resource::backend::sql::SqlBackend;
use crate::resource::error::ResourceProviderError;
use crate::resource::types::{Domain, Project, ResourceBackend};
@@ -58,6 +58,13 @@ pub trait ResourceApi: Send + Sync + Clone {
name: &'a str,
domain_id: &'a str,
) -> Result