From 535de1e09c420ddb90f070c421b63710d0b5ec89 Mon Sep 17 00:00:00 2001 From: yinn_x Date: Mon, 18 Dec 2023 15:42:32 +0800 Subject: [PATCH 1/7] Add guard for delete mutation --- examples/sqlite/tests/guard_mutation_tests.rs | 14 ++++++++++++++ src/mutation/entity_delete_mutation.rs | 16 +++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/examples/sqlite/tests/guard_mutation_tests.rs b/examples/sqlite/tests/guard_mutation_tests.rs index ced4915a..e20d21f3 100644 --- a/examples/sqlite/tests/guard_mutation_tests.rs +++ b/examples/sqlite/tests/guard_mutation_tests.rs @@ -128,6 +128,20 @@ async fn entity_guard_mutation() { assert_eq!(response.errors.len(), 1); assert_eq!(response.errors[0].message, "Entity guard triggered."); + + let response = schema + .execute( + r#" + mutation FilmCategoryDelete { + filmCategoryDelete(filter: { filmId: { eq: 2 } }) + } +"#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Entity guard triggered."); } #[tokio::test] diff --git a/src/mutation/entity_delete_mutation.rs b/src/mutation/entity_delete_mutation.rs index fa1ed0cd..c666a010 100644 --- a/src/mutation/entity_delete_mutation.rs +++ b/src/mutation/entity_delete_mutation.rs @@ -5,7 +5,7 @@ use sea_orm::{ use crate::{ get_filter_conditions, BuilderContext, EntityObjectBuilder, EntityQueryFieldBuilder, - FilterInputBuilder, + FilterInputBuilder, GuardAction, }; /// The configuration structure of EntityDeleteMutationBuilder @@ -66,11 +66,25 @@ impl EntityDeleteMutationBuilder { let context = self.context; + let guard = self.context.guards.entity_guards.get(&object_name); + Field::new( self.type_name::(), TypeRef::named_nn(TypeRef::INT), move |ctx| { FieldFuture::new(async move { + let guard_flag = if let Some(guard) = guard { + (*guard)(&ctx) + } else { + GuardAction::Allow + }; + + if let GuardAction::Block(reason) = guard_flag { + return Err::, async_graphql::Error>(async_graphql::Error::new( + reason.unwrap_or("Entity guard triggered.".into()), + )); + } + let db = ctx.data::()?; let filters = ctx.args.get(&context.entity_delete_mutation.filter_field); From 3bb051cb235af58d941d04abc40ca59d7ac17a85 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 9 Dec 2024 14:00:42 +0800 Subject: [PATCH 2/7] Fixup --- src/outputs/connection_object.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/outputs/connection_object.rs b/src/outputs/connection_object.rs index 87b77e35..642b47a7 100644 --- a/src/outputs/connection_object.rs +++ b/src/outputs/connection_object.rs @@ -110,7 +110,7 @@ impl ConnectionObjectBuilder { if let Some(value) = connection .pagination_info .as_ref() - .map(FieldValue::borrowed_any) + .map(|pagination_info| FieldValue::borrowed_any(pagination_info)) { Ok(Some(value)) } else { From 751bf71a3da8fbe5de991fdef7fd5c3ffca95f62 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 9 Dec 2024 14:27:56 +0800 Subject: [PATCH 3/7] More tests --- examples/mysql/tests/guard_mutation_tests.rs | 166 ++++++++ examples/mysql/tests/guard_tests.rs | 383 ++++++++++++++++++ .../postgres/tests/guard_mutation_tests.rs | 166 ++++++++ examples/postgres/tests/guard_tests.rs | 383 ++++++++++++++++++ 4 files changed, 1098 insertions(+) create mode 100644 examples/mysql/tests/guard_mutation_tests.rs create mode 100644 examples/mysql/tests/guard_tests.rs create mode 100644 examples/postgres/tests/guard_mutation_tests.rs create mode 100644 examples/postgres/tests/guard_tests.rs diff --git a/examples/mysql/tests/guard_mutation_tests.rs b/examples/mysql/tests/guard_mutation_tests.rs new file mode 100644 index 00000000..d1408243 --- /dev/null +++ b/examples/mysql/tests/guard_mutation_tests.rs @@ -0,0 +1,166 @@ +use std::collections::BTreeMap; + +use async_graphql::{dynamic::*, Response}; +use sea_orm::{Database, DatabaseConnection}; +use seaography::{Builder, BuilderContext, FnGuard, GuardsConfig}; +use seaography_mysql_example::entities::*; + +lazy_static::lazy_static! { + static ref CONTEXT : BuilderContext = { + let context = BuilderContext::default(); + let mut entity_guards: BTreeMap = BTreeMap::new(); + entity_guards.insert("FilmCategory".into(), Box::new(|_ctx| { + seaography::GuardAction::Block(None) + })); + let mut field_guards: BTreeMap = BTreeMap::new(); + field_guards.insert("Language.name".into(), Box::new(|_ctx| { + seaography::GuardAction::Block(None) + })); + BuilderContext { + guards: GuardsConfig { + entity_guards, + field_guards, + }, + ..context + } + }; +} + +pub fn schema( + database: DatabaseConnection, + depth: Option, + complexity: Option, +) -> Result { + let mut builder = Builder::new(&CONTEXT, database.clone()); + seaography::register_entities!( + builder, + [ + actor, + address, + category, + city, + country, + customer, + film, + film_actor, + film_category, + film_text, + inventory, + language, + payment, + rental, + staff, + store, + ] + ); + let schema = builder.schema_builder(); + let schema = if let Some(depth) = depth { + schema.limit_depth(depth) + } else { + schema + }; + let schema = if let Some(complexity) = complexity { + schema.limit_complexity(complexity) + } else { + schema + }; + schema.data(database).finish() +} + +pub async fn get_schema() -> Schema { + let database = Database::connect("mysql://sea:sea@127.0.0.1/sakila").await.unwrap(); + let schema = schema(database, None, None).unwrap(); + + schema +} + +pub fn assert_eq(a: Response, b: &str) { + assert_eq!( + a.data.into_json().unwrap(), + serde_json::from_str::(b).unwrap() + ) +} + +#[tokio::test] +async fn entity_guard_mutation() { + let schema = get_schema().await; + + assert_eq( + schema + .execute( + r#" + mutation LanguageUpdate { + languageUpdate( + data: { lastUpdate: "2030-01-01 11:11:11 UTC" } + filter: { languageId: { eq: 6 } } + ) { + languageId + } + } + "#, + ) + .await, + r#" + { + "languageUpdate": [ + { + "languageId": 6 + } + ] + } + "#, + ); + + let response = schema + .execute( + r#" + mutation FilmCategoryUpdate { + filmCategoryUpdate( + data: { filmId: 1, categoryId: 1, lastUpdate: "2030-01-01 11:11:11 UTC" } + ) { + filmId + } + } +"#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Entity guard triggered."); + + let response = schema + .execute( + r#" + mutation FilmCategoryDelete { + filmCategoryDelete(filter: { filmId: { eq: 2 } }) + } +"#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Entity guard triggered."); +} + +#[tokio::test] +async fn field_guard_mutation() { + let schema = get_schema().await; + + let response = schema + .execute( + r#" + mutation LanguageUpdate { + languageUpdate(data: { name: "Cantonese" }, filter: { languageId: { eq: 6 } }) { + languageId + } + } + "#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Field guard triggered."); +} diff --git a/examples/mysql/tests/guard_tests.rs b/examples/mysql/tests/guard_tests.rs new file mode 100644 index 00000000..ec78483a --- /dev/null +++ b/examples/mysql/tests/guard_tests.rs @@ -0,0 +1,383 @@ +use std::collections::BTreeMap; + +use async_graphql::{dynamic::*, Response}; +use sea_orm::{Database, DatabaseConnection, RelationTrait}; +use seaography::{ + Builder, BuilderContext, EntityObjectRelationBuilder, EntityObjectViaRelationBuilder, FnGuard, + GuardsConfig, +}; + +lazy_static::lazy_static! { + static ref CONTEXT : BuilderContext = { + let context = BuilderContext::default(); + let mut entity_guards: BTreeMap = BTreeMap::new(); + entity_guards.insert("FilmCategory".into(), Box::new(|_ctx| { + seaography::GuardAction::Block(None) + })); + let mut field_guards: BTreeMap = BTreeMap::new(); + field_guards.insert("Language.lastUpdate".into(), Box::new(|_ctx| { + seaography::GuardAction::Block(None) + })); + BuilderContext { + guards: GuardsConfig { + entity_guards, + field_guards, + }, + ..context + } + }; +} + +pub fn schema( + database: DatabaseConnection, + depth: Option, + complexity: Option, +) -> Result { + let mut builder = Builder::new(&CONTEXT, database.clone()); + let entity_object_relation_builder = EntityObjectRelationBuilder { context: &CONTEXT }; + let entity_object_via_relation_builder = EntityObjectViaRelationBuilder { context: &CONTEXT }; + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "actor", + seaography_mysql_example::entities::film_actor::Relation::Actor.def(), + ), + entity_object_relation_builder + .get_relation::( + "film", + seaography_mysql_example::entities::film_actor::Relation::Film.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "customer", + seaography_mysql_example::entities::rental::Relation::Customer.def(), + ), + entity_object_relation_builder + .get_relation::( + "inventory", + seaography_mysql_example::entities::rental::Relation::Inventory.def(), + ), + entity_object_relation_builder + .get_relation::( + "payment", + seaography_mysql_example::entities::rental::Relation::Payment.def(), + ), + entity_object_relation_builder + .get_relation::( + "staff", + seaography_mysql_example::entities::rental::Relation::Staff.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_via_relation_builder + .get_relation::( + "film", + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "address", + seaography_mysql_example::entities::staff::Relation::Address.def(), + ), + entity_object_relation_builder + .get_relation::( + "payment", + seaography_mysql_example::entities::staff::Relation::Payment.def(), + ), + entity_object_relation_builder + .get_relation::( + "rental", + seaography_mysql_example::entities::staff::Relation::Rental.def(), + ), + entity_object_relation_builder + .get_relation::( + "selfRef", + seaography_mysql_example::entities::staff::Relation::SelfRef.def(), + ), + entity_object_relation_builder + .get_relation::( + "selfRefReverse", + seaography_mysql_example::entities::staff::Relation::SelfRef.def().rev(), + ), + entity_object_relation_builder + .get_relation::( + "store", + seaography_mysql_example::entities::staff::Relation::Store.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "city", + seaography_mysql_example::entities::country::Relation::City.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_via_relation_builder + .get_relation::("actor"), + entity_object_via_relation_builder + .get_relation::( + "category", + ), + entity_object_relation_builder + .get_relation::( + "inventory", + seaography_mysql_example::entities::film::Relation::Inventory.def(), + ), + entity_object_relation_builder + .get_relation::( + "language1", + seaography_mysql_example::entities::film::Relation::Language1.def(), + ), + entity_object_relation_builder + .get_relation::( + "language2", + seaography_mysql_example::entities::film::Relation::Language2.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_via_relation_builder + .get_relation::("film"), + ]); + builder.register_entity::(vec![]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "address", + seaography_mysql_example::entities::city::Relation::Address.def(), + ), + entity_object_relation_builder + .get_relation::( + "country", + seaography_mysql_example::entities::city::Relation::Country.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "film", + seaography_mysql_example::entities::inventory::Relation::Film.def(), + ), + entity_object_relation_builder + .get_relation::( + "rental", + seaography_mysql_example::entities::inventory::Relation::Rental.def(), + ), + entity_object_relation_builder + .get_relation::( + "store", + seaography_mysql_example::entities::inventory::Relation::Store.def(), + ), + ]); + builder.register_entity::(vec![]); + builder . register_entity :: < seaography_mysql_example:: entities :: film_category :: Entity > (vec ! [entity_object_relation_builder . get_relation :: < seaography_mysql_example:: entities :: film_category :: Entity , seaography_mysql_example:: entities :: category :: Entity > ("category" , seaography_mysql_example:: entities :: film_category :: Relation :: Category . def ()) , entity_object_relation_builder . get_relation :: < seaography_mysql_example:: entities :: film_category :: Entity , seaography_mysql_example:: entities :: film :: Entity > ("film" , seaography_mysql_example:: entities :: film_category :: Relation :: Film . def ())]) ; + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "address", + seaography_mysql_example::entities::customer::Relation::Address.def(), + ), + entity_object_relation_builder + .get_relation::( + "payment", + seaography_mysql_example::entities::customer::Relation::Payment.def(), + ), + entity_object_relation_builder + .get_relation::( + "rental", + seaography_mysql_example::entities::customer::Relation::Rental.def(), + ), + entity_object_relation_builder + .get_relation::( + "store", + seaography_mysql_example::entities::customer::Relation::Store.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "address", + seaography_mysql_example::entities::store::Relation::Address.def(), + ), + entity_object_relation_builder + .get_relation::( + "customer", + seaography_mysql_example::entities::store::Relation::Customer.def(), + ), + entity_object_relation_builder + .get_relation::( + "inventory", + seaography_mysql_example::entities::store::Relation::Inventory.def(), + ), + entity_object_relation_builder + .get_relation::( + "staff", + seaography_mysql_example::entities::store::Relation::Staff.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "customer", + seaography_mysql_example::entities::payment::Relation::Customer.def(), + ), + entity_object_relation_builder + .get_relation::( + "rental", + seaography_mysql_example::entities::payment::Relation::Rental.def(), + ), + entity_object_relation_builder + .get_relation::( + "staff", + seaography_mysql_example::entities::payment::Relation::Staff.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "city", + seaography_mysql_example::entities::address::Relation::City.def(), + ), + entity_object_relation_builder + .get_relation::( + "customer", + seaography_mysql_example::entities::address::Relation::Customer.def(), + ), + entity_object_relation_builder + .get_relation::( + "staff", + seaography_mysql_example::entities::address::Relation::Staff.def(), + ), + entity_object_relation_builder + .get_relation::( + "store", + seaography_mysql_example::entities::address::Relation::Store.def(), + ), + ]); + let schema = builder.schema_builder(); + let schema = if let Some(depth) = depth { + schema.limit_depth(depth) + } else { + schema + }; + let schema = if let Some(complexity) = complexity { + schema.limit_complexity(complexity) + } else { + schema + }; + schema.data(database).finish() +} + +pub async fn get_schema() -> Schema { + let database = Database::connect("mysql://sea:sea@127.0.0.1/sakila").await.unwrap(); + let schema = schema(database, None, None).unwrap(); + + schema +} + +pub fn assert_eq(a: Response, b: &str) { + assert_eq!( + a.data.into_json().unwrap(), + serde_json::from_str::(b).unwrap() + ) +} + +#[tokio::test] +async fn entity_guard() { + let schema = get_schema().await; + + assert_eq( + schema + .execute( + r#" + { + language { + nodes { + languageId + name + } + } + } + "#, + ) + .await, + r#" + { + "language": { + "nodes": [ + { + "languageId": 1, + "name": "English" + }, + { + "languageId": 2, + "name": "Italian" + }, + { + "languageId": 3, + "name": "Japanese" + }, + { + "languageId": 4, + "name": "Mandarin" + }, + { + "languageId": 5, + "name": "French" + }, + { + "languageId": 6, + "name": "German" + } + ] + } + } + "#, + ); + + let response = schema + .execute( + r#" + { + filmCategory { + nodes { + filmId + } + } + } + "#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Entity guard triggered."); +} + +#[tokio::test] +async fn field_guard() { + let schema = get_schema().await; + + let response = schema + .execute( + r#" + { + language { + nodes { + languageId + name + lastUpdate + } + } + } + "#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Field guard triggered."); +} diff --git a/examples/postgres/tests/guard_mutation_tests.rs b/examples/postgres/tests/guard_mutation_tests.rs new file mode 100644 index 00000000..858156a5 --- /dev/null +++ b/examples/postgres/tests/guard_mutation_tests.rs @@ -0,0 +1,166 @@ +use std::collections::BTreeMap; + +use async_graphql::{dynamic::*, Response}; +use sea_orm::{Database, DatabaseConnection}; +use seaography::{Builder, BuilderContext, FnGuard, GuardsConfig}; +use seaography_postgres_example::entities::*; + +lazy_static::lazy_static! { + static ref CONTEXT : BuilderContext = { + let context = BuilderContext::default(); + let mut entity_guards: BTreeMap = BTreeMap::new(); + entity_guards.insert("FilmCategory".into(), Box::new(|_ctx| { + seaography::GuardAction::Block(None) + })); + let mut field_guards: BTreeMap = BTreeMap::new(); + field_guards.insert("Language.name".into(), Box::new(|_ctx| { + seaography::GuardAction::Block(None) + })); + BuilderContext { + guards: GuardsConfig { + entity_guards, + field_guards, + }, + ..context + } + }; +} + +pub fn schema( + database: DatabaseConnection, + depth: Option, + complexity: Option, +) -> Result { + let mut builder = Builder::new(&CONTEXT, database.clone()); + seaography::register_entities!( + builder, + [ + actor, + address, + category, + city, + country, + customer, + film, + film_actor, + film_category, + film_text, + inventory, + language, + payment, + rental, + staff, + store, + ] + ); + let schema = builder.schema_builder(); + let schema = if let Some(depth) = depth { + schema.limit_depth(depth) + } else { + schema + }; + let schema = if let Some(complexity) = complexity { + schema.limit_complexity(complexity) + } else { + schema + }; + schema.data(database).finish() +} + +pub async fn get_schema() -> Schema { + let database = Database::connect("postgres://sea:sea@127.0.0.1/sakila").await.unwrap(); + let schema = schema(database, None, None).unwrap(); + + schema +} + +pub fn assert_eq(a: Response, b: &str) { + assert_eq!( + a.data.into_json().unwrap(), + serde_json::from_str::(b).unwrap() + ) +} + +#[tokio::test] +async fn entity_guard_mutation() { + let schema = get_schema().await; + + assert_eq( + schema + .execute( + r#" + mutation LanguageUpdate { + languageUpdate( + data: { lastUpdate: "2030-01-01 11:11:11 UTC" } + filter: { languageId: { eq: 6 } } + ) { + languageId + } + } + "#, + ) + .await, + r#" + { + "languageUpdate": [ + { + "languageId": 6 + } + ] + } + "#, + ); + + let response = schema + .execute( + r#" + mutation FilmCategoryUpdate { + filmCategoryUpdate( + data: { filmId: 1, categoryId: 1, lastUpdate: "2030-01-01 11:11:11 UTC" } + ) { + filmId + } + } +"#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Entity guard triggered."); + + let response = schema + .execute( + r#" + mutation FilmCategoryDelete { + filmCategoryDelete(filter: { filmId: { eq: 2 } }) + } +"#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Entity guard triggered."); +} + +#[tokio::test] +async fn field_guard_mutation() { + let schema = get_schema().await; + + let response = schema + .execute( + r#" + mutation LanguageUpdate { + languageUpdate(data: { name: "Cantonese" }, filter: { languageId: { eq: 6 } }) { + languageId + } + } + "#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Field guard triggered."); +} diff --git a/examples/postgres/tests/guard_tests.rs b/examples/postgres/tests/guard_tests.rs new file mode 100644 index 00000000..a8e92387 --- /dev/null +++ b/examples/postgres/tests/guard_tests.rs @@ -0,0 +1,383 @@ +use std::collections::BTreeMap; + +use async_graphql::{dynamic::*, Response}; +use sea_orm::{Database, DatabaseConnection, RelationTrait}; +use seaography::{ + Builder, BuilderContext, EntityObjectRelationBuilder, EntityObjectViaRelationBuilder, FnGuard, + GuardsConfig, +}; + +lazy_static::lazy_static! { + static ref CONTEXT : BuilderContext = { + let context = BuilderContext::default(); + let mut entity_guards: BTreeMap = BTreeMap::new(); + entity_guards.insert("FilmCategory".into(), Box::new(|_ctx| { + seaography::GuardAction::Block(None) + })); + let mut field_guards: BTreeMap = BTreeMap::new(); + field_guards.insert("Language.lastUpdate".into(), Box::new(|_ctx| { + seaography::GuardAction::Block(None) + })); + BuilderContext { + guards: GuardsConfig { + entity_guards, + field_guards, + }, + ..context + } + }; +} + +pub fn schema( + database: DatabaseConnection, + depth: Option, + complexity: Option, +) -> Result { + let mut builder = Builder::new(&CONTEXT, database.clone()); + let entity_object_relation_builder = EntityObjectRelationBuilder { context: &CONTEXT }; + let entity_object_via_relation_builder = EntityObjectViaRelationBuilder { context: &CONTEXT }; + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "actor", + seaography_postgres_example::entities::film_actor::Relation::Actor.def(), + ), + entity_object_relation_builder + .get_relation::( + "film", + seaography_postgres_example::entities::film_actor::Relation::Film.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "customer", + seaography_postgres_example::entities::rental::Relation::Customer.def(), + ), + entity_object_relation_builder + .get_relation::( + "inventory", + seaography_postgres_example::entities::rental::Relation::Inventory.def(), + ), + entity_object_relation_builder + .get_relation::( + "payment", + seaography_postgres_example::entities::rental::Relation::Payment.def(), + ), + entity_object_relation_builder + .get_relation::( + "staff", + seaography_postgres_example::entities::rental::Relation::Staff.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_via_relation_builder + .get_relation::( + "film", + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "address", + seaography_postgres_example::entities::staff::Relation::Address.def(), + ), + entity_object_relation_builder + .get_relation::( + "payment", + seaography_postgres_example::entities::staff::Relation::Payment.def(), + ), + entity_object_relation_builder + .get_relation::( + "rental", + seaography_postgres_example::entities::staff::Relation::Rental.def(), + ), + entity_object_relation_builder + .get_relation::( + "selfRef", + seaography_postgres_example::entities::staff::Relation::SelfRef.def(), + ), + entity_object_relation_builder + .get_relation::( + "selfRefReverse", + seaography_postgres_example::entities::staff::Relation::SelfRef.def().rev(), + ), + entity_object_relation_builder + .get_relation::( + "store", + seaography_postgres_example::entities::staff::Relation::Store.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "city", + seaography_postgres_example::entities::country::Relation::City.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_via_relation_builder + .get_relation::("actor"), + entity_object_via_relation_builder + .get_relation::( + "category", + ), + entity_object_relation_builder + .get_relation::( + "inventory", + seaography_postgres_example::entities::film::Relation::Inventory.def(), + ), + entity_object_relation_builder + .get_relation::( + "language1", + seaography_postgres_example::entities::film::Relation::Language1.def(), + ), + entity_object_relation_builder + .get_relation::( + "language2", + seaography_postgres_example::entities::film::Relation::Language2.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_via_relation_builder + .get_relation::("film"), + ]); + builder.register_entity::(vec![]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "address", + seaography_postgres_example::entities::city::Relation::Address.def(), + ), + entity_object_relation_builder + .get_relation::( + "country", + seaography_postgres_example::entities::city::Relation::Country.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "film", + seaography_postgres_example::entities::inventory::Relation::Film.def(), + ), + entity_object_relation_builder + .get_relation::( + "rental", + seaography_postgres_example::entities::inventory::Relation::Rental.def(), + ), + entity_object_relation_builder + .get_relation::( + "store", + seaography_postgres_example::entities::inventory::Relation::Store.def(), + ), + ]); + builder.register_entity::(vec![]); + builder . register_entity :: < seaography_postgres_example:: entities :: film_category :: Entity > (vec ! [entity_object_relation_builder . get_relation :: < seaography_postgres_example:: entities :: film_category :: Entity , seaography_postgres_example:: entities :: category :: Entity > ("category" , seaography_postgres_example:: entities :: film_category :: Relation :: Category . def ()) , entity_object_relation_builder . get_relation :: < seaography_postgres_example:: entities :: film_category :: Entity , seaography_postgres_example:: entities :: film :: Entity > ("film" , seaography_postgres_example:: entities :: film_category :: Relation :: Film . def ())]) ; + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "address", + seaography_postgres_example::entities::customer::Relation::Address.def(), + ), + entity_object_relation_builder + .get_relation::( + "payment", + seaography_postgres_example::entities::customer::Relation::Payment.def(), + ), + entity_object_relation_builder + .get_relation::( + "rental", + seaography_postgres_example::entities::customer::Relation::Rental.def(), + ), + entity_object_relation_builder + .get_relation::( + "store", + seaography_postgres_example::entities::customer::Relation::Store.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "address", + seaography_postgres_example::entities::store::Relation::Address.def(), + ), + entity_object_relation_builder + .get_relation::( + "customer", + seaography_postgres_example::entities::store::Relation::Customer.def(), + ), + entity_object_relation_builder + .get_relation::( + "inventory", + seaography_postgres_example::entities::store::Relation::Inventory.def(), + ), + entity_object_relation_builder + .get_relation::( + "staff", + seaography_postgres_example::entities::store::Relation::Staff.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "customer", + seaography_postgres_example::entities::payment::Relation::Customer.def(), + ), + entity_object_relation_builder + .get_relation::( + "rental", + seaography_postgres_example::entities::payment::Relation::Rental.def(), + ), + entity_object_relation_builder + .get_relation::( + "staff", + seaography_postgres_example::entities::payment::Relation::Staff.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "city", + seaography_postgres_example::entities::address::Relation::City.def(), + ), + entity_object_relation_builder + .get_relation::( + "customer", + seaography_postgres_example::entities::address::Relation::Customer.def(), + ), + entity_object_relation_builder + .get_relation::( + "staff", + seaography_postgres_example::entities::address::Relation::Staff.def(), + ), + entity_object_relation_builder + .get_relation::( + "store", + seaography_postgres_example::entities::address::Relation::Store.def(), + ), + ]); + let schema = builder.schema_builder(); + let schema = if let Some(depth) = depth { + schema.limit_depth(depth) + } else { + schema + }; + let schema = if let Some(complexity) = complexity { + schema.limit_complexity(complexity) + } else { + schema + }; + schema.data(database).finish() +} + +pub async fn get_schema() -> Schema { + let database = Database::connect("postgres://sea:sea@127.0.0.1/sakila").await.unwrap(); + let schema = schema(database, None, None).unwrap(); + + schema +} + +pub fn assert_eq(a: Response, b: &str) { + assert_eq!( + a.data.into_json().unwrap(), + serde_json::from_str::(b).unwrap() + ) +} + +#[tokio::test] +async fn entity_guard() { + let schema = get_schema().await; + + assert_eq( + schema + .execute( + r#" + { + language { + nodes { + languageId + name + } + } + } + "#, + ) + .await, + r#" + { + "language": { + "nodes": [ + { + "languageId": 1, + "name": "English" + }, + { + "languageId": 2, + "name": "Italian" + }, + { + "languageId": 3, + "name": "Japanese" + }, + { + "languageId": 4, + "name": "Mandarin" + }, + { + "languageId": 5, + "name": "French" + }, + { + "languageId": 6, + "name": "German" + } + ] + } + } + "#, + ); + + let response = schema + .execute( + r#" + { + filmCategory { + nodes { + filmId + } + } + } + "#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Entity guard triggered."); +} + +#[tokio::test] +async fn field_guard() { + let schema = get_schema().await; + + let response = schema + .execute( + r#" + { + language { + nodes { + languageId + name + lastUpdate + } + } + } + "#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Field guard triggered."); +} From 9231935b356071ecc3a6fa49c96b566532381d7a Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 9 Dec 2024 14:59:10 +0800 Subject: [PATCH 4/7] More tests --- examples/mysql/tests/guard_mutation_tests.rs | 5 ++- examples/mysql/tests/guard_tests.rs | 6 +++- .../postgres/tests/guard_mutation_tests.rs | 6 ++-- examples/postgres/tests/guard_tests.rs | 16 +++------- examples/postgres/tests/mutation_tests.rs | 32 +++++++++---------- 5 files changed, 32 insertions(+), 33 deletions(-) diff --git a/examples/mysql/tests/guard_mutation_tests.rs b/examples/mysql/tests/guard_mutation_tests.rs index d1408243..65ca6e71 100644 --- a/examples/mysql/tests/guard_mutation_tests.rs +++ b/examples/mysql/tests/guard_mutation_tests.rs @@ -53,6 +53,7 @@ pub fn schema( store, ] ); + builder.register_enumeration::(); let schema = builder.schema_builder(); let schema = if let Some(depth) = depth { schema.limit_depth(depth) @@ -68,7 +69,9 @@ pub fn schema( } pub async fn get_schema() -> Schema { - let database = Database::connect("mysql://sea:sea@127.0.0.1/sakila").await.unwrap(); + let database = Database::connect("mysql://sea:sea@127.0.0.1/sakila") + .await + .unwrap(); let schema = schema(database, None, None).unwrap(); schema diff --git a/examples/mysql/tests/guard_tests.rs b/examples/mysql/tests/guard_tests.rs index ec78483a..3bfa6b0c 100644 --- a/examples/mysql/tests/guard_tests.rs +++ b/examples/mysql/tests/guard_tests.rs @@ -257,6 +257,8 @@ pub fn schema( seaography_mysql_example::entities::address::Relation::Store.def(), ), ]); + builder + .register_enumeration::(); let schema = builder.schema_builder(); let schema = if let Some(depth) = depth { schema.limit_depth(depth) @@ -272,7 +274,9 @@ pub fn schema( } pub async fn get_schema() -> Schema { - let database = Database::connect("mysql://sea:sea@127.0.0.1/sakila").await.unwrap(); + let database = Database::connect("mysql://sea:sea@127.0.0.1/sakila") + .await + .unwrap(); let schema = schema(database, None, None).unwrap(); schema diff --git a/examples/postgres/tests/guard_mutation_tests.rs b/examples/postgres/tests/guard_mutation_tests.rs index 858156a5..437972b0 100644 --- a/examples/postgres/tests/guard_mutation_tests.rs +++ b/examples/postgres/tests/guard_mutation_tests.rs @@ -44,7 +44,6 @@ pub fn schema( film, film_actor, film_category, - film_text, inventory, language, payment, @@ -53,6 +52,7 @@ pub fn schema( store, ] ); + builder.register_enumeration::(); let schema = builder.schema_builder(); let schema = if let Some(depth) = depth { schema.limit_depth(depth) @@ -91,7 +91,7 @@ async fn entity_guard_mutation() { r#" mutation LanguageUpdate { languageUpdate( - data: { lastUpdate: "2030-01-01 11:11:11 UTC" } + data: { lastUpdate: "2030-01-01 11:11:11" } filter: { languageId: { eq: 6 } } ) { languageId @@ -116,7 +116,7 @@ async fn entity_guard_mutation() { r#" mutation FilmCategoryUpdate { filmCategoryUpdate( - data: { filmId: 1, categoryId: 1, lastUpdate: "2030-01-01 11:11:11 UTC" } + data: { filmId: 1, categoryId: 1, lastUpdate: "2030-01-01 11:11:11" } ) { filmId } diff --git a/examples/postgres/tests/guard_tests.rs b/examples/postgres/tests/guard_tests.rs index a8e92387..05675c5e 100644 --- a/examples/postgres/tests/guard_tests.rs +++ b/examples/postgres/tests/guard_tests.rs @@ -92,16 +92,6 @@ pub fn schema( "rental", seaography_postgres_example::entities::staff::Relation::Rental.def(), ), - entity_object_relation_builder - .get_relation::( - "selfRef", - seaography_postgres_example::entities::staff::Relation::SelfRef.def(), - ), - entity_object_relation_builder - .get_relation::( - "selfRefReverse", - seaography_postgres_example::entities::staff::Relation::SelfRef.def().rev(), - ), entity_object_relation_builder .get_relation::( "store", @@ -172,7 +162,6 @@ pub fn schema( seaography_postgres_example::entities::inventory::Relation::Store.def(), ), ]); - builder.register_entity::(vec![]); builder . register_entity :: < seaography_postgres_example:: entities :: film_category :: Entity > (vec ! [entity_object_relation_builder . get_relation :: < seaography_postgres_example:: entities :: film_category :: Entity , seaography_postgres_example:: entities :: category :: Entity > ("category" , seaography_postgres_example:: entities :: film_category :: Relation :: Category . def ()) , entity_object_relation_builder . get_relation :: < seaography_postgres_example:: entities :: film_category :: Entity , seaography_postgres_example:: entities :: film :: Entity > ("film" , seaography_postgres_example:: entities :: film_category :: Relation :: Film . def ())]) ; builder.register_entity::(vec![ entity_object_relation_builder @@ -257,6 +246,7 @@ pub fn schema( seaography_postgres_example::entities::address::Relation::Store.def(), ), ]); + builder.register_enumeration::(); let schema = builder.schema_builder(); let schema = if let Some(depth) = depth { schema.limit_depth(depth) @@ -272,7 +262,9 @@ pub fn schema( } pub async fn get_schema() -> Schema { - let database = Database::connect("postgres://sea:sea@127.0.0.1/sakila").await.unwrap(); + let database = Database::connect("postgres://sea:sea@127.0.0.1/sakila") + .await + .unwrap(); let schema = schema(database, None, None).unwrap(); schema diff --git a/examples/postgres/tests/mutation_tests.rs b/examples/postgres/tests/mutation_tests.rs index 1fa66e91..7654d3af 100644 --- a/examples/postgres/tests/mutation_tests.rs +++ b/examples/postgres/tests/mutation_tests.rs @@ -238,27 +238,27 @@ async fn test_create_batch_mutation() { "nodes": [ { "languageId": 1, - "name": "English " + "name": "English" }, { "languageId": 2, - "name": "Italian " + "name": "Italian" }, { "languageId": 3, - "name": "Japanese " + "name": "Japanese" }, { "languageId": 4, - "name": "Mandarin " + "name": "Mandarin" }, { "languageId": 5, - "name": "French " + "name": "French" }, { "languageId": 6, - "name": "German " + "name": "German" } ] } @@ -287,10 +287,10 @@ async fn test_create_batch_mutation() { { "languageCreateBatch": [ { - "name": "Swedish " + "name": "Swedish" }, { - "name": "Danish " + "name": "Danish" } ] } @@ -317,28 +317,28 @@ async fn test_create_batch_mutation() { "language": { "nodes": [ { - "name": "English " + "name": "English" }, { - "name": "Italian " + "name": "Italian" }, { - "name": "Japanese " + "name": "Japanese" }, { - "name": "Mandarin " + "name": "Mandarin" }, { - "name": "French " + "name": "French" }, { - "name": "German " + "name": "German" }, { - "name": "Swedish " + "name": "Swedish" }, { - "name": "Danish " + "name": "Danish" } ] } From 33a4152ec1f78c2e9f1a58c7649794d581d48c2e Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 9 Dec 2024 15:02:38 +0800 Subject: [PATCH 5/7] fmt --- examples/postgres/tests/guard_mutation_tests.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/postgres/tests/guard_mutation_tests.rs b/examples/postgres/tests/guard_mutation_tests.rs index 437972b0..d533ed00 100644 --- a/examples/postgres/tests/guard_mutation_tests.rs +++ b/examples/postgres/tests/guard_mutation_tests.rs @@ -68,7 +68,9 @@ pub fn schema( } pub async fn get_schema() -> Schema { - let database = Database::connect("postgres://sea:sea@127.0.0.1/sakila").await.unwrap(); + let database = Database::connect("postgres://sea:sea@127.0.0.1/sakila") + .await + .unwrap(); let schema = schema(database, None, None).unwrap(); schema From 722e0874f882e78c7d71cc7c663772f31d496929 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 9 Dec 2024 15:17:41 +0800 Subject: [PATCH 6/7] Fix tests --- examples/postgres/tests/guard_tests.rs | 22 +++++++++++----- examples/postgres/tests/mutation_tests.rs | 32 +++++++++++------------ 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/examples/postgres/tests/guard_tests.rs b/examples/postgres/tests/guard_tests.rs index 05675c5e..f75e98e7 100644 --- a/examples/postgres/tests/guard_tests.rs +++ b/examples/postgres/tests/guard_tests.rs @@ -286,7 +286,7 @@ async fn entity_guard() { .execute( r#" { - language { + language(orderBy: { languageId: ASC }) { nodes { languageId name @@ -302,27 +302,35 @@ async fn entity_guard() { "nodes": [ { "languageId": 1, - "name": "English" + "name": "English " }, { "languageId": 2, - "name": "Italian" + "name": "Italian " }, { "languageId": 3, - "name": "Japanese" + "name": "Japanese " }, { "languageId": 4, - "name": "Mandarin" + "name": "Mandarin " }, { "languageId": 5, - "name": "French" + "name": "French " }, { "languageId": 6, - "name": "German" + "name": "German " + }, + { + "languageId": 7, + "name": "Swedish " + }, + { + "languageId": 8, + "name": "Danish " } ] } diff --git a/examples/postgres/tests/mutation_tests.rs b/examples/postgres/tests/mutation_tests.rs index 7654d3af..1fa66e91 100644 --- a/examples/postgres/tests/mutation_tests.rs +++ b/examples/postgres/tests/mutation_tests.rs @@ -238,27 +238,27 @@ async fn test_create_batch_mutation() { "nodes": [ { "languageId": 1, - "name": "English" + "name": "English " }, { "languageId": 2, - "name": "Italian" + "name": "Italian " }, { "languageId": 3, - "name": "Japanese" + "name": "Japanese " }, { "languageId": 4, - "name": "Mandarin" + "name": "Mandarin " }, { "languageId": 5, - "name": "French" + "name": "French " }, { "languageId": 6, - "name": "German" + "name": "German " } ] } @@ -287,10 +287,10 @@ async fn test_create_batch_mutation() { { "languageCreateBatch": [ { - "name": "Swedish" + "name": "Swedish " }, { - "name": "Danish" + "name": "Danish " } ] } @@ -317,28 +317,28 @@ async fn test_create_batch_mutation() { "language": { "nodes": [ { - "name": "English" + "name": "English " }, { - "name": "Italian" + "name": "Italian " }, { - "name": "Japanese" + "name": "Japanese " }, { - "name": "Mandarin" + "name": "Mandarin " }, { - "name": "French" + "name": "French " }, { - "name": "German" + "name": "German " }, { - "name": "Swedish" + "name": "Swedish " }, { - "name": "Danish" + "name": "Danish " } ] } From 50e6812be82f76f115ff843820e60d0939f344cd Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Mon, 9 Dec 2024 15:22:40 +0800 Subject: [PATCH 7/7] Fix tests --- examples/postgres/tests/guard_tests.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/examples/postgres/tests/guard_tests.rs b/examples/postgres/tests/guard_tests.rs index f75e98e7..45ba63e4 100644 --- a/examples/postgres/tests/guard_tests.rs +++ b/examples/postgres/tests/guard_tests.rs @@ -323,14 +323,6 @@ async fn entity_guard() { { "languageId": 6, "name": "German " - }, - { - "languageId": 7, - "name": "Swedish " - }, - { - "languageId": 8, - "name": "Danish " } ] }