import { Arg , Ctx , Field , FieldResolver , InputType , Int , Mutation , ObjectType , Query , Resolver , Root , UseMiddleware , } from "type-graphql" ; import { getConnection } from "typeorm" ; import { Post } from "../entities/Post" ; import { Updoot } from "../entities/Updoot" ; import { User } from "../entities/User" ; import { isAuth } from "../middleware/isAuth" ; import { MyContext } from "../types" ; @ InputType ( ) class PostInput { @ Field ( ) title : string ; @ Field ( ) text : string ; } @ ObjectType ( ) class PaginatedPosts { @ Field ( ( ) => [ Post ] ) posts : Post [ ] ; @ Field ( ) hasMore : boolean ; } @ Resolver ( Post ) export class PostResolver { @ FieldResolver ( ( ) => String ) textSnippet ( @ Root ( ) post ) { return post . text . slice ( 0 , 50 ) ; } @ FieldResolver ( ( ) => User ) creator ( @ Root ( ) post , @ Ctx ( ) { userLoader } ) { return userLoader . load ( post . creatorId ) ; } @ FieldResolver ( ( ) => Int , { nullable : true } ) async voteStatus ( @ Root ( ) post , @ Ctx ( ) { updootLoader , req } ) { if ( ! req . session . userId ) { return null ; } const updoot = await updootLoader . load ( { postId : post . id , userId : req . session . userId , } ) ; return updoot ? updoot . value : null ; } @ Mutation ( ( ) => Boolean ) @ UseMiddleware ( isAuth ) async vote ( @ Arg ( "postId" , ( ) => Int ) postId , @ Arg ( "value" , ( ) => Int ) value , @ Ctx ( ) { req } ) { const isUpdoot = value !== - 1 ; const realValue = isUpdoot ? 1 : - 1 ; const { userId } = req . session ; const updoot = await Updoot . findOne ( { where : { postId , userId } } ) ; if ( updoot && updoot . value !== realValue ) { await getConnection ( ) . transaction ( async ( tm ) => { await tm . query ( `
    update updoot
    set value = $1
    where "postId" = $2 and "userId" = $3
        ` , [ realValue , postId , userId ] ) ; await tm . query ( `
          update post
          set points = points + $1
          where id = $2
        ` , [ 2 * realValue , postId ] ) ; } ) ; } else if ( ! updoot ) { await getConnection ( ) . transaction ( async ( tm ) => { await tm . query ( `
    insert into updoot ("userId", "postId", value)
    values ($1, $2, $3)
        ` , [ userId , postId , realValue ] ) ; await tm . query ( `
    update post
    set points = points + $1
    where id = $2
      ` , [ realValue , postId ] ) ; } ) ; } return true ; } @ Query ( ( ) => PaginatedPosts ) async posts ( @ Arg ( "limit" , ( ) => Int ) limit , @ Arg ( "cursor" , ( ) => String , { nullable : true } ) cursor ) { const realLimit = Math . min ( 50 , limit ) ; const reaLimitPlusOne = realLimit + 1 ; const replacements = [ reaLimitPlusOne ] ; if ( cursor ) { replacements . push ( new Date ( parseInt ( cursor ) ) ) ; } const posts = await getConnection ( ) . query ( `
    select p.*
    from post p
    ${ cursor ? `where p."createdAt" < $2` : "" }
    order by p."createdAt" DESC
    limit $1
    ` , replacements ) ; return { posts : posts . slice ( 0 , realLimit ) , hasMore : posts . length === reaLimitPlusOne , } ; } @ Query ( ( ) => Post , { nullable : true } ) post ( @ Arg ( "id" , ( ) => Int ) id ) { return Post . findOne ( id ) ; } @ Mutation ( ( ) => Post ) @ UseMiddleware ( isAuth ) async createPost ( @ Arg ( "input" ) input , @ Ctx ( ) { req } ) { return Post . create ( { ... input , creatorId : req . session . userId , } ) . save ( ) ; } @ Mutation ( ( ) => Post , { nullable : true } ) @ UseMiddleware ( isAuth ) async updatePost ( @ Arg ( "id" , ( ) => Int ) id , @ Arg ( "title" ) title , @ Arg ( "text" ) text , @ Ctx ( ) { req } ) { const result = await getConnection ( ) . createQueryBuilder ( ) . update ( Post ) . set ( { title , text } ) . where ( 'id = :id and "creatorId" = :creatorId' , { id , creatorId : req . session . userId , } ) . returning ( "*" ) . execute ( ) ; return result . raw [ 0 ] ; } @ Mutation ( ( ) => Boolean ) @ UseMiddleware ( isAuth ) async deletePost ( @ Arg ( "id" , ( ) => Int ) id , @ Ctx ( ) { req } ) { await Post . delete ( { id , creatorId : req . session . userId } ) ; return true ; } }