Skip to content

Instantly share code, notes, and snippets.

@p-alik
Last active June 6, 2020 05:24
Show Gist options
  • Save p-alik/fb53204bb75733268d918de1fa6804b8 to your computer and use it in GitHub Desktop.
Save p-alik/fb53204bb75733268d918de1fa6804b8 to your computer and use it in GitHub Desktop.

Revisions

  1. p-alik revised this gist Jun 6, 2020. 1 changed file with 33 additions and 38 deletions.
    71 changes: 33 additions & 38 deletions lib.rs
    Original file line number Diff line number Diff line change
    @@ -1,14 +1,18 @@
    use diesel::associations::HasTable;
    use diesel::backend::Backend;
    use diesel::deserialize::FromSql;
    use diesel::dsl::SqlTypeOf;
    use diesel::expression::{Expression, NonAggregate};
    use diesel::expression::{Expression, NonAggregate, SelectableExpression};
    use diesel::insertable::CanInsertInSingleQuery;
    use diesel::mysql::Mysql;
    use diesel::query_builder::{BoxedSelectStatement, QueryFragment};
    use diesel::query_dsl::methods::{BoxedDsl, FilterDsl};
    use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
    use diesel::sql_types::{Bigint, HasSqlType, Integer};
    use diesel::{AppearsOnTable, Connection, EqAll, Identifiable, Insertable, MysqlConnection, Table};
    use diesel::{
    AppearsOnTable, Connection, EqAll, Identifiable, Insertable, MysqlConnection, RunQueryDsl,
    Table,
    };
    use juniper::{http::GraphQLRequest, ExecutionResult, Executor, Selection, Value};
    use serde::{Deserialize, Serialize};
    use std::convert::TryFrom;
    @@ -57,43 +61,33 @@ pub struct NewComment {
    content: String,
    }

    impl<L, I, DB, Ctx> HandleInsert<L, I, DB, Ctx> for comments::table
    impl<L, I, Ctx, T> HandleInsert<L, I, Mysql, Ctx> for comments::table
    where
    L: Table + HasTable<Table = L> + 'static,
    L::PrimaryKey: QueryFragment<Mysql> + Default,
    L::PrimaryKey: EqAll<i32>,
    L::PrimaryKey: Expression<SqlType = Integer>,
    L: LoadingHandler<Mysql, Ctx>,
    <L as LoadingHandler<Mysql, Ctx>>::FieldList:
    WundergraphFieldList<Mysql, <L as LoadingHandler<Mysql, Ctx>>::PrimaryKeyIndex, L, Ctx>,
    <L as LoadingHandler<Mysql, Ctx>>::Columns: BuildSelect<
    L,
    Mysql,
    <<L as LoadingHandler<Mysql, Ctx>>::FieldList as WundergraphFieldList<
    Mysql,
    <L as LoadingHandler<Mysql, Ctx>>::PrimaryKeyIndex,
    L,
    Ctx,
    >>::SqlType,
    >,
    <L as LoadingHandler<Mysql, Ctx>>::Columns: BuildOrder<L, Mysql>,
    <<L as LoadingHandler<Mysql, Ctx>>::Filter as BuildFilter<Mysql>>::Ret: AppearsOnTable<L>,
    <<L as Table>::PrimaryKey as EqAll<i32>>::Output: QueryFragment<Mysql>,
    <<L as Table>::PrimaryKey as EqAll<i32>>::Output: NonAggregate,
    <<L as Table>::PrimaryKey as EqAll<i32>>::Output: AppearsOnTable<L>,
    <L as diesel::QuerySource>::FromClause: QueryFragment<Mysql>,
    L: BoxedDsl<
    T: Table + HasTable<Table = T> + 'static,
    T::FromClause: QueryFragment<Mysql>,
    L: LoadingHandler<Mysql, Ctx, Table = T> + 'static,
    L::FieldList: WundergraphFieldList<Mysql, L::PrimaryKeyIndex, T, Ctx>,
    <L::Filter as BuildFilter<Mysql>>::Ret: AppearsOnTable<T>,
    L::Columns: BuildOrder<T, Mysql>
    + BuildSelect<T, Mysql, SqlTypeOfPlaceholder<L::FieldList, Mysql, L::PrimaryKeyIndex, T, Ctx>>,
    &'static L: Identifiable,
    Ctx: WundergraphContext + QueryModifier<L, Mysql>,
    Ctx::Connection: Connection<Backend = Mysql>,
    I: Insertable<T>,
    I::Values: QueryFragment<Mysql> + CanInsertInSingleQuery<Mysql>,
    T: BoxedDsl<
    'static,
    Mysql,
    Output = BoxedSelectStatement<'static, SqlTypeOf<<L as Table>::AllColumns>, L, Mysql>,
    Output = BoxedSelectStatement<'static, SqlTypeOf<<T as Table>::AllColumns>, T, Mysql>,
    >,
    I: Insertable<comments::table>,
    I::Values: QueryFragment<Mysql> + CanInsertInSingleQuery<Mysql>,
    DB: Backend,
    Ctx: WundergraphContext + QueryModifier<L, Mysql>,
    Ctx::Connection: Connection<Backend = Mysql>,
    <Ctx::Connection as Connection>::Backend: HasSqlType<SqlTypeOf<L::PrimaryKey>>
    + HasSqlType<SqlTypeOfPlaceholder<L::FieldList, Mysql, L::PrimaryKeyIndex, L, Ctx>>,
    <Ctx::Connection as Connection>::Backend: HasSqlType<SqlTypeOf<T::PrimaryKey>>
    + HasSqlType<SqlTypeOfPlaceholder<L::FieldList, Mysql, L::PrimaryKeyIndex, T, Ctx>>,
    T::PrimaryKey: EqAll<i32>,
    T::PrimaryKey: Expression<SqlType = Integer>,
    T::PrimaryKey: QueryFragment<Mysql> + Default,
    <T::PrimaryKey as EqAll<i32>>::Output:
    SelectableExpression<T> + NonAggregate + QueryFragment<Mysql> + 'static,
    i32: FromSql<Integer, Mysql>,
    {
    fn handle_insert(
    selection: Option<&'_ [Selection<'_, WundergraphScalarValue>]>,
    @@ -104,13 +98,13 @@ where
    let conn = ctx.get_connection();
    conn.transaction(|| -> ExecutionResult<WundergraphScalarValue> {
    let look_ahead = executor.look_ahead();
    use crate::diesel::RunQueryDsl;
    insertable.insert_into(comments::table).execute(conn)?;
    insertable.insert_into(T::table()).execute(conn)?;

    let last_insert_id: i64 = diesel::select(LAST_INSERT_ID).first(conn)?;
    let q = L::build_query(&[], &look_ahead)?;
    let q = FilterDsl::filter(
    q,
    L::PrimaryKey::default().eq_all(i32::try_from(last_insert_id)?),
    T::PrimaryKey::default().eq_all(i32::try_from(last_insert_id)?),
    );
    let items = L::load(&look_ahead, selection, executor, q)?;
    Ok(items.into_iter().next().unwrap_or(Value::Null))
    @@ -142,6 +136,7 @@ pub struct CommentChangeset {
    id: i32,
    content: String,
    }

    wundergraph::mutation_object! {
    Mutation{
    Comment(insert = NewComment, update = CommentChangeset, ),
  2. p-alik created this gist Jun 5, 2020.
    165 changes: 165 additions & 0 deletions lib.rs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,165 @@
    use diesel::associations::HasTable;
    use diesel::backend::Backend;
    use diesel::dsl::SqlTypeOf;
    use diesel::expression::{Expression, NonAggregate};
    use diesel::insertable::CanInsertInSingleQuery;
    use diesel::mysql::Mysql;
    use diesel::query_builder::{BoxedSelectStatement, QueryFragment};
    use diesel::query_dsl::methods::{BoxedDsl, FilterDsl};
    use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
    use diesel::sql_types::{Bigint, HasSqlType, Integer};
    use diesel::{AppearsOnTable, Connection, EqAll, Identifiable, Insertable, MysqlConnection, Table};
    use juniper::{http::GraphQLRequest, ExecutionResult, Executor, Selection, Value};
    use serde::{Deserialize, Serialize};
    use std::convert::TryFrom;
    use std::sync::Arc;
    use wundergraph::query_builder::mutations::{HandleBatchInsert, HandleInsert};
    use wundergraph::query_builder::selection::{
    fields::WundergraphFieldList, filter::BuildFilter, order::BuildOrder, select::BuildSelect,
    LoadingHandler, QueryModifier, SqlTypeOfPlaceholder,
    };
    use wundergraph::scalar::WundergraphScalarValue;
    use wundergraph::{WundergraphContext, WundergraphEntity};

    diesel::no_arg_sql_function!(LAST_INSERT_ID, Bigint);

    table! {
    comments (id) {
    id -> Integer,
    post -> Nullable<Integer>,
    commenter -> Nullable<Integer>,
    content -> Text,
    }
    }

    allow_tables_to_appear_in_same_query!(comments,);

    #[derive(Clone, Debug, Identifiable, WundergraphEntity)]
    #[table_name = "comments"]
    #[primary_key(id)]
    pub struct Comment {
    id: i32,
    content: String,
    }

    wundergraph::query_object! {
    Query {
    Comment,
    }
    }

    #[derive(Insertable, juniper::GraphQLInputObject, Clone, Debug)]
    #[graphql(scalar = "WundergraphScalarValue")]
    #[table_name = "comments"]
    pub struct NewComment {
    post: Option<i32>,
    commenter: Option<i32>,
    content: String,
    }

    impl<L, I, DB, Ctx> HandleInsert<L, I, DB, Ctx> for comments::table
    where
    L: Table + HasTable<Table = L> + 'static,
    L::PrimaryKey: QueryFragment<Mysql> + Default,
    L::PrimaryKey: EqAll<i32>,
    L::PrimaryKey: Expression<SqlType = Integer>,
    L: LoadingHandler<Mysql, Ctx>,
    <L as LoadingHandler<Mysql, Ctx>>::FieldList:
    WundergraphFieldList<Mysql, <L as LoadingHandler<Mysql, Ctx>>::PrimaryKeyIndex, L, Ctx>,
    <L as LoadingHandler<Mysql, Ctx>>::Columns: BuildSelect<
    L,
    Mysql,
    <<L as LoadingHandler<Mysql, Ctx>>::FieldList as WundergraphFieldList<
    Mysql,
    <L as LoadingHandler<Mysql, Ctx>>::PrimaryKeyIndex,
    L,
    Ctx,
    >>::SqlType,
    >,
    <L as LoadingHandler<Mysql, Ctx>>::Columns: BuildOrder<L, Mysql>,
    <<L as LoadingHandler<Mysql, Ctx>>::Filter as BuildFilter<Mysql>>::Ret: AppearsOnTable<L>,
    <<L as Table>::PrimaryKey as EqAll<i32>>::Output: QueryFragment<Mysql>,
    <<L as Table>::PrimaryKey as EqAll<i32>>::Output: NonAggregate,
    <<L as Table>::PrimaryKey as EqAll<i32>>::Output: AppearsOnTable<L>,
    <L as diesel::QuerySource>::FromClause: QueryFragment<Mysql>,
    L: BoxedDsl<
    'static,
    Mysql,
    Output = BoxedSelectStatement<'static, SqlTypeOf<<L as Table>::AllColumns>, L, Mysql>,
    >,
    I: Insertable<comments::table>,
    I::Values: QueryFragment<Mysql> + CanInsertInSingleQuery<Mysql>,
    DB: Backend,
    Ctx: WundergraphContext + QueryModifier<L, Mysql>,
    Ctx::Connection: Connection<Backend = Mysql>,
    <Ctx::Connection as Connection>::Backend: HasSqlType<SqlTypeOf<L::PrimaryKey>>
    + HasSqlType<SqlTypeOfPlaceholder<L::FieldList, Mysql, L::PrimaryKeyIndex, L, Ctx>>,
    {
    fn handle_insert(
    selection: Option<&'_ [Selection<'_, WundergraphScalarValue>]>,
    executor: &Executor<'_, Ctx, WundergraphScalarValue>,
    insertable: I,
    ) -> ExecutionResult<WundergraphScalarValue> {
    let ctx = executor.context();
    let conn = ctx.get_connection();
    conn.transaction(|| -> ExecutionResult<WundergraphScalarValue> {
    let look_ahead = executor.look_ahead();
    use crate::diesel::RunQueryDsl;
    insertable.insert_into(comments::table).execute(conn)?;
    let last_insert_id: i64 = diesel::select(LAST_INSERT_ID).first(conn)?;
    let q = L::build_query(&[], &look_ahead)?;
    let q = FilterDsl::filter(
    q,
    L::PrimaryKey::default().eq_all(i32::try_from(last_insert_id)?),
    );
    let items = L::load(&look_ahead, selection, executor, q)?;
    Ok(items.into_iter().next().unwrap_or(Value::Null))
    })
    }
    }

    impl<L, I, DB, Ctx> HandleBatchInsert<L, I, DB, Ctx> for comments::table
    where
    L: LoadingHandler<Mysql, Ctx>,
    DB: Backend,
    Ctx: WundergraphContext + QueryModifier<L, Mysql>,
    Ctx::Connection: Connection<Backend = Mysql>,
    {
    fn handle_batch_insert(
    _selection: Option<&'_ [Selection<'_, WundergraphScalarValue>]>,
    _executor: &Executor<'_, Ctx, WundergraphScalarValue>,
    _insertable: Vec<I>,
    ) -> ExecutionResult<WundergraphScalarValue> {
    unimplemented!()
    }
    }

    #[derive(AsChangeset, Identifiable, juniper::GraphQLInputObject, Clone, Debug)]
    #[graphql(scalar = "WundergraphScalarValue")]
    #[table_name = "comments"]
    #[primary_key(id)]
    pub struct CommentChangeset {
    id: i32,
    content: String,
    }
    wundergraph::mutation_object! {
    Mutation{
    Comment(insert = NewComment, update = CommentChangeset, ),
    }
    }

    pub type Schema<Connection> = juniper::RootNode<
    'static,
    Query<PooledConnection<ConnectionManager<Connection>>>,
    Mutation<PooledConnection<ConnectionManager<Connection>>>,
    WundergraphScalarValue,
    >;

    #[derive(Serialize, Deserialize, Debug)]
    pub struct GraphQLData(GraphQLRequest<WundergraphScalarValue>);

    #[derive(Clone)]
    struct AppState {
    schema: Arc<Schema<MysqlConnection>>,
    pool: Arc<Pool<ConnectionManager<MysqlConnection>>>,
    }