type-graphql#Resolver TypeScript Examples

The following examples show how to use type-graphql#Resolver. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: plugin-newsletter.resolver.ts    From Cromwell with MIT License 6 votes vote down vote up
@Resolver(PluginNewsletter)
export default class PluginNewsletterResolver {

    @Authorized<TAuthRole>("administrator", 'guest')
    @Query(() => [PluginNewsletter])
    async pluginNewsletterExport(): Promise<PluginNewsletter[]> {
        return await getManager().find(PluginNewsletter);
    }

    /** Restrict via decorator: */
    @Authorized<TAuthRole>("administrator", 'guest')
    @Query(() => String)
    async pluginNewsletterStats(@Ctx() ctx: TGraphQLContext): Promise<string> {
        
        // Or via checking manually user info: (both methods can work independently)
        if (ctx.user?.role !== 'administrator')
            throw new UnauthorizedException('Forbidden');

        return (await getManager().find(PluginNewsletter) ?? []).length + '';
    }
}
Example #2
Source File: resolver.ts    From typegraphql-nestjs with MIT License 6 votes vote down vote up
@Resolver(of => Product)
export default class InventoryResolver {
  @Directive(`@requires(fields: "price weight")`)
  @FieldResolver(returns => Number)
  async shippingEstimate(@Root() product: Product): Promise<number> {
    // free for expensive items
    if (product.price > 1000) {
      return 0;
    }

    // estimate is based on weight
    return product.weight * 0.5;
  }
}
Example #3
Source File: image-resolver.ts    From convoychat with GNU General Public License v3.0 6 votes vote down vote up
@Resolver()
class ImageResolver {
  @UseMiddleware(RateLimit({ limit: 10 }))
  @Mutation(() => UploadImageOutput)
  async uploadImage(
    @Arg("file", () => GraphQLUpload) file: Promise<IUpload>
  ): Promise<UploadImageOutput> {
    const { createReadStream } = await file;
    const fileStream = createReadStream();

    return new Promise((resolve, reject) => {
      const cloudStream = cloudinary.uploader.upload_stream(
        {
          unique_filename: true,
          folder: process.env.MEDIA_FOLDER,
        },
        (err, fileUploaded) => {
          if (err) reject(false);

          resolve({
            url: fileUploaded.secure_url,
            public_id: fileUploaded.public_id,
          });
        }
      );

      fileStream.pipe(cloudStream);
    });
  }
}
Example #4
Source File: CommentStat.resolver.ts    From bouncecode-cms with GNU General Public License v3.0 6 votes vote down vote up
@Resolver()
export class CommentStatResolver {
  @Query(() => CommentStatObject, {nullable: true})
  async commentStat(@Arg('where') where: CommentStatUniqueWhereInput) {
    try {
      const result = await getRepository(CommentStatEntity)
        .createQueryBuilder('commentStat')
        .where('commentStat.postId = :postId', {
          postId: where.postId,
        })
        .getOne();

      return result;
    } catch (e) {
      console.log(e);
      return new ApolloError(e);
    }
  }
}
Example #5
Source File: fields.ts    From backend with MIT License 6 votes vote down vote up
// eslint-disable-next-line camelcase
@Resolver(of => Certificate_of_conduct)
export class ExtendedFieldsCertificateOfConductResolver {
    @FieldResolver(returns => [Student])
    @Authorized(Role.ADMIN)
    // eslint-disable-next-line camelcase
    async student(@Root() certificate: Certificate_of_conduct) {
        return await prisma.student.findUnique({
            where: {
                id: certificate.studentId
            }
        });
    }
}
Example #6
Source File: auth.resolver.ts    From hakka with MIT License 6 votes vote down vote up
@Resolver()
export class AuthResolver {
  @Mutation((returns) => Boolean)
  async signup() {
    // TODO
  }

  @Mutation((returns) => Boolean)
  async login() {
    // TODO
  }
}
Example #7
Source File: request.ts    From opensaas with MIT License 6 votes vote down vote up
@Resolver(of => Request)
export class RequestResolver {
  @Query(() => [Request])
  requests(@Ctx() { tenantId }: Context) {
    return Request.find({ where: { tenantId } });
  }

  @Mutation(() => Request)
  async addRequest(@Ctx() { tenantId }: Context, @Arg('newRequestData') newRequestData: NewRequestInput): Promise<Request> {
    const request = Request.create({
      tenantId,
      ...newRequestData,
    });
    return await request.save();
  }

  @Mutation(() => Boolean)
  async removeRequest(@Ctx() { tenantId }: Context, @Arg('id') id: number) {
    const request = await Request.find({ where: { tenantId, id } });
    await Request.remove(request);
    return true;
  }
}
Example #8
Source File: config.resolver.ts    From liferay-grow with MIT License 6 votes vote down vote up
@Resolver()
export class ConfigResolver {
  @Query(() => Configuration, { name: `getServerInfo` })
  getConfig(): Configuration {
    const { APP_NAME = 'Liferay Grow' } = process.env;

    const { version: SERVER_VERSION } = PKG;

    return {
      SERVER_NAME: APP_NAME,
      SERVER_VERSION,
    };
  }
}
Example #9
Source File: CompanyResolver.ts    From type-graphql-dataloader with MIT License 6 votes vote down vote up
@Resolver((of) => Company)
export default class CompanyResolver {
  @Query((returns) => [Company])
  async companies(): Promise<Company[]> {
    return getRepository(Company).find();
  }

  @FieldResolver()
  @Loader<string, Chair[]>(async (ids) => {
    const chairs = await getRepository(Chair).find({
      where: { company: { id: In([...ids]) } },
    });
    const chairsById = groupBy(chairs, "companyId");
    return ids.map((id) => chairsById[id] ?? []);
  })
  chairs(@Root() root: Company) {
    return (dataloader: DataLoader<string, Chair[]>) =>
      dataloader.load(root.id);
  }
}
Example #10
Source File: resolvers.ts    From squid with GNU General Public License v3.0 6 votes vote down vote up
@Resolver()
export class ScalarResolver {
    constructor(private tx: () => Promise<EntityManager>) {}

    @Query(() => [ScalarRow])
    async scalarsExtension(): Promise<ScalarRow[]>  {
        let em = await this.tx()
        return em.find(Scalar, {
            order: {
                id: 'ASC'
            }
        })
    }
}
Example #11
Source File: info.ts    From Koa-GraphQL-Template with MIT License 6 votes vote down vote up
@Resolver(Info)
export class InfoResolver {
    @Query(() => [Info], { nullable: true, description: '查询信息列表' })
    async infos() {
        return await InfoModal.find({}).sort('-meta.createdAt');
    }

    @Mutation(() => Info)
    async saveInfo(@Arg('data') newInfo: InfoInput) {
        const info = new InfoModal(newInfo);
        return await info.save();
    }

    @Mutation(() => Number, { nullable: true })
    async removeAllInfo() {
        const res = await InfoModal.deleteMany({ weight: { $gte: 0 } });
        return res.deletedCount || 0;
    }
}
Example #12
Source File: product-showcase.resolver.ts    From Cromwell with MIT License 5 votes vote down vote up
@Resolver(ProductCategory)
export default class PluginProductShowcaseResolver {

    private get productRepo() { return getCustomRepository(ProductRepository) }

    @Query(() => PagedProduct)
    async pluginProductShowcase(@Arg("slug", { nullable: true }) slug?: string): Promise<TPagedList<TProduct>> {
        logger.log('ProductShowcaseResolver::productShowcase slug:' + slug);
        const timestamp = Date.now();

        let products: TPagedList<TProduct> = {
            elements: []
        };
        const settings = await getCustomRepository(PluginRepository).getPluginSettings<TSettings>('@cromwell/plugin-product-showcase');
        const maxSize = settings?.size ?? 20;

        if (slug) {
            const product = await this.productRepo.getBySlug(slug, ['categories']);
            if (!product?.id) throw new HttpException('Product with slug ' + slug + ' was not found!', HttpStatus.NOT_FOUND);

            // Gather products from all related categories until reach limit (maxSize)
            for (const category of product.categories ?? []) {
                if (category?.id) {
                    const categoryProducts = await this.productRepo.getProductsFromCategory(category.id, {
                        pageSize: maxSize
                    });
                    if (categoryProducts?.elements && products.elements) {
                        for (const prod of categoryProducts.elements) {

                            // Differnt categories may contain same products, we don't want to duplicate them
                            if (products.elements.some(addedProd => addedProd.id === prod.id)) continue;

                            products.elements.push(prod);

                            if (products.elements.length >= maxSize) break;
                        }

                    }
                }

                if (products.elements?.length &&
                    products.elements?.length >= maxSize) break;
            }

            if (products.elements && products.elements.length < maxSize) {
                (await this.productRepo.getProducts({ pageSize: maxSize }))?.elements?.forEach(prod => {
                    if (products.elements && products.elements.length < maxSize) {
                        products.elements?.push(prod);
                    }
                })
            }
        } else {
            products = await this.productRepo.getProducts({ pageSize: maxSize });
        }

        const timestamp2 = Date.now();
        logger.log('ProductShowcaseResolver::productShowcase time elapsed: ' + (timestamp2 - timestamp) + 'ms');

        return products;
    }
}
Example #13
Source File: resolver.ts    From typegraphql-nestjs with MIT License 5 votes vote down vote up
@Resolver(of => User)
export default class AccountsResolver {
  @Query(returns => User)
  me(): User {
    return users[0];
  }
}
Example #14
Source File: hello.ts    From lireddit with MIT License 5 votes vote down vote up
@Resolver()
export class HelloResolver {
  @Query(() => String)
  hello() {
    return "bye";
  }
}
Example #15
Source File: UserAccount.ts    From Wern-Fullstack-Template with MIT License 5 votes vote down vote up
@Resolver(UserAccount)
export class UserResolver {
  @Query(() => UserAccount, { nullable: true })
  user(@Arg('id') id: string) {
    const user = UserAccount.findOne({ where: { id } })
    if (!user) return null

    return user
  }

  @Query(() => [UserAccount], { nullable: true })
  users() {
    console.log('users query reached')
    const users = UserAccount.find()
    if (!users) return null

    return users
  }

  @Mutation(() => UserResponse)
  async createUser(@Arg('options') options: UserInput): Promise<UserResponse> {
    const errors = validateRegister(options)
    if (errors) {
      return { errors }
    }

    const hashedPassword = await argon2.hash(options.password)
    let user
    try {
      const result = await getConnection()
        .createQueryBuilder()
        .insert()
        .into(UserAccount)
        .values({
          username: options.username,
          email: options.email,
          password: hashedPassword,
        })
        .returning('*')
        .execute()
      user = result.raw[0]
    } catch (err) {
      if (err.code === '23505') {
        return {
          errors: [
            {
              field: 'username',
              message: 'username already taken',
            },
          ],
        }
      }
    }

    return { user }
  }
}
Example #16
Source File: fields.ts    From backend with MIT License 5 votes vote down vote up
@Resolver(of => GraphQLModel.Participation_certificate)
export class ExtendedFieldsParticipationCertificateResolver {
    @FieldResolver(returns => [String])
    @Authorized(Role.ADMIN, Role.STUDENT)
    subjectsFormatted(@Root() certificate: ParticipationCertificate) {
        return certificate.subjects.split(",");
    }
}
Example #17
Source File: author.resolver.ts    From mikro-orm-graphql-example with MIT License 5 votes vote down vote up
@Resolver(() => Author)
export class AuthorResolver {
  @Query(() => [Author])
  public async getAuthors(@Ctx() ctx: MyContext, @Info() info: GraphQLResolveInfo): Promise<Author[]> {
    const relationPaths = fieldsToRelations(info);
    return ctx.em.getRepository(Author).findAll(relationPaths);
  }

  @Query(() => Author, { nullable: true })
  public async getAuthor(
    @Arg('id') id: string,
    @Ctx() ctx: MyContext,
    @Info() info: GraphQLResolveInfo,
  ): Promise<Author | null> {
    const relationPaths = fieldsToRelations(info);
    return ctx.em.getRepository(Author).findOne({ id }, relationPaths);
  }

  @Mutation(() => Author)
  public async addAuthor(@Arg('input') input: AuthorValidator, @Ctx() ctx: MyContext): Promise<Author> {
    const author = new Author(input);
    await ctx.em.persist(author).flush();
    return author;
  }

  @Mutation(() => Author)
  public async updateAuthor(
    @Arg('input') input: AuthorValidator,
    @Arg('id') id: string,
    @Ctx() ctx: MyContext,
    @Info() info: GraphQLResolveInfo,
  ): Promise<Author> {
    const relationPaths = fieldsToRelations(info);
    const author = await ctx.em.getRepository(Author).findOneOrFail({ id }, relationPaths);
    author.assign(input);
    await ctx.em.persist(author).flush();
    return author;
  }

  @Mutation(() => Boolean)
  public async deleteAuthor(@Arg('id') id: string, @Ctx() ctx: MyContext): Promise<boolean> {
    const author = await ctx.em.getRepository(Author).findOneOrFail({ id });
    await ctx.em.getRepository(Author).remove(author).flush();
    return true;
  }
}
Example #18
Source File: notification.resolver.ts    From hakka with MIT License 5 votes vote down vote up
@Resolver((of) => Notification)
export class NotificationResolver {
  @Query((returns) => [Notification])
  async notifications(@GqlContext() ctx: Context) {
    const user = requireAuth(ctx)
    const notifications = await prisma.notification.findMany({
      where: {
        userId: user.id,
      },
      take: 100,
      orderBy: {
        createdAt: 'desc',
      },
    })
    return notifications
  }

  @Query((returns) => Int)
  async notificationsCount(@GqlContext() ctx: Context) {
    const user = requireAuth(ctx)
    const count = await prisma.notification.count({
      where: {
        userId: user.id,
        isRead: null,
      },
    })
    return count
  }

  @FieldResolver((returns) => ResolvedDataUnion)
  async resolvedData(@Root() notification: Notification) {
    const { data } = notification
    if (data.type === 'topic-comment') {
      const comment = await prisma.comment.findUnique({
        where: {
          id: data.commentId,
        },
      })
      return { type: data.type, comment }
    }
    if (data.type === 'comment-reply') {
      const replyComment = await prisma.comment.findUnique({
        where: {
          id: data.replyCommentId,
        },
      })
      return { type: data.type, replyComment }
    }
  }

  @Mutation((returns) => Boolean)
  async markAllNotificationsAsRead(@GqlContext() ctx: Context) {
    const user = requireAuth(ctx)
    await prisma.notification.updateMany({
      where: {
        userId: user.id,
      },
      data: {
        isRead: true,
      },
    })
    return true
  }
}
Example #19
Source File: auth.resolver.ts    From liferay-grow with MIT License 5 votes vote down vote up
@Resolver(Profile)
export class AuthResolver {
  @Mutation(() => String, { name: 'authGithub' })
  async authGithub(@Arg('code') code: string): Promise<string> {
    const githubUser = await getGithubUser(code);

    const {
      avatar_url,
      email,
      id: github_id,
      location,
      login: github_login,
      name,
    } = githubUser;

    const profile = await Profile.findOne({
      relations: ['user', 'user.growMap'],
      where: {
        github_login,
      },
    });

    let token = '';

    if (profile) {
      token = await assignToken(profile);
    } else {
      if (VALIDATE_LIFERAY_ORG) {
        const isLiferayMember = await belongsToLiferayOrg(github_login);

        if (!isLiferayMember) {
          logger.error(`${github_login} is not a liferay member`);

          throw new Error('not-a-liferay-member');
        }
      }

      const newUser = await User.create().save();

      const newProfile = await Profile.create({
        avatar_url,
        email,
        github_id,
        github_login,
        location,
        name,
        user: newUser,
      }).save();

      newUser.profile = newProfile;

      token = await assignToken(newProfile);

      await newUser.save();
    }

    logger.info(`Token generated for ${github_login}`);

    return token;
  }
}
Example #20
Source File: testSchemaTypeGraphql.ts    From ra-data-prisma with MIT License 5 votes vote down vote up
@Resolver(() => User)
class CustomUserResolver {
  @FieldResolver((type) => Address, { nullable: true })
  address(user: User): Address | undefined {
    return user.address as unknown as Address;
  }
}
Example #21
Source File: CertResolver.ts    From type-graphql-dataloader with MIT License 5 votes vote down vote up
@Resolver((of) => Cert)
export default class CertResolver {
  @Query((returns) => [Cert])
  async certs(): Promise<Cert[]> {
    return getRepository(Cert).find();
  }
}
Example #22
Source File: create-generic-entity.ts    From Cromwell with MIT License 4 votes vote down vote up
createGenericEntity = <EntityType, EntityInputType = EntityType>(entityName: string,
    EntityClass: new (...args: any[]) => EntityType,
    InputEntityClass?: new (...args: any[]) => EntityInputType) => {

    @EntityRepository(EntityClass)
    class GenericRepository extends BaseRepository<EntityType, EntityInputType> {
        constructor() {
            super(EntityClass)
        }
    }

    @ObjectType(`Paged${entityName}`)
    class PagedEntity implements TPagedList<EntityType> {
        @Field(() => PagedMeta, { nullable: true })
        pagedMeta?: PagedMeta;

        @Field(() => [EntityClass], { nullable: true })
        elements?: EntityType[];
    }

    @ArgsType()
    class CreateArgs {
        @Field(() => InputEntityClass ?? String)
        data: EntityInputType;
    }

    @ArgsType()
    class UpdateArgs {
        @Field(() => Int)
        id: number;

        @Field(() => InputEntityClass ?? String)
        data: EntityInputType;
    }

    const getPagedPath = GraphQLPaths.Generic.getManyPaged + entityName;
    const getAllPath = GraphQLPaths.Generic.getMany + entityName;
    const getBySlugPath = GraphQLPaths.Generic.getOneBySlug + entityName;
    const getByIdPath = GraphQLPaths.Generic.getOneById + entityName;
    const createPath = GraphQLPaths.Generic.create + entityName;
    const updatePath = GraphQLPaths.Generic.update + entityName;
    const deletePath = GraphQLPaths.Generic.delete + entityName;
    const getFilteredPath = GraphQLPaths.Generic.getFiltered + entityName;

    @Resolver(EntityClass, { isAbstract: true })
    abstract class GenericResolver {

        private repository = getCustomRepository(GenericRepository)

        @Authorized<TAuthRole>("administrator", "guest", "author")
        @Query(() => PagedEntity)
        async [getPagedPath](@Arg("pagedParams") pagedParams: PagedParamsInput<EntityType>):
            Promise<TPagedList<EntityType>> {
            return this.repository.getPaged(pagedParams);
        }

        @Authorized<TAuthRole>("administrator", "guest", "author")
        @Query(() => [EntityClass])
        async [getAllPath](): Promise<EntityType[]> {
            return this.repository.getAll();
        }

        @Authorized<TAuthRole>("administrator", "guest", "author")
        @Query(() => EntityClass)
        async [getBySlugPath](@Arg("slug") slug: string): Promise<EntityType | undefined> {
            return this.repository.getBySlug(slug);
        }

        @Authorized<TAuthRole>("administrator", "guest", "author")
        @Query(() => EntityClass)
        async [getByIdPath](@Arg("id", () => Int) id: number): Promise<EntityType | undefined> {
            return this.repository.getById(id);
        }

        @Authorized<TAuthRole>("administrator", "guest", "author")
        @Query(() => PagedEntity)
        async [getFilteredPath](
            @Arg("pagedParams", () => PagedParamsInput, { nullable: true }) pagedParams?: PagedParamsInput<EntityType>,
            @Arg("filterParams", () => BaseFilterInput, { nullable: true }) filterParams?: BaseFilterInput,
        ): Promise<TPagedList<EntityType> | undefined> {
            return this.repository.getFilteredEntities(pagedParams, filterParams);
        }

        @Authorized<TAuthRole>("administrator")
        @Mutation(() => EntityClass)
        async [createPath](@Args() { data }: CreateArgs): Promise<EntityType> {
            return this.repository.createEntity(data);
        }

        @Authorized<TAuthRole>("administrator")
        @Mutation(() => EntityClass)
        async [updatePath](@Args() { id, data }: UpdateArgs): Promise<EntityType> {
            return this.repository.updateEntity(id, data);
        }

        @Authorized<TAuthRole>("administrator")
        @Mutation(() => Boolean)
        async [deletePath](@Arg("id", () => Int) id: number): Promise<boolean> {
            return this.repository.deleteEntity(id);
        }

    }

    return {
        abstractResolver: GenericResolver as any,
        repository: GenericRepository as TObjectType<BaseRepository<EntityType, EntityInputType>>,
        pagedEntity: PagedEntity as any,
        createArgs: CreateArgs as any,
        updateArgs: UpdateArgs as any,
    }
}
Example #23
Source File: invitation-resolver.ts    From convoychat with GNU General Public License v3.0 4 votes vote down vote up
@Resolver(() => Invitation)
class InvitationResolver {
  @Authorized()
  @Query(() => InvitationDetails)
  async getInvitationInfo(
    @Arg("token", { nullable: false }) token: string,
    @Ctx() context: Context
  ) {
    const invite = await InvitationModel.findOne({
      token: token,
    })
      .populate("roomId")
      .populate("invitedBy");

    if (!invite) {
      throw new ApolloError("Could not get invitation info");
    }

    const userid = new ObjectID(context.currentUser.id);
    const currentUserInvite = userid.equals(invite.userId as ObjectId);
    if (!currentUserInvite && !invite.isPublic) {
      throw new ApolloError("Could not get invitation info");
    }

    return {
      id: invite.id,
      room: invite.roomId,
      invitedBy: invite.invitedBy,
      isPublic: invite.isPublic,
      createdAt: invite.createdAt,
    };
  }

  @Authorized()
  @Mutation(() => InvitationLinkResult)
  async createInvitationLink(
    @Arg("roomId", { nullable: false }) roomId: ObjectID,
    @Ctx() context: Context
  ) {
    const existingInvitation = await InvitationModel.findOne({
      roomId: roomId,
      invitedBy: context.currentUser.id,
      isPublic: true,
    });

    const baseURL =
      process.env.NODE_ENV !== "production"
        ? "http://localhost:3000"
        : "https://convoychat.herokuapp.com";

    if (existingInvitation) {
      return {
        link: `${baseURL}/invitation/${existingInvitation.token}`,
      };
    }

    const validRoom = await RoomModel.findOne({ _id: roomId });
    if (!validRoom) throw new Error("Not a valid room");

    // TODO: add expiry time in invitation token
    const token = crypto.randomBytes(16).toString("hex");
    const invite = new InvitationModel({
      isPublic: true,
      roomId: roomId,
      invitedBy: context.currentUser.id,
      token: token,
    });

    await invite.save();

    return { link: `${baseURL}/invitation/${token}` };
  }

  @Authorized()
  @UseMiddleware(RateLimit({ limit: 15 }))
  @Mutation(() => [Invitation])
  async inviteMembers(
    @Args() { roomId, members }: inviteMembersArgs,
    @Ctx() context: Context
  ) {
    try {
      // check if user is a memeber of the specified room
      const user = await UserModel.findOne({
        _id: context.currentUser.id,
        rooms: { $in: [roomId] },
      });

      if (!user) {
        throw new ApolloError(
          "You are not a member of room, Cannot invite members"
        );
      }

      let token = null;

      // create invitations
      const invitations = members.map(memberId => {
        token = crypto.randomBytes(16).toString("hex");
        const invite = new InvitationModel({
          roomId: roomId,
          userId: memberId,
          invitedBy: context.currentUser.id,
          token: token,
        });

        return invite.save();
      });

      // @ts-ignore
      const savedInvites: Invitation[] = await Promise.all(invitations);

      const foundRoom = await RoomModel.findOne({ _id: roomId });

      // send notification
      const notifications = members.map(async (id, index) => {
        return sendNotification({
          context: context,
          sender: context.currentUser.id,
          receiver: id,
          type: NOTIFICATION_TYPE.INVITATION,
          payload: {
            userId: id,
            roomName: foundRoom.name,
            roomId: roomId,
            invitedBy: context.currentUser.id,
            token: savedInvites[index].token,
          },
        });
      });

      await Promise.all(notifications);

      // TODO: Send Email invitations

      return savedInvites;
    } catch (err) {
      console.log(err);
      throw new ApolloError(err);
    }
  }

  @Authorized()
  @Mutation(() => Boolean)
  async acceptInvitation(
    @Arg("token", { nullable: false }) token: string,
    @Ctx() context: Context
  ) {
    // find invitation with token & userId
    const invitation = await InvitationModel.findOne({
      token: token,
    });

    if (!invitation) throw new ApolloError("Invalid Invitation");

    let userToAdd: Ref<User> = null;
    // if invitation is public add the current user
    if (invitation.isPublic === true) {
      userToAdd = context.currentUser.id;
    }

    // if invitation is not public add the invitation.userId
    if (
      invitation.isPublic === false &&
      `${invitation.userId}` === `${context.currentUser.id}`
    ) {
      userToAdd = invitation.userId;
    }

    if (!userToAdd) {
      throw new ApolloError("Something went wrong while accepting invite");
    }

    // throw error and delete the invitation if maximum uses is reached
    if (invitation.uses.length >= invitation.maxUses) {
      await InvitationModel.findOneAndRemove({
        token: token,
      });
      throw new ApolloError("Maximum invitation usage limit exceeded");
    }

    // add user to the room
    const room = await RoomModel.findOneAndUpdate(
      { _id: invitation.roomId },
      { $addToSet: { members: userToAdd } },
      { new: true }
    );

    if (!room) throw new ApolloError("Could not add members to room");

    // update user.rooms
    await UserModel.update(
      { _id: userToAdd },
      { $addToSet: { rooms: room.id } },
      { new: true }
    );

    // delete the notification
    if (!invitation.isPublic) {
      await InvitationModel.findOneAndRemove({ token: token });
    } else {
      await InvitationModel.findOneAndUpdate(
        { token: token },
        { $addToSet: { uses: userToAdd } }
      );
    }

    return true;
  }
}
Example #24
Source File: post.ts    From lireddit with MIT License 4 votes vote down vote up
@Resolver(Post)
export class PostResolver {
  @FieldResolver(() => String)
  textSnippet(@Root() post: Post) {
    return post.text.slice(0, 50);
  }

  @FieldResolver(() => User)
  creator(@Root() post: Post, @Ctx() { userLoader }: MyContext) {
    return userLoader.load(post.creatorId);
  }

  @FieldResolver(() => Int, { nullable: true })
  async voteStatus(
    @Root() post: Post,
    @Ctx() { updootLoader, req }: MyContext
  ) {
    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: number,
    @Arg("value", () => Int) value: number,
    @Ctx() { req }: MyContext
  ) {
    const isUpdoot = value !== -1;
    const realValue = isUpdoot ? 1 : -1;
    const { userId } = req.session;

    const updoot = await Updoot.findOne({ where: { postId, userId } });

    // the user has voted on the post before
    // and they are changing their vote
    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) {
      // has never voted before
      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: number,
    @Arg("cursor", () => String, { nullable: true }) cursor: string | null
  ): Promise<PaginatedPosts> {
    // 20 -> 21
    const realLimit = Math.min(50, limit);
    const reaLimitPlusOne = realLimit + 1;

    const replacements: any[] = [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
    );

    // const qb = getConnection()
    //   .getRepository(Post)
    //   .createQueryBuilder("p")
    //   .innerJoinAndSelect("p.creator", "u", 'u.id = p."creatorId"')
    //   .orderBy('p."createdAt"', "DESC")
    //   .take(reaLimitPlusOne);

    // if (cursor) {
    //   qb.where('p."createdAt" < :cursor', {
    //     cursor: new Date(parseInt(cursor)),
    //   });
    // }

    // const posts = await qb.getMany();
    // console.log("posts: ", posts);

    return {
      posts: posts.slice(0, realLimit),
      hasMore: posts.length === reaLimitPlusOne,
    };
  }

  @Query(() => Post, { nullable: true })
  post(@Arg("id", () => Int) id: number): Promise<Post | undefined> {
    return Post.findOne(id);
  }

  @Mutation(() => Post)
  @UseMiddleware(isAuth)
  async createPost(
    @Arg("input") input: PostInput,
    @Ctx() { req }: MyContext
  ): Promise<Post> {
    return Post.create({
      ...input,
      creatorId: req.session.userId,
    }).save();
  }

  @Mutation(() => Post, { nullable: true })
  @UseMiddleware(isAuth)
  async updatePost(
    @Arg("id", () => Int) id: number,
    @Arg("title") title: string,
    @Arg("text") text: string,
    @Ctx() { req }: MyContext
  ): Promise<Post | null> {
    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: number,
    @Ctx() { req }: MyContext
  ): Promise<boolean> {
    // not cascade way
    // const post = await Post.findOne(id);
    // if (!post) {
    //   return false;
    // }
    // if (post.creatorId !== req.session.userId) {
    //   throw new Error("not authorized");
    // }

    // await Updoot.delete({ postId: id });
    // await Post.delete({ id });

    await Post.delete({ id, creatorId: req.session.userId });
    return true;
  }
}
Example #25
Source File: ChatRoom.resolver.ts    From bouncecode-cms with GNU General Public License v3.0 4 votes vote down vote up
@Resolver()
export class ChatRoomResolver {
  @Query(() => [ChatRoomObject])
  async chatRooms(
    @Arg('where') where: ChatRoomWhereInput,
    @Arg('skip', {nullable: true}) skip: number = 0,
    @Arg('take', {nullable: true}) take: number = 10,
    @Ctx() ctx: Context,
  ) {
    const userId = ctx.user.id;

    const queryBuilder = getRepository(UserEntity)
      .createQueryBuilder('user')
      .leftJoinAndSelect('user.chatRooms', 'chatRooms')
      .andWhere('user.id = :userId', {
        userId: userId,
      });

    if (where.category) {
      queryBuilder
        .leftJoinAndSelect('chatRooms.category', 'category')
        .andWhere('category.id = :categoryId', {
          categoryId: where.category,
        });
    }

    const result = await queryBuilder
      .offset(skip)
      .limit(take)
      .getMany();

    console.log(result);

    return result;
  }

  @Mutation(() => Boolean)
  async createChatRoom(@Arg('data') data: ChatRoomCreateInput) {
    const queryBuilder = await getConnection()
      .createQueryBuilder()
      .insert()
      .into(ChatRoomEntity)
      .values([data])
      .execute();

    console.log(queryBuilder);

    return true;
  }

  @Mutation(() => Boolean)
  async updateChatRoom(
    @Arg('where') where: ChatRoomUniqueWhereInput,
    @Arg('data') data: ChatRoomUpdateInput,
  ) {
    const queryBuilder = await getConnection()
      .createQueryBuilder()
      .update(ChatRoomEntity)
      .set(data)
      .where('id = :id', {id: where.id})
      .execute();

    console.log(queryBuilder);

    return true;
  }

  @Mutation(() => Boolean)
  async deleteChatRoom(@Arg('where') where: ChatRoomUniqueWhereInput) {
    const queryBuilder = await getConnection()
      .createQueryBuilder()
      .delete()
      .from(ChatRoomEntity)
      .where('id = :id', {id: where.id})
      .execute();

    console.log(queryBuilder);

    return true;
  }
}
Example #26
Source File: authentication.ts    From backend with MIT License 4 votes vote down vote up
@Resolver(of => Me)
export class AuthenticationResolver {
    @Authorized(Role.UNAUTHENTICATED)
    @Mutation(returns => Boolean)
    @Deprecated("use loginPassword or loginToken instead")
    async loginLegacy(@Ctx() context: GraphQLContext, @Arg("authToken") authToken: string) {
        ensureSession(context);
        const logger = logInContext(`GraphQL Authentication`, context);

        const pupil = await prisma.pupil.findFirst({
            where: {
                // This drops support for unhashed tokens as present in the REST authentication
                authToken: hashToken(authToken),
                active: true
            }
        });

        if (pupil) {
            if (!pupil.verifiedAt) {
                /* Previously there was an extra database field for verifying the E-Mail.
                   I do not see the purpose of that, as presenting a valid authToken is also proof that the account exists.
                   This can co-exist with the current "verification" implementation.
                   TODO: Drop the verification column once we moved to GraphQL on the frontend */
                logger.info(`Pupil(${pupil.id}) did not verify their e-mail yet, but presented legacy token (thus proved their ownership)`);
                await prisma.pupil.update({
                    data: {
                        verification: null,
                        verifiedAt: new Date()
                    },
                    where: { id: pupil.id }
                });
            }

            await loginAsUser(userForPupil(pupil), context);

            return true;
        }

        const student = await prisma.student.findFirst({
            where: {
                authToken: hashToken(authToken),
                active: true
            }
        });

        if (student) {
            if (!student.verifiedAt) {
                /* Previously there was an extra database field for verifying the E-Mail.
                   I do not see the purpose of that, as presenting a valid authToken is also proof that the account exists.
                   This can co-exist with the current "verification" implementation.
                   TODO: Drop the verification column once we moved to GraphQL on the frontend */
                logger.info(`Student(${student.id}) did not verify their e-mail yet, but presented legacy token (thus proved their ownership)`);
                await prisma.student.update({
                    data: {
                        verification: null,
                        verifiedAt: new Date()
                    },
                    where: { id: student.id }
                });
            }

            await loginAsUser(userForStudent(student), context);

            return true;
        }

        logger.warn(`Invalid authToken`);
        throw new AuthenticationError("Invalid authToken");
    }

    @Authorized(Role.UNAUTHENTICATED)
    @Mutation(returns => Boolean)
    @Deprecated("Use loginPassword instead")
    async loginPasswordLegacy(@Ctx() context: GraphQLContext, @Arg("email") email: string, @Arg("password") password: string) {
        ensureSession(context);
        const logger = logInContext(`GraphQL Authentication`, context);

        const screener = await prisma.screener.findFirst({
            where: {
                email,
                active: true
            }
        });

        const passwordValid = screener && await verifyPassword(password, screener.password);

        if (!screener || !passwordValid) {
            logger.warn(`Invalid email (${email}) or password`);
            throw new AuthenticationError("Invalid email or password");
        }

        await loginAsUser(userForScreener(screener), context);

        return true;
    }

    @Authorized(Role.UNAUTHENTICATED)
    @Mutation(returns => Boolean)
    async loginPassword(@Ctx() context: GraphQLContext, @Arg("email") email: string, @Arg("password") password: string) {
        try {
            const user = await loginPassword(email, password);
            await loginAsUser(user, context);
            return true;
        } catch (error) {
            throw new AuthenticationError("Invalid E-Mail or Password");
        }
    }

    @Authorized(Role.UNAUTHENTICATED)
    @Mutation(returns => Boolean)
    async loginToken(@Ctx() context: GraphQLContext, @Arg("token") token: string) {
        try {
            const user = await loginToken(token);
            await loginAsUser(user, context);
            return true;
        } catch (error) {
            throw new AuthenticationError("Invalid Token");
        }
    }


    @Authorized(Role.USER)
    @Mutation(returns => Boolean)
    logout(@Ctx() context: GraphQLContext) {
        ensureSession(context);
        const logger = logInContext(`GraphQL Authentication`, context);


        if (!context.user) {
            throw new ForbiddenError("User already logged out");
        }

        const deleted = userSessions.delete(context.sessionToken);
        assert(deleted, "User session is successfully deleted");

        context.user = undefined;
        logger.info(`Successfully logged out`);

        return true;
    }

}
Example #27
Source File: comment.resolver.ts    From hakka with MIT License 4 votes vote down vote up
@Resolver((of) => Comment)
export class CommentResolver {
  @Query((returns) => CommentsConnection)
  async comments(@Args() args: CommentsArgs) {
    const skip = (args.page - 1) * args.take

    const comments = await prisma.comment.findMany({
      where: {
        topicId: args.topicId,
      },
      take: args.take + 1,
      skip,
      orderBy: {
        createdAt: args.order,
      },
    })
    const count = await prisma.comment.count({
      where: {
        topicId: args.topicId,
      },
    })

    return {
      items: comments.slice(0, args.take),
      hasNext: comments.length > args.take,
      hasPrev: args.page > 1,
      total: count,
    }
  }

  @Mutation((returns) => Comment)
  async createComment(
    @GqlContext() ctx: Context,
    @Args() args: CreateCommentArgs,
  ) {
    const user = requireAuth(ctx)
    const comment = await prisma.comment.create({
      data: {
        topic: {
          connect: {
            id: args.topicId,
          },
        },
        content: args.content,
        author: {
          connect: {
            id: user.id,
          },
        },
        parent: args.parentId
          ? {
              connect: {
                id: args.parentId,
              },
            }
          : undefined,
      },
    })

    await prisma.topic.update({
      where: {
        id: args.topicId,
      },
      data: {
        lastComment: {
          connect: {
            id: comment.id,
          },
        },
      },
    })

    // Add notification
    notificationQueue.add({ commentId: comment.id })

    return comment
  }

  @FieldResolver((returns) => String)
  html(@Root() comment: Comment) {
    const html = renderMarkdown(comment.content)
    return html
  }

  @FieldResolver((returns) => UserPublicInfo)
  async author(@Root() comment: Comment) {
    const author = await prisma.user.findUnique({
      where: {
        id: comment.authorId,
      },
    })
    return author
  }

  @FieldResolver((returns) => Comment, {
    nullable: true,
  })
  async parent(@Root() comment: Comment) {
    const parentComment =
      comment.parentId &&
      (await prisma.comment.findUnique({
        where: {
          id: comment.parentId,
        },
      }))
    return parentComment
  }

  @FieldResolver((returns) => Topic)
  async topic(@Root() comment: Comment) {
    const topic = await prisma.topic.findUnique({
      where: {
        id: comment.topicId,
      },
    })
    return topic
  }

  @FieldResolver((returns) => Int)
  async likesCount(@Root() comment: Comment) {
    const count = await prisma.userCommentLike.count({
      where: {
        commentId: comment.id,
      },
    })
    return count
  }

  @FieldResolver((returns) => Boolean)
  async isLiked(@GqlContext() ctx: Context, @Root() comment: Comment) {
    if (!ctx.user) {
      return false
    }

    const record = await prisma.userCommentLike.findFirst({
      where: {
        userId: ctx.user.id,
        commentId: comment.id,
      },
    })
    return Boolean(record)
  }

  @Mutation((returns) => Boolean)
  async likeComment(@GqlContext() ctx: Context, @Args() args: LikeCommentArgs) {
    const user = requireAuth(ctx)
    const comment = await prisma.comment.findUnique({
      where: {
        id: args.commentId,
      },
    })
    if (!comment) throw new ApolloError(`comment was not found`)
    let userCommentLike = await prisma.userCommentLike.findFirst({
      where: {
        commentId: comment.id,
        userId: user.id,
      },
    })
    let liked = false
    if (userCommentLike) {
      await prisma.userCommentLike.delete({
        where: {
          id: userCommentLike.id,
        },
      })
    } else {
      userCommentLike = await prisma.userCommentLike.create({
        data: {
          commentId: comment.id,
          userId: user.id,
        },
      })
      liked = true
    }
    return liked
  }
}