File

src/workspace/workspace-user-lists.service.ts

Index

Methods

Constructor

constructor(workspaceUserListRepository: Repository<DbWorkspaceUserLists>, workspaceService: WorkspaceService, userListService: UserListService)
Parameters :
Name Type Optional
workspaceUserListRepository Repository<DbWorkspaceUserLists> No
workspaceService WorkspaceService No
userListService UserListService No

Methods

Async addWorkspaceUserList
addWorkspaceUserList(dto: CreateUserListDto, id: string, userId: number)
Parameters :
Name Type Optional
dto CreateUserListDto No
id string No
userId number No
Async addWorkspaceUserListContributors
addWorkspaceUserListContributors(dto: CollaboratorsDto, id: string, userListId: string, userId: number)
Parameters :
Name Type Optional
dto CollaboratorsDto No
id string No
userListId string No
userId number No
baseQueryBuilder
baseQueryBuilder()
Async deleteWorkspaceUserList
deleteWorkspaceUserList(id: string, userListId: string, userId: number)
Parameters :
Name Type Optional
id string No
userListId string No
userId number No
Returns : any
Async deleteWorkspaceUserListContributor
deleteWorkspaceUserListContributor(id: string, userListId: string, userListContributorId: string, userId: number)
Parameters :
Name Type Optional
id string No
userListId string No
userListContributorId string No
userId number No
Returns : any
Async findAllUserListsByWorkspaceIdForUserId
findAllUserListsByWorkspaceIdForUserId(pageOptionsDto: PageOptionsDto, id: string, userId: number | undefined)
Parameters :
Name Type Optional
pageOptionsDto PageOptionsDto No
id string No
userId number | undefined No
Async findOneUserListByWorkspaceeIdForUserId
findOneUserListByWorkspaceeIdForUserId(id: string, userListId: string, userId: number | undefined)
Parameters :
Name Type Optional
id string No
userListId string No
userId number | undefined No
Private Async findOneUserListByWorkspaceIdListIdUnguarded
findOneUserListByWorkspaceIdListIdUnguarded(id: string, userListId: string)
Parameters :
Name Type Optional
id string No
userListId string No
Async findWorkspaceUserListContributorHightlightedRepos
findWorkspaceUserListContributorHightlightedRepos(pageOptionsDto: PageOptionsDto, id: string, userListId: string, userId: number | undefined)
Parameters :
Name Type Optional
pageOptionsDto PageOptionsDto No
id string No
userListId string No
userId number | undefined No
Async findWorkspaceUserListContributorHightlights
findWorkspaceUserListContributorHightlights(pageOptionsDto: HighlightOptionsDto, id: string, userListId: string, userId: number | undefined)
Parameters :
Name Type Optional
pageOptionsDto HighlightOptionsDto No
id string No
userListId string No
userId number | undefined No
Async findWorkspaceUserListContributors
findWorkspaceUserListContributors(pageOptionsDto: FilterListContributorsDto, id: string, userListId: string, userId: number | undefined)
Parameters :
Name Type Optional
pageOptionsDto FilterListContributorsDto No
id string No
userListId string No
userId number | undefined No
Async moveWorkspaceUserList
moveWorkspaceUserList(dto: MoveWorkspaceUserListDto, homeWorkspaceId: string, newWorkspaceId: string, userId: number)
Parameters :
Name Type Optional
dto MoveWorkspaceUserListDto No
homeWorkspaceId string No
newWorkspaceId string No
userId number No
Async updateWorkspaceUserList
updateWorkspaceUserList(dto: UpdateUserListDto, id: string, userListId: string, userId: number)
Parameters :
Name Type Optional
dto UpdateUserListDto No
id string No
userListId string No
userId number No
import { BadRequestException, Injectable, NotFoundException, UnauthorizedException } from "@nestjs/common";
import { Repository, SelectQueryBuilder } from "typeorm";
import { InjectRepository } from "@nestjs/typeorm";

