import { JwtAuthGuard, VerifiedScopeGuard } from "@cv/auth"; import { AuthorizationService, ClockService, User as DomainUser, Membership, MembershipSortField, MembershipSortOrder, Organization, OrganizationFactory, OrganizationService, PaginationService, UserService, } from "@cv/core"; import { UseGuards } from "@nestjs/common"; import { Args, Mutation, Parent, Query, ResolveField, Resolver, } from "@nestjs/graphql"; import { CurrentUser } from "@/modules/current-user/current-user.decorator"; import { MembershipConnection } from "./membership.type"; import { MembershipConnectionArgs } from "./membership-connection-args.type"; @Resolver(() => Organization) @UseGuards(JwtAuthGuard, VerifiedScopeGuard) export class OrganizationResolver { constructor( private readonly organizationService: OrganizationService, private readonly organizationFactory: OrganizationFactory, private readonly paginationService: PaginationService, private readonly authorizationService: AuthorizationService, private readonly clock: ClockService, private readonly userService: UserService, ) {} @Query(() => Organization, { name: "organization", nullable: true }) async getOrganization( @CurrentUser() user: DomainUser, @Args("id") id: string, ): Promise { const organization = await this.organizationService.findById(id); if (!organization) { return null; } await this.authorizationService.canView(user, organization, Organization); return organization; } @ResolveField(() => MembershipConnection) async memberships( @Parent() organization: Organization, @Args() args: MembershipConnectionArgs, ): Promise { const paginationOptions = this.paginationService.parsePaginationArgs(args); const sortBy = (args.sortBy || "createdAt") as MembershipSortField; const sortOrder = (args.sortOrder || "asc") as MembershipSortOrder; const searchTerm = args.searchTerm; const [items, totalCount] = await Promise.all([ this.organizationService.findMembershipsByOrganization( organization.id, sortBy, sortOrder, searchTerm ?? undefined, ), this.organizationService.countMembershipsByOrganization( organization.id, searchTerm ?? undefined, ), ]); const result = this.paginationService.buildPaginationResult( items, totalCount, paginationOptions, ); return MembershipConnection.fromPaginationResult(result); } @ResolveField(() => Number) async memberCount(@Parent() organization: Organization): Promise { return this.organizationService.countMembershipsByOrganization( organization.id, ); } @Mutation(() => Organization) async createOrganization( @CurrentUser() user: DomainUser, @Args("name") name: string, @Args("description", { nullable: true }) description?: string, ): Promise { await this.authorizationService.canCreate(user, Organization); const dto: { name: string; description?: string } = { name }; if (description !== undefined) { dto.description = description; } const entity = this.organizationFactory.create(dto); return this.organizationService.save(entity); } @Mutation(() => Organization) async updateOrganization( @CurrentUser() user: DomainUser, @Args("id") id: string, @Args("name", { nullable: true }) name?: string, @Args("description", { nullable: true }) description?: string, ): Promise { const organization = await this.organizationService.findByIdOrFail(id); await this.authorizationService.canUpdate(user, organization, Organization); const dto: { name?: string; description?: string } = {}; if (name !== undefined) { dto.name = name; } if (description !== undefined) { dto.description = description; } const updatedEntity = new Organization( organization.id, dto.name ?? organization.name, organization.createdAt, this.clock.now(), dto.description !== undefined ? dto.description : organization.description, ); return this.organizationService.save(updatedEntity); } @Mutation(() => Boolean) async deleteOrganization( @CurrentUser() user: DomainUser, @Args("id") id: string, ): Promise { const organization = await this.organizationService.findByIdOrFail(id); await this.authorizationService.canDelete(user, organization, Organization); await this.organizationService.destroy(organization); return true; } @Mutation(() => Boolean) async addUserToOrganization( @CurrentUser() user: DomainUser, @Args("organizationId") organizationId: string, @Args("userId") userId: string, ): Promise { const organization = await this.organizationService.findByIdOrFail(organizationId); await this.authorizationService.canUpdate(user, organization, Organization); const targetUser = await this.userService.findByIdOrFail(userId); return this.organizationService.addUserToOrganization( organization, targetUser, ); } @Mutation(() => Boolean) async removeUserFromOrganization( @CurrentUser() user: DomainUser, @Args("organizationId") organizationId: string, @Args("userId") userId: string, ): Promise { const organization = await this.organizationService.findByIdOrFail(organizationId); await this.authorizationService.canUpdate(user, organization, Organization); const targetUser = await this.userService.findByIdOrFail(userId); return this.organizationService.removeUserFromOrganization( organization, targetUser, ); } }