feat: User operations on join org
This commit is contained in:
14
src/cache/cache.service.ts
vendored
14
src/cache/cache.service.ts
vendored
@@ -44,12 +44,14 @@ export class CacheService {
|
||||
// Fallback to DB
|
||||
const fresh = await factory();
|
||||
|
||||
// Try setting cache only if Redis available
|
||||
if (this.redisAvailable) {
|
||||
try {
|
||||
await this.cache.set(key, fresh, ttl);
|
||||
} catch {
|
||||
this.redisAvailable = false;
|
||||
if (fresh) {
|
||||
// Try setting cache only if Redis available
|
||||
if (this.redisAvailable) {
|
||||
try {
|
||||
await this.cache.set(key, fresh, ttl);
|
||||
} catch {
|
||||
this.redisAvailable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,14 @@ import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||
import { Logger } from '@nestjs/common';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
|
||||
const swaggerConfig = new DocumentBuilder()
|
||||
.setTitle('Kaa Khane')
|
||||
.setDescription(`API Documentation for Kaa Khane`)
|
||||
.setTitle('MultiTenant Saas')
|
||||
.setDescription(`API Documentation for a simple MultiTenant Saas Application`)
|
||||
.setVersion('0.0.1')
|
||||
.addGlobalResponse(
|
||||
{
|
||||
@@ -46,5 +47,7 @@ async function bootstrap() {
|
||||
const port = config.get<number>('PORT') ?? 3000;
|
||||
|
||||
await app.listen(port);
|
||||
|
||||
Logger.log(`Listning on port ${port}`)
|
||||
}
|
||||
bootstrap();
|
||||
|
||||
1
src/organization-membership/constants/index.ts
Normal file
1
src/organization-membership/constants/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./type"
|
||||
4
src/organization-membership/constants/type.ts
Normal file
4
src/organization-membership/constants/type.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export enum USER_ORG_ACCEPT_REJECT_ACTION {
|
||||
ACCEPT = 'ACCEPT',
|
||||
REJECT = 'REJECT'
|
||||
}
|
||||
@@ -1 +1,3 @@
|
||||
export * from './invite-to-org.dto';
|
||||
export * from './join-request.dto'
|
||||
export * from "./user-invitation-action.dto"
|
||||
|
||||
@@ -12,15 +12,6 @@ export class InviteUserToOrganizationRequestDTO {
|
||||
@IsNotEmpty()
|
||||
invitedUserEmail: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Organization id',
|
||||
example: 'eeec2c79-766a-4174-8004-2e57642095fe',
|
||||
type: 'string',
|
||||
})
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
orgId: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Role to assign',
|
||||
example: ORG_ROLE.member,
|
||||
|
||||
13
src/organization-membership/dto/join-request.dto.ts
Normal file
13
src/organization-membership/dto/join-request.dto.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||
import { IsNotEmpty, IsOptional, IsString, IsUUID } from "class-validator";
|
||||
|
||||
export class JoinRequestToOrganizationRequestDTO {
|
||||
@ApiPropertyOptional({
|
||||
description: 'Message along with the request',
|
||||
example: 'I would like to join',
|
||||
type: 'string',
|
||||
})
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
requestMessage?: string;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||
import { IsEnum, IsNotEmpty, IsOptional, IsString, IsUUID } from "class-validator";
|
||||
import { USER_ORG_ACCEPT_REJECT_ACTION } from "../constants";
|
||||
|
||||
|
||||
export class UserOrganizationInvitationActionRequestDTO {
|
||||
@ApiProperty({
|
||||
description: 'Action',
|
||||
example: USER_ORG_ACCEPT_REJECT_ACTION.ACCEPT,
|
||||
type: 'string',
|
||||
})
|
||||
@IsEnum(USER_ORG_ACCEPT_REJECT_ACTION)
|
||||
@IsNotEmpty()
|
||||
action: string
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Message(reject reason)',
|
||||
example: 'Bad sry or smth',
|
||||
type: 'string',
|
||||
})
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
message?: string
|
||||
}
|
||||
@@ -1,62 +1,145 @@
|
||||
import { Body, Controller, Get, Param, Patch, Post } from '@nestjs/common';
|
||||
import { Body, Controller, Delete, Get, Param, ParseEnumPipe, ParseUUIDPipe, Patch, Post, Query } from '@nestjs/common';
|
||||
import { OrganizationMembershipService } from './organization-membership.service';
|
||||
import { RequestContextService } from 'core/als/request-context.service';
|
||||
import { ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { InviteUserToOrganizationRequestDTO } from './dto';
|
||||
import { ApiBearerAuth, ApiOperation, ApiParam, ApiQuery } from '@nestjs/swagger';
|
||||
import { InviteUserToOrganizationRequestDTO, JoinRequestToOrganizationRequestDTO, UserOrganizationInvitationActionRequestDTO } from './dto';
|
||||
import { ORGANIZATION_JOIN_REQUEST_TYPE } from 'prisma/generated/prisma/enums';
|
||||
|
||||
@Controller('organization-membership')
|
||||
/* NOTE: Regarding endpoint path naming
|
||||
* - Since we follow REST style, endpoint are resource based.
|
||||
* - So insted of /organization/:orgId/invitation-action, we user ..../invitation/:invitationId
|
||||
* (invitationid points to a resource)
|
||||
* */
|
||||
|
||||
@Controller('membership')
|
||||
@ApiBearerAuth('access-token')
|
||||
export class OrganizationMembershipController {
|
||||
constructor(
|
||||
private readonly orgMemService: OrganizationMembershipService,
|
||||
private readonly requestContext: RequestContextService,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
@Post('/invite')
|
||||
async inviteUserToOrg(@Body() body: InviteUserToOrganizationRequestDTO) {
|
||||
const user = this.requestContext.user;
|
||||
return await this.orgMemService.inviteUserToOrg(user.userId, body);
|
||||
}
|
||||
|
||||
@Post('/request')
|
||||
async requestToJoinOrg() {}
|
||||
|
||||
// TODO: Move invite to org. section and join to user. Also option to cancel invitation and join req.
|
||||
|
||||
/* *
|
||||
* USER OPERATIONS
|
||||
* */
|
||||
@Patch('user/accept-invitation')
|
||||
acceptInvitation() {}
|
||||
|
||||
@Patch('user/reject-invitation')
|
||||
rejectInvitation() {}
|
||||
|
||||
@Get('user/invitations')
|
||||
async getUserInvitations() {
|
||||
@ApiOperation({ summary: 'Send request to join an organization' })
|
||||
@ApiParam({
|
||||
name: 'orgId',
|
||||
type: String,
|
||||
})
|
||||
@Post('organization/:orgId/join-request')
|
||||
async requestToJoinOrg(
|
||||
@Param('orgId', new ParseUUIDPipe()) orgId: string,
|
||||
@Body() body: JoinRequestToOrganizationRequestDTO
|
||||
) {
|
||||
const user = this.requestContext.user;
|
||||
return await this.orgMemService.getUserInvitations(user.userId);
|
||||
return await this.orgMemService.usersRequestToJoin(user.userId, orgId, body)
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: 'Cancel a sent invitation to join an organization' })
|
||||
@ApiParam({
|
||||
name: 'orgId',
|
||||
type: String,
|
||||
})
|
||||
@Delete('organization/:orgId/join-request')
|
||||
async cancelRequestToJoinOrg(@Param('orgId', new ParseUUIDPipe()) orgId: string) {
|
||||
const user = this.requestContext.user;
|
||||
return await this.orgMemService.userCancelOrgJoinRequest(user.userId, orgId)
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: 'Accept or reject an invitation from an organization' })
|
||||
@ApiParam({
|
||||
name: 'orgId',
|
||||
type: String,
|
||||
})
|
||||
@Patch('organization/:orgId/invitation')
|
||||
async acceptOrRejectInvitation(
|
||||
@Param('orgId', new ParseUUIDPipe()) orgId: string,
|
||||
@Body() body: UserOrganizationInvitationActionRequestDTO
|
||||
) {
|
||||
const user = this.requestContext.user;
|
||||
return await this.orgMemService.userOrganiaztionRequestAction(
|
||||
user.userId,
|
||||
orgId,
|
||||
body
|
||||
)
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: 'List invitations recieved or join requests sent' })
|
||||
@ApiQuery({
|
||||
name: 'requestType',
|
||||
enum: ORGANIZATION_JOIN_REQUEST_TYPE,
|
||||
required: false,
|
||||
})
|
||||
@Get('me/invitations')
|
||||
async getUserInvitations(
|
||||
@Query(
|
||||
'requestType',
|
||||
new ParseEnumPipe(
|
||||
ORGANIZATION_JOIN_REQUEST_TYPE, { optional: true }
|
||||
)
|
||||
) requestType?: ORGANIZATION_JOIN_REQUEST_TYPE
|
||||
) {
|
||||
const user = this.requestContext.user;
|
||||
return await this.orgMemService.getUserInvitations(user.userId, requestType);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: 'List organizations user is part of' })
|
||||
@Get('me/organizations')
|
||||
async getUserOrganizations() {
|
||||
const user = this.requestContext.user;
|
||||
return await this.orgMemService.getOrganizationsOfUser(user.userId)
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: 'Leave an organization' })
|
||||
@ApiParam({
|
||||
name: 'orgId',
|
||||
type: String,
|
||||
})
|
||||
@Delete('organization/:orgId/member/me')
|
||||
async leaveOrganization(
|
||||
@Param('orgId', new ParseUUIDPipe()) orgId: string,
|
||||
) {
|
||||
const user = this.requestContext.user;
|
||||
return await this.orgMemService.userLeaveAnOrganization(user.userId, orgId)
|
||||
}
|
||||
|
||||
@Get('user/organizations')
|
||||
async getUserOrganizations() {}
|
||||
|
||||
/* *
|
||||
* ORGANIZATION OPERATIONS
|
||||
* */
|
||||
@Get('organization/:id/members')
|
||||
async getOrganizationMemebers(@Param('id') orgId: string) {
|
||||
return await this.orgMemService.getMemebersOfOrganization(orgId);
|
||||
}
|
||||
|
||||
@Get('organization/:id/invitations')
|
||||
async getOrganizationInvitations(@Param('id') orgId: string) {
|
||||
return await this.orgMemService.getMemebersOfOrganization(orgId);
|
||||
}
|
||||
|
||||
@Patch('organization/:id/accept-request')
|
||||
acceptJoinRequest() {}
|
||||
|
||||
@Patch('organization/:id/reject-request')
|
||||
rejectJoinRequest() {}
|
||||
// @ApiOperation({ summary: 'Invite user to organization' })
|
||||
// @ApiParam({
|
||||
// name: 'orgId',
|
||||
// type: String,
|
||||
// })
|
||||
// @Post('organization/:orgId/invitation')
|
||||
// async inviteUserToOrg(
|
||||
// @Param('orgId', new ParseUUIDPipe()) orgId: string,
|
||||
// @Body() body: InviteUserToOrganizationRequestDTO
|
||||
// ) {
|
||||
// const user = this.requestContext.user;
|
||||
// return await this.orgMemService.inviteUserToOrg(
|
||||
// user.userId,
|
||||
// orgId,
|
||||
// body
|
||||
// );
|
||||
// }
|
||||
// @Get('organization/:id/members')
|
||||
// async getOrganizationMemebers(@Param('id') orgId: string) {
|
||||
// return await this.orgMemService.getMemebersOfOrganization(orgId);
|
||||
// }
|
||||
//
|
||||
// @Get('organization/:id/invitations')
|
||||
// async getOrganizationInvitations(@Param('id') orgId: string) {
|
||||
// return await this.orgMemService.getOrganizationRequestList(orgId);
|
||||
// }
|
||||
//
|
||||
// @Patch('organization/:id/accept-request')
|
||||
// acceptJoinRequest() { }
|
||||
//
|
||||
// @Patch('organization/:id/reject-request')
|
||||
// rejectJoinRequest() { }
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { OrganizationService } from 'src/organization/organization.service';
|
||||
import { UserService } from 'src/user/user.service';
|
||||
import { InviteUserToOrganizationRequestDTO } from './dto';
|
||||
import { InviteUserToOrganizationRequestDTO, UserOrganizationInvitationActionRequestDTO } from './dto';
|
||||
import { PrismaService } from 'src/prisma/prisma.service';
|
||||
import {
|
||||
ORGANIZATION_JOIN_REQUEST,
|
||||
@@ -15,6 +15,8 @@ import {
|
||||
import { AuthorizationService } from 'src/authorization/authorization.service';
|
||||
import { USER_ORGANIZATION_OPERATIONS } from 'src/authorization/operations';
|
||||
import { Prisma } from 'prisma/generated/prisma/client';
|
||||
import { JoinRequestToOrganizationRequestDTO } from './dto/join-request.dto';
|
||||
import { USER_ORG_ACCEPT_REJECT_ACTION } from './constants';
|
||||
|
||||
@Injectable()
|
||||
export class OrganizationMembershipService {
|
||||
@@ -23,14 +25,15 @@ export class OrganizationMembershipService {
|
||||
private readonly orgService: OrganizationService,
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly authorization: AuthorizationService,
|
||||
) {}
|
||||
) { }
|
||||
async inviteUserToOrg(
|
||||
userId: string,
|
||||
orgId: string,
|
||||
dto: InviteUserToOrganizationRequestDTO,
|
||||
) {
|
||||
const { invitedUserEmail, ...rest } = dto;
|
||||
const [orgExists, invitedUser] = await Promise.all([
|
||||
this.orgService.organizationExists(dto.orgId),
|
||||
this.orgService.findById(orgId),
|
||||
this.userService.findByEmail(invitedUserEmail),
|
||||
]);
|
||||
|
||||
@@ -41,7 +44,7 @@ export class OrganizationMembershipService {
|
||||
await this.prisma.organizationUserJoinTable.findUnique({
|
||||
where: {
|
||||
userId_orgId: {
|
||||
orgId: dto.orgId,
|
||||
orgId: orgId,
|
||||
userId: invitedUser.id,
|
||||
},
|
||||
},
|
||||
@@ -52,7 +55,7 @@ export class OrganizationMembershipService {
|
||||
|
||||
const canInviteUser = await this.authorization.canPerformOperation(
|
||||
userId,
|
||||
dto.orgId,
|
||||
orgId,
|
||||
USER_ORGANIZATION_OPERATIONS.INVITE_USERS,
|
||||
);
|
||||
if (!canInviteUser) throw new ForbiddenException('Insufficient Permission');
|
||||
@@ -62,6 +65,7 @@ export class OrganizationMembershipService {
|
||||
data: {
|
||||
...rest,
|
||||
userId: invitedUser.id,
|
||||
orgId,
|
||||
requestType: ORGANIZATION_JOIN_REQUEST_TYPE.INVITED,
|
||||
},
|
||||
});
|
||||
@@ -77,63 +81,227 @@ export class OrganizationMembershipService {
|
||||
}
|
||||
}
|
||||
|
||||
requestToJoin() {}
|
||||
|
||||
// TODO: reject, rejectReason
|
||||
async acceptInvitation(userId: string, orgId: string) {
|
||||
const orgExists = await this.orgService.organizationExists(orgId);
|
||||
if (!orgExists) throw new NotFoundException('Organization');
|
||||
|
||||
const [userAlreadyPart, isUserInvited] = await Promise.all([
|
||||
this.prisma.organizationUserJoinTable.findUnique({
|
||||
where: {
|
||||
userId_orgId: {
|
||||
orgId,
|
||||
userId,
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
/* *
|
||||
* USER OPERATIONS
|
||||
* */
|
||||
async usersRequestToJoin(
|
||||
userId: string,
|
||||
orgId: string,
|
||||
dto: JoinRequestToOrganizationRequestDTO
|
||||
) {
|
||||
const [
|
||||
orgExists,
|
||||
invitationAlreadySent,
|
||||
userAlreadyPartOf
|
||||
] = await Promise.all([
|
||||
this.orgService.findById(orgId),
|
||||
this.prisma.organizationJoinRequest.findUnique({
|
||||
where: {
|
||||
userId_orgId: {
|
||||
orgId,
|
||||
userId,
|
||||
orgId: orgId
|
||||
},
|
||||
status: ORGANIZATION_JOIN_REQUEST.PENDING,
|
||||
status: ORGANIZATION_JOIN_REQUEST.PENDING
|
||||
},
|
||||
select: { orgId: true }
|
||||
}),
|
||||
]);
|
||||
this.prisma.organizationUserJoinTable.findUnique({
|
||||
where: {
|
||||
userId_orgId: {
|
||||
orgId: orgId,
|
||||
userId
|
||||
}
|
||||
},
|
||||
select: { userId: true }
|
||||
})
|
||||
])
|
||||
|
||||
if (userAlreadyPart)
|
||||
throw new BadRequestException('User already part of this organization');
|
||||
if (!isUserInvited)
|
||||
throw new BadRequestException(
|
||||
'User has no invitations from this organization',
|
||||
);
|
||||
if (!orgExists)
|
||||
throw new NotFoundException("Organization")
|
||||
if (invitationAlreadySent)
|
||||
throw new BadRequestException("Invitation to join this organization already sent")
|
||||
if (userAlreadyPartOf)
|
||||
throw new BadRequestException("User already part of the organization")
|
||||
|
||||
return await this.prisma.$transaction(async (tx) => {
|
||||
await tx.organizationJoinRequest.update({
|
||||
return await this.prisma.organizationJoinRequest.create({
|
||||
data: {
|
||||
orgId: orgId,
|
||||
userId,
|
||||
requestType: ORGANIZATION_JOIN_REQUEST_TYPE.REQUESTED,
|
||||
requestMessage: dto.requestMessage,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
async userCancelOrgJoinRequest(
|
||||
userId: string,
|
||||
orgId: string
|
||||
) {
|
||||
try {
|
||||
await this.prisma.organizationJoinRequest.update({
|
||||
where: {
|
||||
userId_orgId: {
|
||||
userId,
|
||||
orgId,
|
||||
orgId
|
||||
},
|
||||
},
|
||||
data: {
|
||||
status: ORGANIZATION_JOIN_REQUEST.ACCEPTED,
|
||||
},
|
||||
});
|
||||
|
||||
return await tx.organizationUserJoinTable.create({
|
||||
data: {
|
||||
orgId,
|
||||
userId,
|
||||
},
|
||||
});
|
||||
});
|
||||
status: ORGANIZATION_JOIN_REQUEST.CANCELLED,
|
||||
}
|
||||
})
|
||||
}
|
||||
catch (err) {
|
||||
throw new NotFoundException("Join request")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* List of organizations that:
|
||||
* - user have requested to join
|
||||
* - have send invitations to user
|
||||
*
|
||||
* filtered by requestType
|
||||
* */
|
||||
async userOrganizationJoinRequestList(
|
||||
userId: string,
|
||||
requestType: string
|
||||
) {
|
||||
const joinReqType: ORGANIZATION_JOIN_REQUEST_TYPE | undefined = ORGANIZATION_JOIN_REQUEST_TYPE[requestType];
|
||||
if (!joinReqType)
|
||||
throw new BadRequestException("Invalid request type")
|
||||
|
||||
return await this.prisma.organizationJoinRequest.findMany({
|
||||
where: {
|
||||
userId,
|
||||
requestType: joinReqType
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
async userOrganiaztionRequestAction(
|
||||
userId: string,
|
||||
orgId: string,
|
||||
dto: UserOrganizationInvitationActionRequestDTO
|
||||
) {
|
||||
const [orgExists, hasUserSendRequest] = await Promise.all([
|
||||
this.orgService.findById(orgId),
|
||||
this.prisma.organizationJoinRequest.findUnique({
|
||||
where: {
|
||||
userId_orgId: {
|
||||
userId,
|
||||
orgId: orgId
|
||||
},
|
||||
status: ORGANIZATION_JOIN_REQUEST.PENDING,
|
||||
requestType: ORGANIZATION_JOIN_REQUEST_TYPE.REQUESTED
|
||||
}
|
||||
})
|
||||
])
|
||||
|
||||
if (!orgExists)
|
||||
throw new NotFoundException("Organization")
|
||||
if (!hasUserSendRequest)
|
||||
throw new BadRequestException("No pending join request")
|
||||
|
||||
try {
|
||||
return await this.prisma.$transaction(async (tx) => {
|
||||
const userAction = dto.action === USER_ORG_ACCEPT_REJECT_ACTION.ACCEPT
|
||||
? ORGANIZATION_JOIN_REQUEST.ACCEPTED
|
||||
: ORGANIZATION_JOIN_REQUEST.REJECTED;
|
||||
|
||||
await tx.organizationJoinRequest.update({
|
||||
where: {
|
||||
userId_orgId: {
|
||||
userId,
|
||||
orgId: orgId,
|
||||
}
|
||||
},
|
||||
data: {
|
||||
status: userAction,
|
||||
...(
|
||||
userAction === ORGANIZATION_JOIN_REQUEST.REJECTED && dto.message
|
||||
? { rejectReason: dto.message }
|
||||
: {}
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
if (userAction === ORGANIZATION_JOIN_REQUEST.ACCEPTED)
|
||||
await tx.organizationUserJoinTable.create({
|
||||
data: {
|
||||
userId: userId,
|
||||
orgId: orgId,
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
if (err.code === 'P2002')
|
||||
throw new BadRequestException('User already part of this organization.');
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: reject, rejectReason
|
||||
//
|
||||
// async acceptInvitation(userId: string, orgId: string) {
|
||||
// const orgExists = await this.orgService.organizationExists(orgId);
|
||||
// if (!orgExists) throw new NotFoundException('Organization');
|
||||
//
|
||||
// const [userAlreadyPart, isUserInvited] = await Promise.all([
|
||||
// this.prisma.organizationUserJoinTable.findUnique({
|
||||
// where: {
|
||||
// userId_orgId: {
|
||||
// orgId,
|
||||
// userId,
|
||||
// },
|
||||
// },
|
||||
// }),
|
||||
//
|
||||
// this.prisma.organizationJoinRequest.findUnique({
|
||||
// where: {
|
||||
// userId_orgId: {
|
||||
// orgId,
|
||||
// userId,
|
||||
// },
|
||||
// status: ORGANIZATION_JOIN_REQUEST.PENDING,
|
||||
// },
|
||||
// }),
|
||||
// ]);
|
||||
//
|
||||
// if (userAlreadyPart)
|
||||
// throw new BadRequestException('User already part of this organization');
|
||||
// if (!isUserInvited)
|
||||
// throw new BadRequestException(
|
||||
// 'User has no invitations from this organization',
|
||||
// );
|
||||
//
|
||||
// return await this.prisma.$transaction(async (tx) => {
|
||||
// await tx.organizationJoinRequest.update({
|
||||
// where: {
|
||||
// userId_orgId: {
|
||||
// userId,
|
||||
// orgId,
|
||||
// },
|
||||
// },
|
||||
// data: {
|
||||
// status: ORGANIZATION_JOIN_REQUEST.ACCEPTED,
|
||||
// },
|
||||
// });
|
||||
//
|
||||
// return await tx.organizationUserJoinTable.create({
|
||||
// data: {
|
||||
// orgId,
|
||||
// userId,
|
||||
// },
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
async getUserInvitations(
|
||||
userId: string,
|
||||
requestType: ORGANIZATION_JOIN_REQUEST_TYPE = ORGANIZATION_JOIN_REQUEST_TYPE.INVITED,
|
||||
@@ -153,8 +321,58 @@ export class OrganizationMembershipService {
|
||||
});
|
||||
}
|
||||
|
||||
async getOrganizationsOfUser(userId: string) {
|
||||
return await this.prisma.organizationJoinRequest.findMany({
|
||||
where: {
|
||||
userId
|
||||
},
|
||||
include: {
|
||||
organization: {
|
||||
select: {
|
||||
name: true,
|
||||
description: true
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async userLeaveAnOrganization(userId: string, orgId: string) {
|
||||
try {
|
||||
await this.prisma.organizationUserJoinTable.delete({
|
||||
where: {
|
||||
userId_orgId: { userId, orgId }
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
throw new NotFoundException("Membership not found");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async getOrganizationRequestList(
|
||||
orgId: string,
|
||||
requestType: ORGANIZATION_JOIN_REQUEST_TYPE = ORGANIZATION_JOIN_REQUEST_TYPE.REQUESTED,
|
||||
status: ORGANIZATION_JOIN_REQUEST = ORGANIZATION_JOIN_REQUEST.PENDING,
|
||||
) {
|
||||
// TODO: Check can perform
|
||||
return await this.prisma.organizationJoinRequest.findMany({
|
||||
where: {
|
||||
orgId: orgId,
|
||||
status: status,
|
||||
requestType: requestType,
|
||||
},
|
||||
include: {
|
||||
user: {
|
||||
select: { email: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async getMemebersOfOrganization(orgId: string) {
|
||||
const orgExists = await this.orgService.organizationExists(orgId);
|
||||
const orgExists = await this.orgService.findById(orgId);
|
||||
if (!orgExists) throw new NotFoundException('Organization');
|
||||
|
||||
return await this.prisma.organizationUserJoinTable.findMany({
|
||||
|
||||
@@ -21,7 +21,7 @@ export class OrganizationService {
|
||||
// private readonly reqContext: RequestContextService,
|
||||
private readonly authorization: AuthorizationService,
|
||||
private readonly cacheService: CacheService,
|
||||
) {}
|
||||
) { }
|
||||
async createNewOrganization(
|
||||
userId: string,
|
||||
dto: CreateNewOrganizationRequestDTO,
|
||||
@@ -60,7 +60,7 @@ export class OrganizationService {
|
||||
orgId: string,
|
||||
dto: UpdateOrganizationRequestDTO,
|
||||
) {
|
||||
const orgExists = await this.organizationExists(orgId);
|
||||
const orgExists = await this.findById(orgId);
|
||||
if (!orgExists) throw new NotFoundException('Organization');
|
||||
|
||||
const canUserUpdateOrganization =
|
||||
@@ -83,7 +83,7 @@ export class OrganizationService {
|
||||
|
||||
// TODO: Either empty or choose someone to be owner
|
||||
async deleteAnOrganization(userId: string, orgId: string) {
|
||||
const orgExists = await this.organizationExists(orgId);
|
||||
const orgExists = await this.findById(orgId);
|
||||
if (!orgExists) throw new NotFoundException('Organization');
|
||||
|
||||
const canUserDeleteOrganization =
|
||||
@@ -118,13 +118,16 @@ export class OrganizationService {
|
||||
});
|
||||
}
|
||||
|
||||
async organizationExists(orgId: string) {
|
||||
return await this.prisma.organization.findUnique({
|
||||
where: { id: orgId },
|
||||
select: { id: true },
|
||||
});
|
||||
}
|
||||
// async organizationExists(orgId: string) {
|
||||
// return await this.prisma.organization.findUnique({
|
||||
// where: { id: orgId },
|
||||
// select: { id: true },
|
||||
// });
|
||||
// }
|
||||
|
||||
/*
|
||||
* Its neat, it caches info plus only selects id so faster DB search as well
|
||||
* */
|
||||
async findById(orgId: string) {
|
||||
const organization = await this.cacheService.getOrSet(
|
||||
orgId,
|
||||
|
||||
Reference in New Issue
Block a user