File

src/workspace/workspace-contributors.service.ts

Index

Methods

Constructor

constructor(workspaceContributorRepository: Repository<DbWorkspaceContributor>, workspaceService: WorkspaceService, userService: UserService)
Parameters :
Name Type Optional
workspaceContributorRepository Repository<DbWorkspaceContributor> No
workspaceService WorkspaceService No
userService UserService No

Methods

Async addOneWorkspaceContributor
addOneWorkspaceContributor(id: string, userId: number, contributorId?: number, contributorLogin?: string)
Parameters :
Name Type Optional
id string No
userId number No
contributorId number Yes
contributorLogin string Yes
Async addWorkspaceContributors
addWorkspaceContributors(dto: UpdateWorkspaceContributorsDto, id: string, userId: number)
Parameters :
Name Type Optional
dto UpdateWorkspaceContributorsDto No
id string No
userId number No
baseQueryBuilder
baseQueryBuilder()
Async deleteOneWorkspaceContributor
deleteOneWorkspaceContributor(id: string, userId: number, contributorId?: number, contributorLogin?: string)
Parameters :
Name Type Optional
id string No
userId number No
contributorId number Yes
contributorLogin string Yes
Returns : unknown
Async deleteWorkspaceContributors
deleteWorkspaceContributors(dto: DeleteWorkspaceContributorsDto, id: string, userId: number)
Parameters :
Name Type Optional
dto DeleteWorkspaceContributorsDto No
id string No
userId number No
Returns : unknown
Private Async executeAddWorkspaceContributor
executeAddWorkspaceContributor(workspace: DbWorkspace, contributorId?: number, contributorLogin?: string)
Parameters :
Name Type Optional
workspace DbWorkspace No
contributorId number Yes
contributorLogin string Yes
Returns : any
Async findAllContributors
findAllContributors(id: string, skip?: number, limit?: number)
Parameters :
Name Type Optional
id string No
skip number Yes
limit number Yes
Async findAllContributorsGuarded
findAllContributorsGuarded(pageOptionsDto: PageOptionsDto, id: string, userId: number | undefined)
Parameters :
Name Type Optional
pageOptionsDto PageOptionsDto No
id string No
userId number | undefined No
Returns : Promise<PageDto<DbWorkspaceContributor>>
import { BadRequestException, Injectable, NotFoundException, UnauthorizedException } from "@nestjs/common";
import { Repository, SelectQueryBuilder } from "typeorm";
import { InjectRepository } from "@nestjs/typeorm";

import { PageMetaDto } from "../common/dtos/page-meta.dto";
import { PageDto } from "../common/dtos/page.dto";
import { PageOptionsDto } from "../common/dtos/page-options.dto";
import { UserService } from "../user/services/user.service";
import { DbWorkspaceContributor } from "./entities/workspace-contributors.entity";
import { DbWorkspace } from "./entities/workspace.entity";
import { WorkspaceService } from "./workspace.service";
import { canUserEditWorkspace, canUserViewWorkspace } from "./common/memberAccess";
import { UpdateWorkspaceContributorsDto } from "./dtos/update-workspace-contributors.dto";
import { DeleteWorkspaceContributorsDto } from "./dtos/delete-workspace-contributors.dto";

@Injectable()
export class WorkspaceContributorsService {
  constructor(
    @InjectRepository(DbWorkspaceContributor, "ApiConnection")
    private workspaceContributorRepository: Repository<DbWorkspaceContributor>,
    private workspaceService: WorkspaceService,
    private userService: UserService
  ) {}

  baseQueryBuilder(): SelectQueryBuilder<DbWorkspaceContributor> {
    const builder = this.workspaceContributorRepository.createQueryBuilder("workspace_contributors");

    return builder;
  }

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

    /*
     * viewers, editors, and owners can see what contributors belongs to a workspace
     */

    const canView = canUserViewWorkspace(workspace, userId);

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

    const entities = await this.findAllContributors(id, pageOptionsDto.skip, pageOptionsDto.limit);

    const pageMetaDto = new PageMetaDto({ itemCount: entities.length, pageOptionsDto });

