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 Enum Types Handling #181

Merged
merged 7 commits into from
Dec 2, 2024
Merged
Changes from 1 commit
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
Next Next commit
enum_type_name
billy1624 committed Nov 7, 2024
commit 7f90d34a0145efd5b68a53f9a53bf870aa2fe7ef
155 changes: 80 additions & 75 deletions src/builder_context/types_map.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{collections::BTreeMap, num::ParseIntError};

use heck::ToUpperCamelCase;
use async_graphql::dynamic::{TypeRef, ValueAccessor};
use sea_orm::{ColumnTrait, ColumnType, EntityTrait};

@@ -238,87 +239,91 @@ impl TypesMapHelper {
&self,
ty: &ColumnType,
not_null: bool,
enum_type_name: Option<String>,
) -> Option<TypeRef> {
let active_enum_builder = ActiveEnumBuilder {
context: self.context,
};

match ty {
ColumnType::Char(_) | ColumnType::String(_) | ColumnType::Text => {
Some(TypeRef::named(TypeRef::STRING))
}
ColumnType::TinyInteger
| ColumnType::SmallInteger
| ColumnType::Integer
| ColumnType::BigInteger
| ColumnType::TinyUnsigned
| ColumnType::SmallUnsigned
| ColumnType::Unsigned
| ColumnType::BigUnsigned => Some(TypeRef::named(TypeRef::INT)),
ColumnType::Float | ColumnType::Double => Some(TypeRef::named(TypeRef::FLOAT)),
ColumnType::Decimal(_) | ColumnType::Money(_) => Some(TypeRef::named(TypeRef::STRING)),
ColumnType::DateTime
| ColumnType::Timestamp
| ColumnType::TimestampWithTimeZone
| ColumnType::Time
| ColumnType::Date => Some(TypeRef::named(TypeRef::STRING)),
ColumnType::Year => Some(TypeRef::named(TypeRef::INT)),
ColumnType::Interval(_, _) => Some(TypeRef::named(TypeRef::STRING)),
ColumnType::Binary(_)
| ColumnType::VarBinary(_)
| ColumnType::Bit(_)
| ColumnType::VarBit(_)
| ColumnType::Blob => Some(TypeRef::named(TypeRef::STRING)),
ColumnType::Boolean => Some(TypeRef::named(TypeRef::BOOLEAN)),
// FIXME: support json type
ColumnType::Json | ColumnType::JsonBinary => None,
ColumnType::Uuid => Some(TypeRef::named(TypeRef::STRING)),
ColumnType::Enum {
name: enum_name,
variants: _,
} => Some(TypeRef::named(
active_enum_builder.type_name_from_iden(enum_name),
)),
ColumnType::Cidr | ColumnType::Inet | ColumnType::MacAddr => {
Some(TypeRef::named(TypeRef::STRING))
}
#[cfg(not(feature = "with-postgres-array"))]
ColumnType::Array(_) => None,
#[cfg(feature = "with-postgres-array")]
ColumnType::Array(iden) => {
// FIXME: Propagating the not_null flag here is probably incorrect. The following
// types are all logically valid:
// - [T]
// - [T!]
// - [T]!
// - [T!]!
// - [[T]]
// - [[T!]]
// - [[T]!]
// - [[T!]!]
// - [[T!]!]!
// - [[T!]]! (etc, recursively)
//
// This is true for both GraphQL itself but also for the equivalent types in some
// backends, like Postgres.
//
// However, the not_null flag lives on the column definition in sea_query, not on
// the type itself. That means we lose the ability to represent nullability
// reliably on any inner type. We have three options:
// - pass down the flag (what we're doing here):
// pros: likely the most common intent from those who care about nullability
// cons: can be incorrect in both inserts and queries
// - always pass true:
// pros: none? maybe inserts are easier to reason about?
// cons: just as likely to be wrong as flag passing
// - always pass false:
// pros: always technically workable for queries (annoying for non-null data)
// conts: bad for inserts
let iden_type = self.sea_orm_column_type_to_graphql_type(iden.as_ref(), true);
iden_type.map(|it| TypeRef::List(Box::new(it)))
match enum_type_name {
Some(enum_type_name) => Some(TypeRef::named(format!("{}Enum", enum_type_name.to_upper_camel_case()))),
None => match ty {
ColumnType::Char(_) | ColumnType::String(_) | ColumnType::Text => {
Some(TypeRef::named(TypeRef::STRING))
}
ColumnType::TinyInteger
| ColumnType::SmallInteger
| ColumnType::Integer
| ColumnType::BigInteger
| ColumnType::TinyUnsigned
| ColumnType::SmallUnsigned
| ColumnType::Unsigned
| ColumnType::BigUnsigned => Some(TypeRef::named(TypeRef::INT)),
ColumnType::Float | ColumnType::Double => Some(TypeRef::named(TypeRef::FLOAT)),
ColumnType::Decimal(_) | ColumnType::Money(_) => Some(TypeRef::named(TypeRef::STRING)),
ColumnType::DateTime
| ColumnType::Timestamp
| ColumnType::TimestampWithTimeZone
| ColumnType::Time
| ColumnType::Date => Some(TypeRef::named(TypeRef::STRING)),
ColumnType::Year => Some(TypeRef::named(TypeRef::INT)),
ColumnType::Interval(_, _) => Some(TypeRef::named(TypeRef::STRING)),
ColumnType::Binary(_)
| ColumnType::VarBinary(_)
| ColumnType::Bit(_)
| ColumnType::VarBit(_)
| ColumnType::Blob => Some(TypeRef::named(TypeRef::STRING)),
ColumnType::Boolean => Some(TypeRef::named(TypeRef::BOOLEAN)),
// FIXME: support json type
ColumnType::Json | ColumnType::JsonBinary => None,
ColumnType::Uuid => Some(TypeRef::named(TypeRef::STRING)),
ColumnType::Enum {
name: enum_name,
variants: _,
} => Some(TypeRef::named(
active_enum_builder.type_name_from_iden(enum_name),
)),
ColumnType::Cidr | ColumnType::Inet | ColumnType::MacAddr => {
Some(TypeRef::named(TypeRef::STRING))
}
#[cfg(not(feature = "with-postgres-array"))]
ColumnType::Array(_) => None,
#[cfg(feature = "with-postgres-array")]
ColumnType::Array(iden) => {
// FIXME: Propagating the not_null flag here is probably incorrect. The following
// types are all logically valid:
// - [T]
// - [T!]
// - [T]!
// - [T!]!
// - [[T]]
// - [[T!]]
// - [[T]!]
// - [[T!]!]
// - [[T!]!]!
// - [[T!]]! (etc, recursively)
//
// This is true for both GraphQL itself but also for the equivalent types in some
// backends, like Postgres.
//
// However, the not_null flag lives on the column definition in sea_query, not on
// the type itself. That means we lose the ability to represent nullability
// reliably on any inner type. We have three options:
// - pass down the flag (what we're doing here):
// pros: likely the most common intent from those who care about nullability
// cons: can be incorrect in both inserts and queries
// - always pass true:
// pros: none? maybe inserts are easier to reason about?
// cons: just as likely to be wrong as flag passing
// - always pass false:
// pros: always technically workable for queries (annoying for non-null data)
// conts: bad for inserts
let iden_type = self.sea_orm_column_type_to_graphql_type(iden.as_ref(), true, enum_type_name);
iden_type.map(|it| TypeRef::List(Box::new(it)))
}
ColumnType::Custom(_iden) => Some(TypeRef::named(TypeRef::STRING)),
_ => None,
}
ColumnType::Custom(_iden) => Some(TypeRef::named(TypeRef::STRING)),
_ => None,
}
.map(|ty| {
if not_null {
8 changes: 2 additions & 6 deletions src/enumerations/active_enum.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use async_graphql::dynamic::Enum;
use heck::{ToSnakeCase, ToUpperCamelCase};
use heck::ToUpperCamelCase;
use sea_orm::{ActiveEnum, DynIden, Value};

use crate::BuilderContext;
@@ -19,11 +19,7 @@ impl std::default::Default for ActiveEnumConfig {
format!("{}Enum", name.to_upper_camel_case())
}),
variant_name: Box::new(|_enum_name: &str, variant: &str| -> String {
if cfg!(feature = "field-snake-case") {
variant.to_snake_case()
} else {
variant.to_upper_camel_case().to_ascii_uppercase()
}
variant.trim_matches('\'').to_string()
}),
}
}
7 changes: 1 addition & 6 deletions src/inputs/active_enum_filter_input.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::BTreeSet;

use async_graphql::dynamic::ObjectAccessor;
use heck::{ToSnakeCase, ToUpperCamelCase};
use heck::ToUpperCamelCase;
use sea_orm::{ActiveEnum, ColumnTrait, ColumnType, Condition, DynIden, EntityTrait};

use crate::{ActiveEnumBuilder, BuilderContext, FilterInfo, FilterOperation, SeaResult};
@@ -89,11 +89,6 @@ where
let extract_variant = move |input: &str| -> String {
let variant = variants.iter().find(|variant| {
let variant = variant.to_string();
let variant = if cfg!(feature = "field-snake-case") {
variant.to_snake_case()
} else {
variant.to_upper_camel_case().to_ascii_uppercase()
};
variant.eq(input)
});
variant.unwrap().to_string()
2 changes: 2 additions & 0 deletions src/inputs/entity_input.rs
Original file line number Diff line number Diff line change
@@ -95,6 +95,7 @@ impl EntityInputBuilder {
}

let column_def = column.def();
let enum_type_name = column.enum_type_name();

let auto_increment = match <T::PrimaryKey as PrimaryKeyToColumn>::from_column(column) {
Some(_) => T::PrimaryKey::auto_increment(),
@@ -107,6 +108,7 @@ impl EntityInputBuilder {
let graphql_type = match types_map_helper.sea_orm_column_type_to_graphql_type(
column_def.get_column_type(),
is_insert_not_nullable,
enum_type_name,
) {
Some(type_name) => type_name,
None => return object,
6 changes: 3 additions & 3 deletions src/outputs/entity_object.rs
Original file line number Diff line number Diff line change
@@ -112,10 +112,12 @@ impl EntityObjectBuilder {
let column_name = self.column_name::<T>(&column);

let column_def = column.def();
let enum_type_name = column.enum_type_name();

let graphql_type = match types_map_helper.sea_orm_column_type_to_graphql_type(
column_def.get_column_type(),
!column_def.is_null(),
enum_type_name,
) {
Some(type_name) => type_name,
None => return object,
@@ -212,9 +214,7 @@ fn sea_query_value_to_graphql_value(
sea_orm::Value::BigUnsigned(value) => value.map(Value::from),
sea_orm::Value::Float(value) => value.map(Value::from),
sea_orm::Value::Double(value) => value.map(Value::from),
sea_orm::Value::String(value) if is_enum => {
value.map(|it| Value::from(it.as_str().to_upper_camel_case().to_ascii_uppercase()))
}
sea_orm::Value::String(value) if is_enum => value.map(|it| Value::from(it.as_str())),
sea_orm::Value::String(value) => value.map(|it| Value::from(it.as_str())),
sea_orm::Value::Char(value) => value.map(|it| Value::from(it.to_string())),