import { DbUserHighlight } from "../user/entities/user-highlight.entity";
import { DbUserHighlightRepo } from "../highlight/entities/user-highlight-repo.entity";
import { CreateUserListDto } from "../user-lists/dtos/create-user-list.dto";
import { PageMetaDto } from "../common/dtos/page-meta.dto";
import { PageDto } from "../common/dtos/page.dto";
import { PageOptionsDto } from "../common/dtos/page-options.dto";
import { DbUserList } from "../user-lists/entities/user-list.entity";
import { UserListService } from "../user-lists/user-list.service";
import { UpdateUserListDto } from "../user-lists/dtos/update-user-list.dto";
import { FilterListContributorsDto } from "../user-lists/dtos/filter-contributors.dto";
import { DbUserListContributor } from "../user-lists/entities/user-list-contributor.entity";
import { CollaboratorsDto } from "../user-lists/dtos/collaborators.dto";
import { HighlightOptionsDto } from "../highlight/dtos/highlight-options.dto";
import { MoveWorkspaceUserListDto } from "./dtos/move-workspace-list.dto";
import { DbWorkspaceUserLists } from "./entities/workspace-user-list.entity";
import { canUserEditWorkspace, canUserViewWorkspace } from "./common/memberAccess";
import { WorkspaceService } from "./workspace.service";

@Injectable()
export class WorkspaceUserListsService {
  constructor(
    @InjectRepository(DbWorkspaceUserLists, "ApiConnection")
    private workspaceUserListRepository: Repository<DbWorkspaceUserLists>,
    private workspaceService: WorkspaceService,
    private userListService: UserListService
  ) {}

  baseQueryBuilder(): SelectQueryBuilder<DbWorkspaceUserLists> {
    const builder = this.workspaceUserListRepository.createQueryBuilder("workspace_user_lists");

    return builder;
  }

  async findAllUserListsByWorkspaceIdForUserId(
    pageOptionsDto: PageOptionsDto,
    id: string,
    userId: number | undefined
  ): Promise<PageDto<DbUserList>> {
    const workspace = await this.workspaceService.findOneById(id);

    /*
     * viewers, editors, and owners can see userLists that belong to a workspace
     */

    const canView = canUserViewWorkspace(workspace, userId);

    if (!canView) {
      throw new NotFoundException();
    }

    const queryBuilder = this.baseQueryBuilder()
      .leftJoinAndSelect("workspace_user_lists.user_list", "workspace_user_lists_user_list")
      .leftJoinAndSelect("workspace_user_lists_user_list.contributors", "workspace_user_lists_user_list_contributors")
      .where("workspace_user_lists.workspace_id = :id", { id })
      .orderBy("workspace_user_lists_user_list.updated_at", "DESC")
      .skip(pageOptionsDto.skip)
      .take(pageOptionsDto.limit);

    const itemCount = await queryBuilder.getCount();
    const entities: DbUserList[] = await queryBuilder
      .getMany()
      .then((results) => results.map((result) => result.user_list));

    const pageMetaDto = new PageMetaDto({ itemCount, pageOptionsDto });

    return new PageDto(entities, pageMetaDto);
  }

  private async findOneUserListByWorkspaceIdListIdUnguarded(id: string, userListId: string): Promise<DbUserList> {
    const userList: DbUserList | undefined = await this.baseQueryBuilder()
      .leftJoinAndSelect("workspace_user_lists.user_list", "workspace_user_lists_user_list")
      .leftJoinAndSelect(
        `workspace_user_lists_user_list.workspaces`,
        `workspace_user_lists_workspace`,
        `workspace_user_lists_user_list.id = workspace_user_lists.user_list_id`
      )
      .where("workspace_user_lists.workspace_id = :id", { id })
      .andWhere("workspace_user_lists.user_list_id = :userListId", { userListId })
      .getOne()
      .then((result) => (result ? result.user_list : undefined));

    if (!userList) {
      throw new NotFoundException();
    }

    return userList;
  }

  async findOneUserListByWorkspaceeIdForUserId(
    id: string,
    userListId: string,
    userId: number | undefined
  ): Promise<DbUserList> {
    const workspace = await this.workspaceService.findOneById(id);

    /*
     * viewers, editors, and owners can see userLists that belong to a workspace
     */

    const canView = canUserViewWorkspace(workspace, userId);

    if (!canView) {
      throw new NotFoundException();
    }

    return this.findOneUserListByWorkspaceIdListIdUnguarded(id, userListId);
  }