    return new PageDto(entities, pageMetaDto);
  }

  async findAllContributors(id: string, skip?: number, limit?: number): Promise<DbWorkspaceContributor[]> {
    const queryBuilder = this.baseQueryBuilder();

    queryBuilder
      .leftJoinAndSelect(
        "workspace_contributors.contributor",
        "workspace_contributors_contributor",
        "workspace_contributors.contributor_id = workspace_contributors_contributor.id"
      )
      .where("workspace_contributors.workspace_id = :id", { id });

    if (skip) {
      queryBuilder.skip(skip);
    }

    if (limit) {
      queryBuilder.take(limit);
    }

    return queryBuilder.getMany();
  }

  async addOneWorkspaceContributor(
    id: string,
    userId: number,
    contributorId?: number,
    contributorLogin?: string
  ): Promise<DbWorkspace> {
    const workspace = await this.workspaceService.findOneById(id);

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

    const canUpdate = canUserEditWorkspace(workspace, userId);

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

    await this.executeAddWorkspaceContributor(workspace, contributorId, contributorLogin);

    return this.workspaceService.findOneById(id);
  }

  async addWorkspaceContributors(
    dto: UpdateWorkspaceContributorsDto,
    id: string,
    userId: number
  ): Promise<DbWorkspace> {
    const workspace = await this.workspaceService.findOneById(id);

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

    const canUpdate = canUserEditWorkspace(workspace, userId);

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

    const promises = dto.contributors.map(async (contributor) => {
      await this.executeAddWorkspaceContributor(workspace, contributor.id, contributor.login);
    });

    await Promise.all(promises);
    return this.workspaceService.findOneById(id);
  }

  private async executeAddWorkspaceContributor(
    workspace: DbWorkspace,
    contributorId?: number,
    contributorLogin?: string
  ) {
    if (!contributorId && !contributorLogin) {
      throw new BadRequestException("either user id or login must be provided");
    }

    const user = await this.userService.tryFindUserOrMakeStub({ userId: contributorId, username: contributorLogin });

    const existingContributor = await this.workspaceContributorRepository.findOne({
      where: {
        workspace_id: workspace.id,
        contributor_id: user.id,
      },
      withDeleted: true,
    });

    if (existingContributor) {
      await this.workspaceContributorRepository.restore(existingContributor.id);
    } else {
      if (user.type.toLowerCase() === "organization") {
        throw new NotFoundException("not a contributor");
      }

      const newContributor = new DbWorkspaceContributor();

      newContributor.workspace = workspace;
      newContributor.contributor = user;

      await this.workspaceContributorRepository.save(newContributor);
    }
  }

  async deleteOneWorkspaceContributor(id: string, userId: number, contributorId?: number, contributorLogin?: string) {
    const user = await this.userService.tryFindUserOrMakeStub({ userId: contributorId, username: contributorLogin });

    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 existingContributor = await this.workspaceContributorRepository.findOne({
      where: {
        workspace_id: id,
        contributor_id: user.id,
      },
    });

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

    await this.workspaceContributorRepository.softDelete(existingContributor.id);

    return this.workspaceService.findOneById(id);
  }

  async deleteWorkspaceContributors(dto: DeleteWorkspaceContributorsDto, id: 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 promises = dto.contributors.map(async (contributor) => {
      const user = await this.userService.tryFindUserOrMakeStub({
        userId: contributor.id,
        username: contributor.login,
      });

      const existingContributor = await this.workspaceContributorRepository.findOne({
        where: {
          workspace_id: id,
          contributor_id: user.id,
        },
      });

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

      return this.workspaceContributorRepository.softDelete(existingContributor.id);
    });

    try {
      await Promise.all(promises);
    } catch (error) {
      dto.contributors.forEach(async (contributor) => {
        const user = await this.userService.tryFindUserOrMakeStub({
          userId: contributor.id,
          username: contributor.login,
        });

        // restore the contributors who may have been soft deleted
        const existingContributor = await this.workspaceContributorRepository.findOne({
          where: {
            workspace_id: id,
            contributor_id: user.id,
          },
          withDeleted: true,
        });

        if (existingContributor) {
          await this.workspaceContributorRepository.restore(existingContributor.id);
        }
      });

      // throws the original error
      throw error;
    }

    return this.workspaceService.findOneById(id);
  }
}

results matching ""

    No results matching ""