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

feat: update macro DeriveRelatedEntity to deal with nested create mutation #2493

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
13 changes: 10 additions & 3 deletions sea-orm-codegen/src/entity/base_entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,12 @@ impl Entity {
// 1st step get conjunct relations data
let conjunct_related_attrs = self.conjunct_relations.iter().map(|conj| {
let entity = format!("super::{}::Entity", conj.get_to_snake_case());
let active_model = format!("super::{}::ActiveModel", conj.get_to_snake_case());

quote! {
#[sea_orm(
entity = #entity
entity = #entity,
active_model = #active_model
)]
}
});
Expand All @@ -139,7 +141,10 @@ impl Entity {
Some(module_name) => format!("super::{}::Entity", module_name),
None => String::from("Entity"),
};

let active_model = match rel.get_module_name() {
Some(module_name) => format!("super::{}::ActiveModel", module_name),
None => String::from("ActiveModel"),
};
if rel.self_referencing || !rel.impl_related || rel.num_suffix > 0 {
let def = if reverse {
format!("Relation::{}.def().rev()", rel.get_enum_name())
Expand All @@ -150,13 +155,15 @@ impl Entity {
quote! {
#[sea_orm(
entity = #entity,
active_model = #active_model,
def = #def
)]
}
} else {
quote! {
#[sea_orm(
entity = #entity
entity = #entity,
active_model = #active_model
)]
}
}
Expand Down
3 changes: 3 additions & 0 deletions sea-orm-macros/src/derives/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ pub mod related_attr {
/// Entity ident
pub entity: Option<syn::Lit>,
///
/// ActiveModel ident
pub active_model: Option<syn::Lit>,
///
/// Allows to specify RelationDef
///
/// Optional
Expand Down
142 changes: 140 additions & 2 deletions sea-orm-macros/src/derives/related_entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod private {

struct DeriveRelatedEntity {
entity_ident: TokenStream,
active_model_ident: TokenStream,
ident: syn::Ident,
variants: syn::punctuated::Punctuated<syn::Variant, syn::token::Comma>,
}
Expand All @@ -30,6 +31,13 @@ mod private {
Some(entity_ident) => entity_ident.map_err(|_| Error::InvalidEntityPath)?,
None => quote! { Entity },
};
let active_model_ident =
match sea_attr.active_model.as_ref().map(Self::parse_lit_string) {
Some(active_model_ident) => {
active_model_ident.map_err(|_| Error::InvalidEntityPath)?
}
None => quote! { ActiveModel },
};

let variants = match input.data {
syn::Data::Enum(syn::DataEnum { variants, .. }) => variants,
Expand All @@ -38,6 +46,7 @@ mod private {

Ok(DeriveRelatedEntity {
entity_ident,
active_model_ident,
ident,
variants,
})
Expand All @@ -46,8 +55,9 @@ mod private {
fn expand(&self) -> syn::Result<TokenStream> {
let ident = &self.ident;
let entity_ident = &self.entity_ident;
let active_model_ident = &self.active_model_ident;

let variant_implementations: Vec<TokenStream> = self
let get_relation_variant_implementations: Vec<TokenStream> = self
.variants
.iter()
.map(|variant| {
Expand Down Expand Up @@ -85,6 +95,106 @@ mod private {
})
.collect::<Result<Vec<_>, _>>()?;

let get_relation_input_variant_implementations: Vec<TokenStream> = self
.variants
.iter()
.map(|variant| {
let attr = related_attr::SeaOrm::from_attributes(&variant.attrs)?;

let enum_name = &variant.ident;

let target_entity = attr
.entity
.as_ref()
.map(Self::parse_lit_string)
.ok_or_else(|| {
syn::Error::new_spanned(variant, "Missing value for 'entity'")
})??;

let def = match attr.def {
Some(def) => Some(Self::parse_lit_string(&def).map_err(|_| {
syn::Error::new_spanned(variant, "Missing value for 'def'")
})?),
None => None,
};

let name = enum_name.to_string().to_lower_camel_case();

if let Some(def) = def {
Result::<_, syn::Error>::Ok(quote! {
Self::#enum_name => builder.get_relation_input::<#entity_ident, #target_entity>(#name, #def)
})
} else {
Result::<_, syn::Error>::Ok(quote! {
Self::#enum_name => via_builder.get_relation_input::<#entity_ident, #target_entity>(#name)
})
}

})
.collect::<Result<Vec<_>, _>>()?;

let insert_related_variant_implementations: Vec<TokenStream> = self
.variants
.iter()
.map(|variant| {
let attr = related_attr::SeaOrm::from_attributes(&variant.attrs)?;

let enum_name = &variant.ident;

let target_entity = attr
.entity
.as_ref()
.map(Self::parse_lit_string)
.ok_or_else(|| {
syn::Error::new_spanned(variant, "Missing value for 'entity'")
})??;


let target_active_model = attr.active_model.as_ref().map(Self::parse_lit_string)
.ok_or_else(||{
syn::Error::new_spanned(variant, "Missing value for 'active_model'")
})??;

let def = match attr.def {
Some(def) => Some(Self::parse_lit_string(&def).map_err(|_| {
syn::Error::new_spanned(variant, "Missing value for 'def'")
})?),
None => None,
};

let name = enum_name.to_string().to_lower_camel_case();

if let Some(def) = def {
Result::<_, syn::Error>::Ok(quote! {
Self::#enum_name => {
if let Some(obj) = input_object.get(#name){
let active_models =
builder.insert_related::<#entity_ident, #active_model_ident, #target_entity, #target_active_model>(#def, &obj, owner)?;
if let Some(active_models) = active_models {
#target_entity::insert_many(active_models).exec(transaction).await?;
}
}
Ok(())
}
})
} else {
Result::<_, syn::Error>::Ok(quote! {
Self::#enum_name => {
if let Some(obj) = input_object.get(#name) {
let active_models =
via_builder.insert_related::<#entity_ident, #active_model_ident, #target_entity, #target_active_model>(&obj, owner)?;
if let Some(active_models) = active_models {
#target_entity::insert_many(active_models).exec(transaction).await?;
}
}
Ok(())
}
})
}

})
.collect::<Result<Vec<_>, _>>()?;

// Get the path of the `async-graphql` on the application's Cargo.toml
let async_graphql_crate = match crate_name("async-graphql") {
// if found, use application's `async-graphql`
Expand All @@ -103,11 +213,39 @@ mod private {
let builder = seaography::EntityObjectRelationBuilder { context };
let via_builder = seaography::EntityObjectViaRelationBuilder { context };
match self {
#(#variant_implementations,)*
#(#get_relation_variant_implementations,)*
_ => panic!("No relations for this entity"),
}
}

fn get_relation_input(
&self,
context: &'static seaography::BuilderContext,
) -> #async_graphql_crate::dynamic::InputValue {
let builder = seaography::EntityObjectRelationBuilder { context };
let via_builder = seaography::EntityObjectViaRelationBuilder { context };
match self {
#(#get_relation_input_variant_implementations,)*
_ => panic!("No relations for this entity"),
}
}

async fn insert_related(
&self,
context: &'static seaography::BuilderContext,
input_object: &#async_graphql_crate::dynamic::ObjectAccessor<'_>,
transaction: &sea_orm::DatabaseTransaction,
owner: bool,
) -> #async_graphql_crate::Result<()> {
let builder = seaography::EntityObjectRelationBuilder { context };
let via_builder = seaography::EntityObjectViaRelationBuilder { context };
match self {
#(#insert_related_variant_implementations,)*
_ => panic!("No relations for this entity"),
}

}

}
})
}
Expand Down