  async findWorkspaceUserListContributors(
    pageOptionsDto: FilterListContributorsDto,
    id: string,
    userListId: string,
    userId: number | undefined
  ): Promise<PageDto<DbUserListContributor>> {
    const workspace = await this.workspaceService.findOneById(id);

    /*
     * viewers, editors, and owners can see userLists contributors within a workspace
     */

    const canView = canUserViewWorkspace(workspace, userId);

    if (!canView) {
      throw new NotFoundException();
    }

    const userList = await this.findOneUserListByWorkspaceIdListIdUnguarded(id, userListId);

    return this.userListService.findContributorsByListId(pageOptionsDto, userList.id);
  }

  async findWorkspaceUserListContributorHightlights(
    pageOptionsDto: HighlightOptionsDto,
    id: string,
    userListId: string,
    userId: number | undefined
  ): Promise<PageDto<DbUserHighlight>> {
    const workspace = await this.workspaceService.findOneById(id);

    /*
     * viewers, editors, and owners can see userLists contributors highlights within a workspace
     */

    const canView = canUserViewWorkspace(workspace, userId);

    if (!canView) {
      throw new NotFoundException();
    }

    const userList = await this.findOneUserListByWorkspaceIdListIdUnguarded(id, userListId);

    return this.userListService.findListContributorsHighlights(pageOptionsDto, userList.id);
  }

  async findWorkspaceUserListContributorHightlightedRepos(
    pageOptionsDto: PageOptionsDto,
    id: string,
    userListId: string,
    userId: number | undefined
  ): Promise<PageDto<DbUserHighlightRepo>> {
    const workspace = await this.workspaceService.findOneById(id);

    /*
     * viewers, editors, and owners can see userLists contributors highlighted repos within a workspace
     */

    const canView = canUserViewWorkspace(workspace, userId);

    if (!canView) {
      throw new NotFoundException();
    }

    const userList = await this.findOneUserListByWorkspaceIdListIdUnguarded(id, userListId);

    return this.userListService.findListContributorsHighlightedRepos(pageOptionsDto, userList.id);
  }

  async addWorkspaceUserList(dto: CreateUserListDto, id: string, userId: number): Promise<DbWorkspaceUserLists> {
    const workspace = await this.workspaceService.findOneById(id);

    /*
     * owners and editors can add workspace userList pages
     */

    const canEdit = canUserEditWorkspace(workspace, userId);

    if (!canEdit) {
      throw new UnauthorizedException();
    }

    const newUserList = await this.userListService.addUserList(userId, dto, id);

    const listContributors = dto.contributors.map(async (contributor) =>
      this.userListService.addUserListContributor(newUserList.id, contributor.id, contributor.login)
    );

    await Promise.allSettled(listContributors);

    const queryBuilder = this.baseQueryBuilder();

    queryBuilder.where("workspace_user_lists.user_list_id = :id", { id: newUserList.id });

    const item: DbWorkspaceUserLists | null = await queryBuilder.getOne();

    if (!item) {
      throw new NotFoundException("newly created workspace was not found");
    }

    return item;
  }

  async addWorkspaceUserListContributors(
    dto: CollaboratorsDto,
    id: string,
    userListId: string,
    userId: number
  ): Promise<DbUserListContributor[]> {
    const workspace = await this.workspaceService.findOneById(id);

    /*
     * owners and editors can add workspace userList pages
     */

    const canEdit = canUserEditWorkspace(workspace, userId);

    if (!canEdit) {
      throw new UnauthorizedException();
    }

    const userList = await this.findOneUserListByWorkspaceIdListIdUnguarded(id, userListId);

    const contributors = dto.contributors.map(async (contributor) =>
      this.userListService.addUserListContributor(userList.id, contributor.id, contributor.login)
    );

    return Promise.all(contributors);
  }

