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

Update mutation breaks when using Postgres native enums #158

Open
guidomb opened this issue Nov 29, 2023 · 1 comment
Open

Update mutation breaks when using Postgres native enums #158

guidomb opened this issue Nov 29, 2023 · 1 comment

Comments

@guidomb
Copy link

guidomb commented Nov 29, 2023

Description

If I have an entity with a field of type enum that's mapped to a native enum type in Postgres. When trying to update the entity's enum field via an update mutation and providing the value as a native GraphQL enum, the mutation panics when executed. To avoid the crash the value must be passed as string but that makes the explorer display an error and (per my understanding) this is incorrect.

author.rs

//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.6

use sea_orm::entity::prelude::*;
use serde::Serialize;

use crate::AuthorRole;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize)]
#[sea_orm(table_name = "author")]
pub struct Model {
    #[sea_orm(primary_key, auto_increment = false)]
    pub id: Uuid,
    #[sea_orm(column_type = "Text", unique)]
    pub email: String,
    #[sea_orm(column_type = "Text")]
    pub first_name: String,
    #[sea_orm(column_type = "Text")]
    pub last_name: String,
    pub created_at: DateTimeWithTimeZone,
    #[sea_orm(unique)]
    pub username: String,
    pub role: AuthorRole,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
    #[sea_orm(has_many = "super::post::Entity")]
    Posts,
}

impl Related<super::post::Entity> for Entity {
    fn to() -> RelationDef {
        Relation::Posts.def()
    }
}

impl ActiveModelBehavior for ActiveModel {}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)]
pub enum RelatedEntity {
    #[sea_orm(entity = "super::post::Entity")]
    Post,
}

author_role.rs

#[derive(
    EnumIter, DeriveActiveEnum, PartialEq, Eq, Clone, Copy, Debug, Serialize, async_graphql::Enum,
)]
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "author_role")]
#[serde(rename_all = "UPPERCASE")]
pub enum AuthorRole {
    #[sea_orm(string_value = "WRITTER")]
    Writter,
    #[sea_orm(string_value = "PUBLISHER")]
    Publisher,
    #[sea_orm(string_value = "EDITOR")]
    Editor,
}

migration

use entities::*;
use sea_orm_migration::{
    prelude::*,
    sea_orm::{EntityTrait, Set},
};

#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager
            .create_table(
                Table::create()
                    .table(Author)
                    .if_not_exists()
                    .col(
                        ColumnDef::new(author::Column::Id)
                            .uuid()
                            .extra("DEFAULT gen_random_uuid()")
                            .not_null()
                            .primary_key(),
                    )
                    .col(
                        ColumnDef::new(author::Column::Email)
                            .string_len(254)
                            .unique_key()
                            .not_null(),
                    )
                    .col(
                        ColumnDef::new(author::Column::FirstName)
                            .string_len(100)
                            .not_null(),
                    )
                    .col(
                        ColumnDef::new(author::Column::LastName)
                            .string_len(100)
                            .not_null(),
                    )
                    .col(
                        ColumnDef::new(author::Column::Username)
                            .string_len(50)
                            .unique_key()
                            .not_null(),
                    )
                    .col(
                        ColumnDef::new(post::Column::CreatedAt)
                            .timestamp_with_time_zone()
                            .extra("DEFAULT now()")
                            .not_null(),
                    )
                    .to_owned(),
            )
            .await?;
        manager
            .create_table(
                Table::create()
                    .table(Post)
                    .if_not_exists()
                    .col(
                        ColumnDef::new(post::Column::Id)
                            .uuid()
                            .extra("DEFAULT gen_random_uuid()")
                            .not_null()
                            .primary_key(),
                    )
                    .col(
                        ColumnDef::new(post::Column::Title)
                            .string_len(100)
                            .not_null(),
                    )
                    .col(
                        ColumnDef::new(post::Column::Description)
                            .string_len(500)
                            .not_null(),
                    )
                    .col(ColumnDef::new(post::Column::Content).text().not_null())
                    .col(
                        ColumnDef::new(post::Column::CreatedAt)
                            .timestamp_with_time_zone()
                            .extra("DEFAULT now()")
                            .not_null(),
                    )
                    .col(ColumnDef::new(post::Column::AuthorId).uuid().not_null())
                    .foreign_key(
                        ForeignKeyCreateStatement::new()
                            .name("post_author")
                            .from(Post, post::Column::AuthorId)
                            .to(Author, author::Column::Id),
                    )
                    .to_owned(),
            )
            .await?;

        // Seeding tables
        let result = Author::insert(author::ActiveModel {
            email: Set("[email protected]".into()),
            first_name: Set("Guido".into()),
            last_name: Set("Marucci Blas".into()),
            username: Set("guidomb".into()),
            ..Default::default()
        })
        .exec(manager.get_connection())
        .await?;
        let author_01_id = result.last_insert_id;

        let result = Author::insert(author::ActiveModel {
            email: Set("[email protected]".into()),
            first_name: Set("Nicolás".into()),
            last_name: Set("Magni".into()),
            username: Set("nicomagni".into()),
            ..Default::default()
        })
        .exec(manager.get_connection())
        .await?;
        let author_02_id = result.last_insert_id;

        let posts = vec![
            post::ActiveModel {
                title: Set("Some example post".into()),
                description: Set("This is an example post used for debugging".into()),
                content: Set("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.".into()),
                author_id: Set(author_01_id),
                ..Default::default()
            },
            post::ActiveModel {
                title: Set("Another example post".into()),
                description: Set("This is another example post used for debugging".into()),
                content: Set("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.".into()),
                author_id: Set(author_02_id),
                ..Default::default()
            }
        ];
        Post::insert_many(posts)
            .exec(manager.get_connection())
            .await?;

        Ok(())
    }

    async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager
            .drop_table(Table::drop().table(Post).to_owned())
            .await?;
        manager
            .drop_table(Table::drop().table(Author).to_owned())
            .await?;

        Ok(())
    }
}

error

{
  "data": null,
  "errors": [
    {
      "message": "[async_graphql] Error { message: \"internal: not a string\", extensions: None }",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ]
    }
  ]
}

Screenshot 2023-11-29 at 13 09 45
Screenshot 2023-11-29 at 13 10 13

Steps to Reproduce

  1. Define an entity with a field of type enum that's mapped to a native Postgres enum
  2. Update the entity field of type enum via an update mutation providing a value as a native GraphQL enum

Expected Behavior

The field gets updated

Actual Behavior

The mutation fails

Reproduces How Often

Always

Versions

source = "git+https://github.com/SeaQL/seaography.git#9c43f72017fb27bd5e2602c553ee59acc45ae26e"

Additional Information

@YiNNx
Copy link
Contributor

YiNNx commented Nov 29, 2023

I also encountered this problem. But for my case, changing the enum value to the string can make the mutation operation successful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants