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

Fix SelfRef relations, add reverse SelfRef #99

Merged
merged 7 commits into from
Nov 14, 2022
Merged
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
106 changes: 67 additions & 39 deletions derive/src/relation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ pub struct SeaOrm {
}

pub fn compact_relation_fn(item: &syn::DataEnum) -> Result<TokenStream, crate::error::Error> {
let (loaders, functions): (Vec<_>, Vec<_>) = item
let relations_parameters: Vec<(String, Option<String>, Option<String>, bool)> = item
.variants
.iter()
.map(
|variant| -> Result<(TokenStream, TokenStream), crate::error::Error> {
|variant| -> Result<(String, Option<String>, Option<String>, bool), crate::error::Error> {
let attrs = SeaOrm::from_attributes(&variant.attrs)?;

let belongs_to = match attrs.belongs_to {
Expand All @@ -30,22 +30,12 @@ pub fn compact_relation_fn(item: &syn::DataEnum) -> Result<TokenStream, crate::e
_ => None,
};

relation_fn(variant.ident.to_string(), belongs_to, has_many)
Ok((variant.ident.to_string(), belongs_to, has_many, false))
},
)
.collect::<Result<Vec<_>, crate::error::Error>>()?
.into_iter()
.map(|(loader, func)| (loader, func))
.unzip();
.collect::<Result<Vec<_>, crate::error::Error>>()?;

Ok(quote! {
#(#loaders)*

#[async_graphql::ComplexObject]
impl Model {
#(#functions)*
}
})
produce_relations(relations_parameters)
}

#[derive(Debug)]
Expand Down Expand Up @@ -122,23 +112,50 @@ pub fn expanded_relation_fn(item: &syn::ItemImpl) -> Result<TokenStream, crate::
})
.collect::<Result<Vec<ExpandedParams>, crate::error::Error>>()?;

