import { AuthenticationError } from 'apollo-server-express'; import { isUUID } from 'class-validator'; import { Arg, Authorized, Ctx, Mutation, Query, Resolver } from 'type-graphql'; import { getRepository, In } from 'typeorm'; import { ContextType, UserStatus } from '@/types'; import { Page, ProductList, User } from '@/entities'; import { connectionFromQuery } from '../shared/pagination/utils'; import { CreateProductListInput, CreateProductListPayload, ProductListConnection, ProductListInput, UpdatePagesInListInput, UpdatePagesInListPayload, UpdateProductListInput, UpdateProductListPayload, } from './types'; import { checkEditListPermission } from './utils'; @Resolver(ProductList) export class ProductListResolver { @Authorized([UserStatus.ANONYMOUS]) @Query(() => ProductListConnection, { nullable: true }) async productLists( @Ctx() { user }: ContextType, @Arg('first', { nullable: true }) first?: number, @Arg('last', { nullable: true }) last?: number, @Arg('before', { nullable: true }) before?: string, @Arg('after', { nullable: true }) after?: string ): Promise { if (!user) { throw new AuthenticationError( 'You must be signed in to get product lists.' ); } const query = getRepository(ProductList) .createQueryBuilder('ProductList') .leftJoinAndSelect('ProductList.collaborators', 'collaborators') .leftJoinAndSelect('ProductList.createdBy', 'user') .where({ createdBy: { id: user.id, }, }) .orWhere('collaborators.id = :userId', { userId: user.id }); return connectionFromQuery({ first, last, before, after }, query); } @Query(() => ProductList, { nullable: true }) async productList( @Arg('input') { id }: ProductListInput ): Promise { return ProductList.findOneOrFail(id); } @Authorized([UserStatus.ANONYMOUS]) @Mutation(() => CreateProductListPayload) async createProductList( @Arg('input') { pageIds, collaboratorIds }: CreateProductListInput, @Ctx() { user }: ContextType ): Promise { if (!user) { throw new AuthenticationError( 'You must be signed in to create a product list.' ); } let list: ProductList = ProductList.create({ createdBy: user }); list = await this.updateListFieldsAndSave( list, { pageIds, collaboratorIds }, user ); return { productList: list, }; } @Authorized([UserStatus.ANONYMOUS]) @Mutation(() => UpdateProductListPayload) async updateProductList( @Arg('input') { id, pageIds, collaboratorIds }: UpdateProductListInput, @Ctx() { user }: ContextType ): Promise { if (!user) { throw new AuthenticationError( 'You must be signed in to update a product list.' ); } let list = await ProductList.findOneOrFail(id); list = await this.updateListFieldsAndSave( list, { pageIds, collaboratorIds }, user ); return { productList: list, }; } @Authorized([UserStatus.ANONYMOUS]) @Mutation(() => UpdatePagesInListPayload) async updatePagesInList( @Arg('input') { listId, pageIdsToAdd, pageIdsToRemove }: UpdatePagesInListInput, @Ctx() { user }: ContextType ): Promise { if (!user) { throw new AuthenticationError( 'You must be signed in to update pages in list.' ); } let list: ProductList = (listId ? await ProductList.findOneOrFail(listId) : await ProductList.findOne({ where: { createdBy: user }, order: { createdAt: 'DESC' }, })) || ProductList.create({ createdBy: user, pages: Promise.resolve([]) }); const pageIds = (await list.pages) .filter((p) => !pageIdsToRemove?.includes(p.firebaseId)) // @TODO remove hack of using firebaseId .map((p) => p.id); pageIds.push(...(pageIdsToAdd || [])); list = await this.updateListFieldsAndSave( list, { pageIds: pageIds.filter((id) => !(pageIdsToRemove || []).includes(id)), }, user ); return { productList: list, }; } private async updateListFieldsAndSave( list: ProductList, { pageIds, collaboratorIds }: Partial, authenticatedUser: User ): Promise { await checkEditListPermission(list, authenticatedUser); if (pageIds) { list.pages = Promise.resolve( await Page.find({ where: [ { id: In(pageIds.filter((id) => isUUID(id))), }, { firebaseId: In(pageIds), // @TODO remove hack of using firebaseId }, ], }) ); } // only owners can edit collaborators if (list.createdBy.id === authenticatedUser.id && collaboratorIds) { list.collaborators = Promise.resolve( await User.findByIds(collaboratorIds) ); } return list.save(); } }