perf: db metrix
This commit is contained in:
@@ -21,7 +21,8 @@
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json",
|
||||
"do-test": "pnpm run test"
|
||||
"do-test": "pnpm run test",
|
||||
"seed": "node --loader ts-node/esm prisma/seed.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs-modules/mailer": "^1.8.1",
|
||||
@@ -57,6 +58,7 @@
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"passport-microsoft": "^1.0.0",
|
||||
"pg": "^8.11.3",
|
||||
"prisma": "^4.16.2",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
|
||||
58
packages/hoppscotch-backend/prisma/seed.ts
Normal file
58
packages/hoppscotch-backend/prisma/seed.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { PrismaClient, TeamMemberRole } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
const noOfUsers = 600000;
|
||||
|
||||
const getAllUser = async () => {
|
||||
const users = await prisma.user.findMany();
|
||||
return users;
|
||||
};
|
||||
|
||||
const createUsers = async () => {
|
||||
for (let i = 1; i <= noOfUsers; i++) {
|
||||
try {
|
||||
await prisma.user.create({
|
||||
data: {
|
||||
email: `${i}@gmail.com`,
|
||||
},
|
||||
});
|
||||
} catch (_) {}
|
||||
}
|
||||
};
|
||||
|
||||
const createTeams = async () => {
|
||||
const users = await getAllUser();
|
||||
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
try {
|
||||
await prisma.team.create({
|
||||
data: {
|
||||
name: `Team ${i + 1}`,
|
||||
members: {
|
||||
create: {
|
||||
userUid: users[i].uid,
|
||||
role: TeamMemberRole.OWNER,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (_) {}
|
||||
}
|
||||
};
|
||||
|
||||
async function main() {
|
||||
console.log('Seeding...');
|
||||
|
||||
await createUsers();
|
||||
await createTeams();
|
||||
}
|
||||
|
||||
main()
|
||||
.then(async () => {
|
||||
await prisma.$disconnect();
|
||||
})
|
||||
.catch(async (e) => {
|
||||
console.error(e);
|
||||
await prisma.$disconnect();
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -20,6 +20,7 @@ import { ShortcodeModule } from './shortcode/shortcode.module';
|
||||
import { COOKIES_NOT_FOUND } from './errors';
|
||||
import { ThrottlerModule } from '@nestjs/throttler';
|
||||
import { AppController } from './app.controller';
|
||||
import { DbModule } from './db/db.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -66,6 +67,7 @@ import { AppController } from './app.controller';
|
||||
ttl: +process.env.RATE_LIMIT_TTL,
|
||||
limit: +process.env.RATE_LIMIT_MAX,
|
||||
}),
|
||||
DbModule,
|
||||
UserModule,
|
||||
AuthModule,
|
||||
AdminModule,
|
||||
|
||||
1
packages/hoppscotch-backend/src/constants.ts
Normal file
1
packages/hoppscotch-backend/src/constants.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const PG_CONNECTION = 'PG_CONNECTION';
|
||||
21
packages/hoppscotch-backend/src/db/db.module.ts
Normal file
21
packages/hoppscotch-backend/src/db/db.module.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { Pool } from 'pg';
|
||||
import { PG_CONNECTION } from 'src/constants';
|
||||
|
||||
const dbProvider = {
|
||||
provide: PG_CONNECTION,
|
||||
useValue: new Pool({
|
||||
user: 'postgres',
|
||||
host: 'hoppscotch-db',
|
||||
database: 'hoppscotch',
|
||||
password: 'testpass',
|
||||
port: 5432,
|
||||
}),
|
||||
};
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [dbProvider],
|
||||
exports: [dbProvider],
|
||||
})
|
||||
export class DbModule {}
|
||||
@@ -7,7 +7,11 @@ export class PrismaService
|
||||
implements OnModuleInit, OnModuleDestroy
|
||||
{
|
||||
constructor() {
|
||||
super();
|
||||
super(
|
||||
{
|
||||
log: ['query', 'info', 'warn', 'error'],
|
||||
}
|
||||
);
|
||||
}
|
||||
async onModuleInit() {
|
||||
await this.$connect();
|
||||
|
||||
@@ -22,6 +22,7 @@ import { throwErr } from 'src/utils';
|
||||
import { AuthUser } from 'src/types/AuthUser';
|
||||
import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard';
|
||||
import { SkipThrottle } from '@nestjs/throttler';
|
||||
import { cons } from 'fp-ts/lib/ReadonlyNonEmptyArray';
|
||||
|
||||
@UseGuards(GqlThrottlerGuard)
|
||||
@Resolver(() => Team)
|
||||
@@ -55,8 +56,13 @@ export class TeamResolver {
|
||||
description: 'Returns the list of members of a team',
|
||||
complexity: 10,
|
||||
})
|
||||
teamMembers(@Parent() team: Team): Promise<TeamMember[]> {
|
||||
return this.teamService.getTeamMembers(team.id);
|
||||
async teamMembers(@Parent() team: Team): Promise<TeamMember[]> {
|
||||
const startR = Date.now();
|
||||
const members = await this.teamService.getTeamMembers(team.id);
|
||||
const endR = Date.now();
|
||||
console.log('response generation: (teamMembers)', endR - startR, 'ms');
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
@ResolveField(() => TeamMemberRole, {
|
||||
@@ -64,41 +70,61 @@ export class TeamResolver {
|
||||
nullable: true,
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
myRole(
|
||||
async myRole(
|
||||
@Parent() team: Team,
|
||||
@GqlUser() user: AuthUser,
|
||||
): Promise<TeamMemberRole | null> {
|
||||
return this.teamService.getRoleOfUserInTeam(team.id, user.uid);
|
||||
const startR = Date.now();
|
||||
const role = await this.teamService.getRoleOfUserInTeam(team.id, user.uid);
|
||||
const endR = Date.now();
|
||||
console.log('response generation: (myRole)', endR - startR, 'ms');
|
||||
|
||||
return role;
|
||||
}
|
||||
|
||||
@ResolveField(() => Int, {
|
||||
description: 'The number of users with the OWNER role in the team',
|
||||
})
|
||||
ownersCount(@Parent() team: Team): Promise<number> {
|
||||
return this.teamService.getCountOfUsersWithRoleInTeam(
|
||||
async ownersCount(@Parent() team: Team): Promise<number> {
|
||||
const startR = Date.now();
|
||||
const count = await this.teamService.getCountOfUsersWithRoleInTeam(
|
||||
team.id,
|
||||
TeamMemberRole.OWNER,
|
||||
);
|
||||
const endR = Date.now();
|
||||
console.log('response generation: (ownersCount)', endR - startR, 'ms');
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@ResolveField(() => Int, {
|
||||
description: 'The number of users with the EDITOR role in the team',
|
||||
})
|
||||
editorsCount(@Parent() team: Team): Promise<number> {
|
||||
return this.teamService.getCountOfUsersWithRoleInTeam(
|
||||
async editorsCount(@Parent() team: Team): Promise<number> {
|
||||
const startR = Date.now();
|
||||
const count = await this.teamService.getCountOfUsersWithRoleInTeam(
|
||||
team.id,
|
||||
TeamMemberRole.EDITOR,
|
||||
);
|
||||
const endR = Date.now();
|
||||
console.log('response generation: (editorsCount)', endR - startR, 'ms');
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@ResolveField(() => Int, {
|
||||
description: 'The number of users with the VIEWER role in the team',
|
||||
})
|
||||
viewersCount(@Parent() team: Team): Promise<number> {
|
||||
return this.teamService.getCountOfUsersWithRoleInTeam(
|
||||
async viewersCount(@Parent() team: Team): Promise<number> {
|
||||
const startR = Date.now();
|
||||
const count = await this.teamService.getCountOfUsersWithRoleInTeam(
|
||||
team.id,
|
||||
TeamMemberRole.VIEWER,
|
||||
);
|
||||
const endR = Date.now();
|
||||
console.log('response generation: (viewersCount)', endR - startR, 'ms');
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// Query
|
||||
@@ -106,7 +132,7 @@ export class TeamResolver {
|
||||
description: 'List of teams that the executing user belongs to.',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
myTeams(
|
||||
async myTeams(
|
||||
@GqlUser() user: AuthUser,
|
||||
@Args({
|
||||
name: 'cursor',
|
||||
@@ -117,7 +143,15 @@ export class TeamResolver {
|
||||
})
|
||||
cursor?: string,
|
||||
): Promise<Team[]> {
|
||||
return this.teamService.getTeamsOfUser(user.uid, cursor ?? null);
|
||||
const startR = Date.now();
|
||||
const teams = await this.teamService.getTeamsOfUser(
|
||||
user.uid,
|
||||
cursor ?? null,
|
||||
);
|
||||
const endR = Date.now();
|
||||
console.log('response generation: (myTeams)', endR - startR, 'ms');
|
||||
|
||||
return teams;
|
||||
}
|
||||
|
||||
@Query(() => Team, {
|
||||
@@ -130,7 +164,7 @@ export class TeamResolver {
|
||||
TeamMemberRole.EDITOR,
|
||||
TeamMemberRole.OWNER,
|
||||
)
|
||||
team(
|
||||
async team(
|
||||
@Args({
|
||||
name: 'teamID',
|
||||
type: () => ID,
|
||||
@@ -138,7 +172,12 @@ export class TeamResolver {
|
||||
})
|
||||
teamID: string,
|
||||
): Promise<Team | null> {
|
||||
return this.teamService.getTeamWithID(teamID);
|
||||
const startR = Date.now();
|
||||
const team = await this.teamService.getTeamWithID(teamID);
|
||||
const endR = Date.now();
|
||||
console.log('response generation: (team)', endR - startR, 'ms');
|
||||
|
||||
return team;
|
||||
}
|
||||
|
||||
// Mutation
|
||||
@@ -151,7 +190,11 @@ export class TeamResolver {
|
||||
@Args({ name: 'name', description: 'Displayed name of the team' })
|
||||
name: string,
|
||||
): Promise<Team> {
|
||||
const startR = Date.now();
|
||||
const team = await this.teamService.createTeam(name, user.uid);
|
||||
const endR = Date.now();
|
||||
console.log('response generation: (createTeam)', endR - startR, 'ms');
|
||||
|
||||
if (E.isLeft(team)) throwErr(team.left);
|
||||
return team.right;
|
||||
}
|
||||
@@ -169,7 +212,11 @@ export class TeamResolver {
|
||||
})
|
||||
teamID: string,
|
||||
): Promise<boolean> {
|
||||
const startR = Date.now();
|
||||
const isUserLeft = await this.teamService.leaveTeam(teamID, user.uid);
|
||||
const endR = Date.now();
|
||||
console.log('response generation: (leaveTeam)', endR - startR, 'ms');
|
||||
|
||||
if (E.isLeft(isUserLeft)) throwErr(isUserLeft.left);
|
||||
return isUserLeft.right;
|
||||
}
|
||||
@@ -194,7 +241,11 @@ export class TeamResolver {
|
||||
})
|
||||
userUid: string,
|
||||
): Promise<boolean> {
|
||||
const startR = Date.now();
|
||||
const isRemoved = await this.teamService.leaveTeam(teamID, userUid);
|
||||
const endR = Date.now();
|
||||
console.log('response generation: (removeTeamMember)', endR - startR, 'ms');
|
||||
|
||||
if (E.isLeft(isRemoved)) throwErr(isRemoved.left);
|
||||
return isRemoved.right;
|
||||
}
|
||||
@@ -210,7 +261,11 @@ export class TeamResolver {
|
||||
@Args({ name: 'newName', description: 'The updated name of the team' })
|
||||
newName: string,
|
||||
): Promise<Team> {
|
||||
const startR = Date.now();
|
||||
const team = await this.teamService.renameTeam(teamID, newName);
|
||||
const endR = Date.now();
|
||||
console.log('response generation: (renameTeam)', endR - startR, 'ms');
|
||||
|
||||
if (E.isLeft(team)) throwErr(team.left);
|
||||
return team.right;
|
||||
}
|
||||
@@ -224,7 +279,11 @@ export class TeamResolver {
|
||||
@Args({ name: 'teamID', description: 'ID of the team', type: () => ID })
|
||||
teamID: string,
|
||||
): Promise<boolean> {
|
||||
const startR = Date.now();
|
||||
const isDeleted = await this.teamService.deleteTeam(teamID);
|
||||
const endR = Date.now();
|
||||
console.log('response generation: (deleteTeam)', endR - startR, 'ms');
|
||||
|
||||
if (E.isLeft(isDeleted)) throwErr(isDeleted.left);
|
||||
return isDeleted.right;
|
||||
}
|
||||
@@ -254,11 +313,19 @@ export class TeamResolver {
|
||||
})
|
||||
newRole: TeamMemberRole,
|
||||
): Promise<TeamMember> {
|
||||
const startR = Date.now();
|
||||
const teamMember = await this.teamService.updateTeamMemberRole(
|
||||
teamID,
|
||||
userUid,
|
||||
newRole,
|
||||
);
|
||||
const endR = Date.now();
|
||||
console.log(
|
||||
'response generation: (updateTeamMemberRole)',
|
||||
endR - startR,
|
||||
'ms',
|
||||
);
|
||||
|
||||
if (E.isLeft(teamMember)) throwErr(teamMember.left);
|
||||
return teamMember.right;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Injectable, OnModuleInit } from '@nestjs/common';
|
||||
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
|
||||
import { TeamMember, TeamMemberRole, Team } from './team.model';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { TeamMember as DbTeamMember } from '@prisma/client';
|
||||
@@ -23,6 +23,8 @@ import * as T from 'fp-ts/Task';
|
||||
import * as A from 'fp-ts/Array';
|
||||
import { throwErr } from 'src/utils';
|
||||
import { AuthUser } from '../types/AuthUser';
|
||||
import { PG_CONNECTION } from 'src/constants';
|
||||
import { Client } from 'pg';
|
||||
|
||||
@Injectable()
|
||||
export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
@@ -30,8 +32,11 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly userService: UserService,
|
||||
private readonly pubsub: PubSubService,
|
||||
@Inject(PG_CONNECTION) private conn: Client,
|
||||
) {}
|
||||
|
||||
enableRawSql: boolean = false;
|
||||
|
||||
onModuleInit() {
|
||||
this.userService.registerUserDataHandler(this);
|
||||
}
|
||||
@@ -52,12 +57,37 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
teamID: string,
|
||||
role: TeamMemberRole,
|
||||
): Promise<number> {
|
||||
return await this.prisma.teamMember.count({
|
||||
if (this.enableRawSql) {
|
||||
const startQ = Date.now();
|
||||
const count = await this.conn.query(
|
||||
`SELECT COUNT(*) FROM "TeamMember" WHERE "teamID" = '${teamID}' AND "role" = '${role}';`,
|
||||
);
|
||||
const endQ = Date.now();
|
||||
console.log(
|
||||
'getCountOfUsersWithRoleInTeam >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms >>>>>',
|
||||
count.rows,
|
||||
);
|
||||
return count.rows[0].count;
|
||||
}
|
||||
|
||||
const startQ = Date.now();
|
||||
const count = await this.prisma.teamMember.count({
|
||||
where: {
|
||||
teamID,
|
||||
role,
|
||||
},
|
||||
});
|
||||
const endQ = Date.now();
|
||||
console.log(
|
||||
'getCountOfUsersWithRoleInTeam >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms >>>>>',
|
||||
count,
|
||||
);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
async addMemberToTeamWithEmail(
|
||||
@@ -77,6 +107,11 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
uid: string,
|
||||
role: TeamMemberRole,
|
||||
): Promise<TeamMember> {
|
||||
const tm = await this.conn.query(
|
||||
`INSERT INTO "TeamMember" (id, userUid, teamID, role) VALUES ('${new Date().toISOString()}', '${uid}', '${teamID}', '${role}') RETURNING *;`,
|
||||
);
|
||||
console.log('addMemberToTeam >>>>>>>>>>', tm.rows[0]);
|
||||
|
||||
const teamMember = await this.prisma.teamMember.create({
|
||||
data: {
|
||||
userUid: uid,
|
||||
@@ -101,6 +136,31 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
}
|
||||
|
||||
async deleteTeam(teamID: string): Promise<E.Left<string> | E.Right<boolean>> {
|
||||
if (this.enableRawSql) {
|
||||
const startQ = Date.now();
|
||||
const t = await this.conn.query(
|
||||
`SELECT * FROM "Team" WHERE "id" = '${teamID}'`,
|
||||
);
|
||||
if (t.rows.length === 0) return E.left(TEAM_INVALID_ID);
|
||||
|
||||
await this.conn.query(
|
||||
`DELETE FROM "TeamMember" WHERE "teamID" = '${teamID}' RETURNING *`,
|
||||
);
|
||||
|
||||
await this.conn.query(`DELETE FROM "Team" WHERE "id" = '${teamID}'`);
|
||||
const endQ = Date.now();
|
||||
console.log(
|
||||
'deleteTeam >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms',
|
||||
'>>>>>',
|
||||
t.rows[0],
|
||||
);
|
||||
|
||||
return E.right(true);
|
||||
}
|
||||
|
||||
const startQ = Date.now();
|
||||
const team = await this.prisma.team.findUnique({
|
||||
where: {
|
||||
id: teamID,
|
||||
@@ -119,6 +179,8 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
id: teamID,
|
||||
},
|
||||
});
|
||||
const endQ = Date.now();
|
||||
console.log('deleteTeam >>>>>>>>>>', endQ - startQ, 'ms', '>>>>>', team);
|
||||
|
||||
return E.right(true);
|
||||
}
|
||||
@@ -135,6 +197,26 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
const isValidTitle = this.validateTeamName(newName);
|
||||
if (E.isLeft(isValidTitle)) return isValidTitle;
|
||||
|
||||
if (this.enableRawSql) {
|
||||
const startQ = Date.now();
|
||||
const ut = await this.conn.query(
|
||||
`UPDATE "Team" SET "name" = '${newName}' WHERE "id" = '${teamID}' RETURNING *`,
|
||||
);
|
||||
const endQ = Date.now();
|
||||
console.log(
|
||||
'renameTeam >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms',
|
||||
'>>>>>',
|
||||
ut.rows[0],
|
||||
);
|
||||
|
||||
return E.right(<Team>{
|
||||
id: ut.rows[0].id,
|
||||
name: ut.rows[0].name,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const updatedTeam = await this.prisma.team.update({
|
||||
where: {
|
||||
@@ -156,6 +238,48 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
userUid: string,
|
||||
newRole: TeamMemberRole,
|
||||
): Promise<E.Left<string> | E.Right<TeamMember>> {
|
||||
if (this.enableRawSql) {
|
||||
const startQ = Date.now();
|
||||
const oc = await this.conn.query(
|
||||
`SELECT COUNT(*) FROM "TeamMember" WHERE "teamID" = '${teamID}' AND "role" = '${TeamMemberRole.OWNER}';`,
|
||||
);
|
||||
const tm = await this.conn.query(
|
||||
`SELECT * FROM "TeamMember" WHERE "teamID" = '${teamID}' AND "userUid" = '${userUid}'`,
|
||||
);
|
||||
if (tm.rows.length === 0) return E.left(TEAM_MEMBER_NOT_FOUND);
|
||||
|
||||
const ownerCount = oc.rows[0].count;
|
||||
if (
|
||||
tm.rows[0].role === TeamMemberRole.OWNER &&
|
||||
newRole != TeamMemberRole.OWNER &&
|
||||
ownerCount === 1
|
||||
) {
|
||||
return E.left(TEAM_ONLY_ONE_OWNER);
|
||||
}
|
||||
|
||||
const utm = await this.conn.query(
|
||||
`UPDATE "teamMember" SET "role" = '${newRole}' WHERE "teamID" = '${teamID}' AND "userUid" = '${userUid}'`,
|
||||
);
|
||||
const endQ = Date.now();
|
||||
console.log(
|
||||
'updateTeamMemberRole >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms',
|
||||
'>>>>>',
|
||||
utm.rows[0],
|
||||
);
|
||||
|
||||
const updatedMember: TeamMember = {
|
||||
membershipID: utm.rows[0].id,
|
||||
userUid: utm.rows[0].userUid,
|
||||
role: TeamMemberRole[utm.rows[0].role],
|
||||
};
|
||||
this.pubsub.publish(`team/${teamID}/member_updated`, updatedMember);
|
||||
|
||||
return E.right(updatedMember);
|
||||
}
|
||||
|
||||
const startQ = Date.now();
|
||||
const ownerCount = await this.prisma.teamMember.count({
|
||||
where: {
|
||||
teamID,
|
||||
@@ -192,6 +316,14 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
role: newRole,
|
||||
},
|
||||
});
|
||||
const endQ = Date.now();
|
||||
console.log(
|
||||
'updateTeamMemberRole >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms',
|
||||
'>>>>>',
|
||||
result,
|
||||
);
|
||||
|
||||
const updatedMember: TeamMember = {
|
||||
membershipID: result.id,
|
||||
@@ -208,6 +340,30 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
teamID: string,
|
||||
userUid: string,
|
||||
): Promise<E.Left<string> | E.Right<boolean>> {
|
||||
if (this.enableRawSql) {
|
||||
const oc = await this.conn.query(
|
||||
`SELECT COUNT(*) FROM "TeamMember" WHERE "teamID" = '${teamID}' AND "role" = '${TeamMemberRole.OWNER}';`,
|
||||
);
|
||||
const ownerCount = oc.rows[0].count;
|
||||
console.log('leaveTeam >>>>>>>>>>', oc.rows);
|
||||
|
||||
const member = await this.getTeamMember(teamID, userUid);
|
||||
if (!member) return E.left(TEAM_INVALID_ID_OR_USER);
|
||||
|
||||
if (ownerCount === 1 && member.role === TeamMemberRole.OWNER) {
|
||||
return E.left(TEAM_ONLY_ONE_OWNER);
|
||||
}
|
||||
|
||||
const dtm = await this.conn.query(
|
||||
`DELETE FROM "TeamMember" WHERE "teamID" = '${teamID}' AND "userUid" = '${userUid}'`,
|
||||
);
|
||||
console.log('leaveTeam >>>>>>>>>>', dtm);
|
||||
|
||||
this.pubsub.publish(`team/${teamID}/member_removed`, userUid);
|
||||
|
||||
return E.right(true);
|
||||
}
|
||||
|
||||
const ownerCount = await this.prisma.teamMember.count({
|
||||
where: {
|
||||
teamID,
|
||||
@@ -248,6 +404,34 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
const isValidName = this.validateTeamName(name);
|
||||
if (E.isLeft(isValidName)) return isValidName;
|
||||
|
||||
if (this.enableRawSql) {
|
||||
const startQ = Date.now();
|
||||
const t = await this.conn.query(
|
||||
`INSERT INTO "Team" (id, name) VALUES ('${new Date().toISOString()}', '${name}') RETURNING *`,
|
||||
);
|
||||
const tm = await this.conn.query(
|
||||
`INSERT INTO "TeamMember" ("id", "userUid", "teamID", "role") VALUES ('${new Date().toISOString()}', '${creatorUid}' , '${
|
||||
t.rows[0].id
|
||||
}', '${TeamMemberRole.OWNER}') RETURNING *`,
|
||||
);
|
||||
const endQ = Date.now();
|
||||
``;
|
||||
console.log(
|
||||
'createTeam >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms',
|
||||
'>>>>>',
|
||||
t.rows[0],
|
||||
tm.rows[0],
|
||||
);
|
||||
|
||||
return E.right(<Team>{
|
||||
id: t.rows[0].id,
|
||||
name: t.rows[0].name,
|
||||
});
|
||||
}
|
||||
|
||||
const startQ = Date.now();
|
||||
const team = await this.prisma.team.create({
|
||||
data: {
|
||||
name: name,
|
||||
@@ -259,12 +443,42 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
},
|
||||
},
|
||||
});
|
||||
const endQ = Date.now();
|
||||
console.log('createTeam >>>>>>>>>> ', endQ - startQ, 'ms', '>>>>>', team);
|
||||
|
||||
return E.right(team);
|
||||
}
|
||||
|
||||
async getTeamsOfUser(uid: string, cursor: string | null): Promise<Team[]> {
|
||||
if (this.enableRawSql) {
|
||||
const startQ = Date.now();
|
||||
let users;
|
||||
if (cursor) {
|
||||
users = await this.conn.query(
|
||||
`SELECT * FROM "TeamMember" LEFT JOIN "Team" ON "TeamMember"."teamID" = "Team"."id" WHERE "TeamMember"."userUid" = '${uid}' and "TeamMember"."teamID" > '${cursor}' LIMIT 10`,
|
||||
);
|
||||
} else {
|
||||
users = await this.conn.query(
|
||||
`SELECT * FROM "TeamMember" LEFT JOIN "Team" ON "TeamMember"."teamID" = "Team"."id" WHERE "TeamMember"."userUid" = '${uid}' LIMIT 10`,
|
||||
);
|
||||
}
|
||||
const endQ = Date.now();
|
||||
console.log(
|
||||
'getTeamsOfUser >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms',
|
||||
'>>>>>',
|
||||
users.rows,
|
||||
);
|
||||
|
||||
return users.rows.map((entry) => ({
|
||||
id: entry.teamID,
|
||||
name: entry.name,
|
||||
}));
|
||||
}
|
||||
|
||||
if (!cursor) {
|
||||
const startQ = Date.now();
|
||||
const entries = await this.prisma.teamMember.findMany({
|
||||
take: 10,
|
||||
where: {
|
||||
@@ -274,9 +488,18 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
team: true,
|
||||
},
|
||||
});
|
||||
const endQ = Date.now();
|
||||
console.log(
|
||||
'getTeamsOfUser >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms',
|
||||
'>>>>>',
|
||||
entries,
|
||||
);
|
||||
|
||||
return entries.map((entry) => entry.team);
|
||||
} else {
|
||||
const startQ = Date.now();
|
||||
const entries = await this.prisma.teamMember.findMany({
|
||||
take: 10,
|
||||
skip: 1,
|
||||
@@ -293,17 +516,56 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
team: true,
|
||||
},
|
||||
});
|
||||
const endQ = Date.now();
|
||||
console.log(
|
||||
'getTeamsOfUser >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms',
|
||||
'>>>>>',
|
||||
entries,
|
||||
);
|
||||
|
||||
return entries.map((entry) => entry.team);
|
||||
}
|
||||
}
|
||||
|
||||
async getTeamWithID(teamID: string): Promise<Team | null> {
|
||||
if (this.enableRawSql) {
|
||||
const startQ = Date.now();
|
||||
const team = await this.conn.query(
|
||||
`SELECT * FROM "Team" WHERE "id" = '${teamID}'`,
|
||||
);
|
||||
const endQ = Date.now();
|
||||
console.log(
|
||||
'getTeamWithID >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms',
|
||||
'>>>>>',
|
||||
team.rows,
|
||||
);
|
||||
|
||||
if (team.rows.length === 0) return null;
|
||||
return <Team>{
|
||||
id: team.rows[0].id,
|
||||
name: team.rows[0].name,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const startQ = Date.now();
|
||||
const team = await this.prisma.team.findUnique({
|
||||
where: {
|
||||
id: teamID,
|
||||
},
|
||||
});
|
||||
const endQ = Date.now();
|
||||
console.log(
|
||||
'getTeamWithID >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms',
|
||||
'>>>>>',
|
||||
team,
|
||||
);
|
||||
|
||||
return team;
|
||||
} catch (_e) {
|
||||
@@ -353,7 +615,30 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
teamID: string,
|
||||
userUid: string,
|
||||
): Promise<TeamMember | null> {
|
||||
if (this.enableRawSql) {
|
||||
const startQ = Date.now();
|
||||
const member = await this.conn.query(
|
||||
`SELECT * FROM "TeamMember" WHERE "teamID" = '${teamID}' AND "userUid" = '${userUid}'`,
|
||||
);
|
||||
const endQ = Date.now();
|
||||
console.log(
|
||||
'getTeamMember >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms',
|
||||
'>>>>>',
|
||||
member.rows,
|
||||
);
|
||||
|
||||
if (member.rows.length === 0) return null;
|
||||
return <TeamMember>{
|
||||
membershipID: member.rows[0].id,
|
||||
userUid: member.rows[0].userUid,
|
||||
role: TeamMemberRole[member.rows[0].role],
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const startQ = Date.now();
|
||||
const teamMember = await this.prisma.teamMember.findUnique({
|
||||
where: {
|
||||
teamID_userUid: {
|
||||
@@ -362,6 +647,14 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
},
|
||||
},
|
||||
});
|
||||
const endQ = Date.now();
|
||||
console.log(
|
||||
'getTeamMember >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms',
|
||||
'>>>>>',
|
||||
teamMember,
|
||||
);
|
||||
|
||||
if (!teamMember) return null;
|
||||
|
||||
@@ -433,11 +726,44 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
}
|
||||
|
||||
async getTeamMembers(teamID: string): Promise<TeamMember[]> {
|
||||
if (this.enableRawSql) {
|
||||
const startQ = Date.now();
|
||||
const members = await this.conn.query(
|
||||
`SELECT * FROM "TeamMember" WHERE "teamID" = '${teamID}'`,
|
||||
);
|
||||
const endQ = Date.now();
|
||||
|
||||
console.log(
|
||||
'getTeamMembers >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms',
|
||||
'>>>>>',
|
||||
members.rows,
|
||||
);
|
||||
|
||||
return members.rows.map((entry) => {
|
||||
return {
|
||||
membershipID: entry.id,
|
||||
userUid: entry.userUid,
|
||||
role: TeamMemberRole[entry.role],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const startQ = Date.now();
|
||||
const dbTeamMembers = await this.prisma.teamMember.findMany({
|
||||
where: {
|
||||
teamID,
|
||||
},
|
||||
});
|
||||
const endQ = Date.now();
|
||||
console.log(
|
||||
'getTeamMembers >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms',
|
||||
'>>>>>',
|
||||
dbTeamMembers,
|
||||
);
|
||||
|
||||
const members = dbTeamMembers.map(
|
||||
(entry) =>
|
||||
@@ -470,8 +796,39 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
teamID: string,
|
||||
cursor: string | null,
|
||||
): Promise<TeamMember[]> {
|
||||
if (this.enableRawSql) {
|
||||
const startQ = Date.now();
|
||||
let members;
|
||||
if (cursor) {
|
||||
members = await this.conn.query(
|
||||
`SELECT * FROM "TeamMember" WHERE "teamID" = '${teamID}' AND "id" > '${cursor}' LIMIT 10`,
|
||||
);
|
||||
}
|
||||
members = await this.conn.query(
|
||||
`SELECT * FROM "TeamMember" WHERE "teamID" = '${teamID}' LIMIT 10`,
|
||||
);
|
||||
const endQ = Date.now();
|
||||
console.log(
|
||||
'getMembersOfTeam >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms',
|
||||
'>>>>>',
|
||||
members.rows,
|
||||
);
|
||||
|
||||
return members.rows.map(
|
||||
(entry) =>
|
||||
<TeamMember>{
|
||||
membershipID: entry.id,
|
||||
userUid: entry.userUid,
|
||||
role: TeamMemberRole[entry.role],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let teamMembers: DbTeamMember[];
|
||||
|
||||
const startQ = Date.now();
|
||||
if (!cursor) {
|
||||
teamMembers = await this.prisma.teamMember.findMany({
|
||||
take: 10,
|
||||
@@ -491,6 +848,14 @@ export class TeamService implements UserDataHandler, OnModuleInit {
|
||||
},
|
||||
});
|
||||
}
|
||||
const endQ = Date.now();
|
||||
console.log(
|
||||
'getMembersOfTeam >>>>>>>>>>',
|
||||
endQ - startQ,
|
||||
'ms',
|
||||
'>>>>>',
|
||||
teamMembers,
|
||||
);
|
||||
|
||||
const members = teamMembers.map(
|
||||
(entry) =>
|
||||
|
||||
Reference in New Issue
Block a user