  async moveWorkspaceUserList(
    dto: MoveWorkspaceUserListDto,
    homeWorkspaceId: string,
    newWorkspaceId: string,
    userId: number
  ): Promise<DbWorkspaceUserLists> {
    /*
     * owners and editors can move workspace user list they are members of.
     * note: the given user must be an owner / editor of BOTH workspaces to move it.
     */

    const homeWorkspace = await this.workspaceService.findOneById(homeWorkspaceId);
    const canEditHomeWorkspace = canUserEditWorkspace(homeWorkspace, userId);

    if (!canEditHomeWorkspace) {
      throw new UnauthorizedException();
    }

    const workspaceUserList = await this.workspaceUserListRepository.findOne({
      where: {
        user_list_id: dto.id,
      },
    });

    if (!workspaceUserList) {
      throw new NotFoundException(`workspace user list with user list id ${dto.id} not found`);
    }

    const newWorkspace = await this.workspaceService.findOneById(newWorkspaceId);
    const canEditNewWorkspace = canUserEditWorkspace(newWorkspace, userId);

    if (!canEditNewWorkspace) {
      throw new UnauthorizedException();
    }

    await this.workspaceUserListRepository.update(workspaceUserList.id, {
      workspace: newWorkspace,
    });

    const queryBuilder = this.baseQueryBuilder();

    queryBuilder.where("workspace_user_lists.user_list_id = :id", { id: dto.id });

    const item: DbWorkspaceUserLists | null = await queryBuilder.getOne();

    if (!item) {
      throw new NotFoundException("newly moved workspace user list was not found");
    }

    return item;
  }

  async updateWorkspaceUserList(
    dto: UpdateUserListDto,
    id: string,
    userListId: string,
    userId: number
  ): Promise<DbUserList> {
    const workspace = await this.workspaceService.findOneById(id);

    /*
     * owners and editors can modify a workspace userList page
     */

    const canEdit = canUserEditWorkspace(workspace, userId);

    if (!canEdit) {
      throw new UnauthorizedException();
    }

    const userList = await this.findOneUserListByWorkspaceIdListIdUnguarded(id, userListId);

    await this.userListService.updateUserList(userList.id, {
      name: dto.name,
    });

    try {
      // 1. Add all contributors from DTO
      await Promise.allSettled(
        dto.contributors.map(async (contributor) =>
          this.userListService.addUserListContributor(userListId, contributor.id, contributor.login)
        )
      );

      // get current contributors for the list (including old/new ones)
      const currentContributors = await this.userListService.findAllContributorsByListId(userListId);

      // 2. Create a set of all unique identifiers from the provided list
      const providedIdentifiers = new Set(
        dto.contributors.flatMap((contributor) => [contributor.id, contributor.login].filter(Boolean))
      );

      // 3. Determine the list of contributors to remove using set operations
      const contributorsToRemove = currentContributors.filter(
        (contributor) =>
          !providedIdentifiers.has(contributor.user_id) && !providedIdentifiers.has(contributor.login ?? "")
      );

      // 4. Remove the contributors that are no longer needed
      await Promise.allSettled(
        contributorsToRemove.map(async (contributor) =>
          this.userListService.deleteUserListContributor(userListId, contributor.id)
        )
      );
    } catch (e: unknown) {
      if (e instanceof Error) {
        throw new BadRequestException(`Unable to PATCH contributors: ${e.message}`);
      }
    }

    return this.findOneUserListByWorkspaceIdListIdUnguarded(id, userListId);
  }

  async deleteWorkspaceUserList(id: string, userListId: string, userId: number) {
    const workspace = await this.workspaceService.findOneById(id);

    /*
     * owners and editors can delete the workspace contributors
     */

    const canDelete = canUserEditWorkspace(workspace, userId);

    if (!canDelete) {
      throw new UnauthorizedException();
    }

    const userList = await this.findOneUserListByWorkspaceIdListIdUnguarded(id, userListId);

    await this.userListService.deleteUserList(userList.id);
  }

  async deleteWorkspaceUserListContributor(
    id: string,
    userListId: string,
    userListContributorId: string,
    userId: number
  ) {
    const workspace = await this.workspaceService.findOneById(id);

    /*
     * owners and editors can delete workspace userList contributors
     */

    const canEdit = canUserEditWorkspace(workspace, userId);

    if (!canEdit) {
      throw new UnauthorizedException();
    }

    const userList = await this.findOneUserListByWorkspaceIdListIdUnguarded(id, userListId);

    await this.userListService.deleteUserListContributor(userList.id, userListContributorId);
  }
}

results matching ""

    No results matching ""