let (loaders, functions): (Vec<_>, Vec<_>) = expanded_params
let relations_parameters: Vec<(String, Option<String>, Option<String>, bool)> = expanded_params
.iter()
.map(
|params| -> Result<(TokenStream, TokenStream), crate::error::Error> {
let belongs_to = if params.relation_type.to_string().eq("belongs_to") {
Some(params.related_type.to_token_stream().to_string())
} else {
None
};
.map(|params| -> Result<(String, Option<String>, Option<String>, bool), crate::error::Error> {
let belongs_to = if params.relation_type.to_string().eq("belongs_to") {
Some(params.related_type.to_token_stream().to_string())
} else {
None
};

let has_many = if params.relation_type.to_string().ne("belongs_to") {
Some(params.related_type.to_token_stream().to_string())
} else {
None
};

let relation_name = params.variant.to_string();

Ok((relation_name, belongs_to, has_many, false))
})
.collect::<Result<Vec<_>, crate::error::Error>>()?;

let has_many = if params.relation_type.to_string().ne("belongs_to") {
Some(params.related_type.to_token_stream().to_string())
} else {
None
};
produce_relations(relations_parameters)
}

relation_fn(params.variant.to_string(), belongs_to, has_many)
pub fn produce_relations(
relations_parameters: Vec<(String, Option<String>, Option<String>, bool)>,
) -> Result<TokenStream, crate::error::Error> {
let relations_copy = relations_parameters.clone();

let reverse_self_references_parameters = relations_copy
.into_iter()
.filter(|(_, belongs_to, has_one, _)| {
belongs_to.eq(&Some("Entity".into())) || has_one.eq(&Some("Entity".into()))
karatakis marked this conversation as resolved.
Show resolved Hide resolved
})
.map(|(relation_name, belongs_to, has_many, _)| {
(relation_name, has_many, belongs_to, true)
});

let (loaders, functions): (Vec<_>, Vec<_>) = relations_parameters
.into_iter()
.chain(reverse_self_references_parameters)
.map(
|(relation_name, belongs_to, has_many, reverse)| -> Result<(TokenStream, TokenStream), crate::error::Error> {
relation_fn(relation_name, belongs_to, has_many, reverse)
},
)
.collect::<Result<Vec<_>, crate::error::Error>>()?
Expand All @@ -147,8 +164,6 @@ pub fn expanded_relation_fn(item: &syn::ItemImpl) -> Result<TokenStream, crate::
.unzip();

Ok(quote! {
#item

#(#loaders)*

#[async_graphql::ComplexObject]
Expand All @@ -162,9 +177,22 @@ pub fn relation_fn(
relation_name: String,
belongs_to: Option<String>,
has_many: Option<String>,
reverse: bool,
) -> Result<(TokenStream, TokenStream), crate::error::Error> {
let relation_ident = format_ident!("{}", relation_name.to_upper_camel_case());

let relation_name = if reverse {
format_ident!("{}Reverse", relation_name.to_upper_camel_case())
} else {
format_ident!("{}", relation_name.to_upper_camel_case())
};

let (reverse, column_type) = if reverse {
(quote! { true }, quote! { to_col })
} else {
(quote! { false }, quote! { from_col })
};

let target_path = if let Some(target_path) = &has_many {
target_path
} else if let Some(target_path) = &belongs_to {
Expand All @@ -180,9 +208,7 @@ pub fn relation_fn(
.parse()
.unwrap()
} else {
return Err(crate::error::Error::Internal(
"Cannot parse entity path".into(),
));
quote! { self }
};

let (return_type, extra_imports, map_method) = if has_many.is_some() {
Expand All @@ -200,7 +226,7 @@ pub fn relation_fn(
};

let relation_enum = quote! {Relation::#relation_ident};
let foreign_key_name = format_ident!("{}FK", relation_ident).to_token_stream();
let foreign_key_name = format_ident!("{}FK", relation_name).to_token_stream();

Ok((
quote! {
Expand Down Expand Up @@ -230,6 +256,7 @@ pub fn relation_fn(
::fetch_relation_data::<#path::Entity, #path::Filter, #path::OrderBy>(
keys,
#relation_enum.def(),
#reverse,
&self.db,
).await?
.into_iter()
Expand All @@ -242,7 +269,7 @@ pub fn relation_fn(
}
},
quote! {
pub async fn #relation_ident<'a>(
pub async fn #relation_name<'a>(
&self,
ctx: &async_graphql::Context<'a>,
) -> Option<#return_type> {
Expand All @@ -253,16 +280,17 @@ pub fn relation_fn(
.data::<async_graphql::dataloader::DataLoader<crate::OrmDataloader>>()
.unwrap();

let from_column: Column = Column::from_str(
// TODO support multiple value keys
let target_column: Column = Column::from_str(
#relation_enum
.def()
.from_col
.#column_type
.to_string()
.to_snake_case()
.as_str()
).unwrap();

let key = #foreign_key_name(seaography::RelationKeyStruct(self.get(from_column), None, None));
let key = #foreign_key_name(seaography::RelationKeyStruct(self.get(target_column), None, None));

let data: Option<_> = data_loader.load_one(key).await.unwrap();

Expand Down
6 changes: 3 additions & 3 deletions examples/mysql/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ name = 'seaography-mysql-example'
version = '0.2.0'

[dependencies]
async-graphql = { version = "4.0.10", features = ["decimal", "chrono", "dataloader"] }
async-graphql-poem = { version = "4.0.10" }
poem = { version = "1.3.29" }
async-graphql = { version = "4.0.14", features = ["decimal", "chrono", "dataloader"] }
async-graphql-poem = { version = "4.0.14" }
async-trait = { version = "0.1.53" }
dotenv = "0.15.0"
poem = { version = "1.3.29" }
sea-orm = { version = "^0.10", features = ["sqlx-mysql", "runtime-async-std-native-tls"] }
tokio = { version = "1.17.0", features = ["macros", "rt-multi-thread"] }
tracing = { version = "0.1.34" }
Expand Down
5 changes: 3 additions & 2 deletions examples/mysql/sakila-data.sql

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions examples/mysql/sakila-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,9 @@ CREATE TABLE film_category (

--
-- Table structure for table `film_text`
--
--
-- InnoDB added FULLTEXT support in 5.6.10. If you use an
-- earlier version, then consider upgrading (recommended) or
-- earlier version, then consider upgrading (recommended) or
-- changing InnoDB to MyISAM as the film_text engine
--

Expand Down Expand Up @@ -315,6 +315,7 @@ CREATE TABLE staff (
first_name VARCHAR(45) NOT NULL,
last_name VARCHAR(45) NOT NULL,
address_id INT NOT NULL,
reports_to_id INT,
picture BLOB DEFAULT NULL,
email VARCHAR(50) DEFAULT NULL,
store_id INT NOT NULL,
Expand All @@ -326,6 +327,7 @@ CREATE TABLE staff (
KEY idx_fk_store_id (store_id),
KEY idx_fk_address_id (address_id),
CONSTRAINT fk_staff_store FOREIGN KEY (store_id) REFERENCES store (store_id) ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT fk_staff_staff FOREIGN KEY (reports_to_id) REFERENCES staff (staff_id) ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT fk_staff_address FOREIGN KEY (address_id) REFERENCES address (address_id) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Expand Down
2 changes: 1 addition & 1 deletion examples/mysql/src/entities/customer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub struct Model {
pub address_id: i32,
pub active: i8,
pub create_date: DateTime,
pub last_update: Option<DateTimeUtc>,
pub last_update: DateTimeUtc,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)]
Expand Down
8 changes: 1 addition & 7 deletions examples/mysql/src/entities/film_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,7 @@ pub struct Model {
pub description: Option<String>,
}

#[derive(Copy, Clone, Debug, EnumIter, seaography::macros::RelationsCompact)]
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)]
pub enum Relation {}

impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
panic!("No RelationDef")
}
}

impl ActiveModelBehavior for ActiveModel {}
8 changes: 1 addition & 7 deletions examples/mysql/src/entities/language.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,7 @@ pub struct Model {
pub last_update: DateTimeUtc,
}

#[derive(Copy, Clone, Debug, EnumIter, seaography::macros::RelationsCompact)]
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)]
pub enum Relation {}

impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
panic!("No RelationDef")
}
}

impl ActiveModelBehavior for ActiveModel {}
2 changes: 1 addition & 1 deletion examples/mysql/src/entities/payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub struct Model {
#[sea_orm(column_type = "Decimal(Some((5, 2)))")]
pub amount: Decimal,
pub payment_date: DateTime,
pub last_update: Option<DateTimeUtc>,
pub last_update: DateTimeUtc,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)]
Expand Down
9 changes: 9 additions & 0 deletions examples/mysql/src/entities/staff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct Model {
pub first_name: String,
pub last_name: String,
pub address_id: i32,
pub reports_to_id: Option<i32>,
pub picture: Option<Vec<u8>>,
pub email: Option<String>,
pub store_id: i32,
Expand Down Expand Up @@ -44,6 +45,14 @@ pub enum Relation {
on_delete = "Restrict"
)]
Store,
#[sea_orm(
belongs_to = "Entity",
from = "Column::ReportsToId",
to = "Column::StaffId",
on_update = "Restrict",
on_delete = "Restrict"
)]
SelfRef,
#[sea_orm(has_many = "super::payment::Entity")]
Payment,
#[sea_orm(has_many = "super::rental::Entity")]
Expand Down
2 changes: 0 additions & 2 deletions examples/mysql/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ async fn main() {
.with_max_level(tracing::Level::INFO)
.with_test_writer()
.init();

let database = Database::connect(&*DATABASE_URL)
.await
.expect("Fail to initialize database connection");
Expand All @@ -61,7 +60,6 @@ async fn main() {
&*ENDPOINT,
get(graphql_playground).post(GraphQL::new(schema)),
);

println!("Visit GraphQL Playground at http://{}", *URL);
Server::new(TcpListener::bind(&*URL))
.run(app)
Expand Down
60 changes: 59 additions & 1 deletion examples/mysql/tests/query_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,4 +442,62 @@ async fn test_cursor_pagination_no_next() {
}
"#,
)
}
}

#[tokio::test]
async fn test_self_ref() {
let schema = get_schema().await;

assert_eq(
schema
.execute(
r#"
{
staff {
nodes {
firstName
reportsToId
selfRefReverse {
staffId
firstName
}
selfRef {
staffId
firstName
}
}
}
}
"#,
)
.await,
r#"
{
"staff": {
"nodes": [
{
"firstName": "Mike",
"reportsToId": null,
"selfRefReverse": [
{
"staffId": 2,
"firstName": "Jon"
}
],
"selfRef": null
},
{
"firstName": "Jon",
"reportsToId": 1,
"selfRefReverse": null,
"selfRef": {
"staffId": 1,
"firstName": "Mike"
}
}
]
}
}
"#,
)
}
Loading