From e3d9710792a9dafb1a23e0c626f362d8b17e1143 Mon Sep 17 00:00:00 2001 From: rfquin Date: Wed, 10 Sep 2025 17:39:55 +1000 Subject: [PATCH 01/17] Add inviteCode to user schema & generate on user creation --- server/prisma/schema.prisma | 1 + server/src/auth/auth.service.ts | 3 +++ server/src/auth/oidc.strategy.ts | 2 ++ server/src/user/types.ts | 1 + server/src/user/user.service.ts | 34 ++++++++++++++++++++++++++++++++ 5 files changed, 41 insertions(+) diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma index 4fba3b329..8be35a94e 100644 --- a/server/prisma/schema.prisma +++ b/server/prisma/schema.prisma @@ -24,6 +24,7 @@ model User { firstName String lastName String + inviteCode String @unique profilePictureUrl String? timetables Timetable[] settings Settings? diff --git a/server/src/auth/auth.service.ts b/server/src/auth/auth.service.ts index 9c6d9d696..a6b19dc66 100644 --- a/server/src/auth/auth.service.ts +++ b/server/src/auth/auth.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from 'src/prisma/prisma.service'; import { AuthProvider, Prisma, User } from 'src/generated/prisma/client'; +import { UserService } from '../user/user.service'; import { Term } from 'src/timetable/types'; import { GraphqlService } from 'src/graphql/graphql.service'; @@ -17,6 +18,7 @@ export class AuthService { constructor( private readonly prisma: PrismaService, private readonly graphql: GraphqlService, + private readonly user: UserService, ) {} private readonly TIMETABLE_DEFAULT_NAME = 'My Timetable'; @@ -58,6 +60,7 @@ export class AuthService { authSubject: subject, firstName: params.firstName, lastName: params.lastName, + inviteCode: await this.user.generateUniqueInviteCode(), isGuest: params.isGuest, settings: { create: {} }, }, diff --git a/server/src/auth/oidc.strategy.ts b/server/src/auth/oidc.strategy.ts index 5add31be2..bc79309a3 100644 --- a/server/src/auth/oidc.strategy.ts +++ b/server/src/auth/oidc.strategy.ts @@ -13,6 +13,7 @@ import { import { AuthenticateOptions } from 'openid-client/build/passport'; import { promisify } from 'util'; import { AuthService } from './auth.service'; +import { UserService } from '../user/user.service'; const { Strategy } = // eslint-disable-next-line @typescript-eslint/no-require-imports require('openid-client/passport') as typeof import('openid-client/build/passport'); @@ -74,6 +75,7 @@ export class OidcStrategy extends PassportStrategy(Strategy, 'oidc') { const userData = userInfo.userData as { firstName: string; lastName: string; + inviteCode: string; zid: string; department: string; program: number; diff --git a/server/src/user/types.ts b/server/src/user/types.ts index 08515837a..70c6f450c 100644 --- a/server/src/user/types.ts +++ b/server/src/user/types.ts @@ -2,6 +2,7 @@ export class UserInfo { id: string; firstName: string; lastName: string; + inviteCode: string; profilePictureUrl?: string; } diff --git a/server/src/user/user.service.ts b/server/src/user/user.service.ts index 2df6f138a..902439f4a 100644 --- a/server/src/user/user.service.ts +++ b/server/src/user/user.service.ts @@ -25,6 +25,7 @@ export class UserService { id: data.id, firstName: data.firstName, lastName: data.lastName, + inviteCode: data.inviteCode, profilePictureUrl: data.profilePictureUrl ?? undefined, }; } @@ -72,4 +73,37 @@ export class UserService { }, }); } + + private async isInviteCodeAlreadyUsed(inviteCode: string): Promise { + const code = await this.prisma.user.findFirst({ + where: { + inviteCode: inviteCode, + }, + select: { + inviteCode: true, + }, + }); + + return code !== null; + } + + // Note: this does NOT guarantee uniqueness + private generateInviteCode(): string { + return new Array(6) + .fill(undefined) + .map(() => + Math.floor(Math.random() * 36) + .toString(36) + .toUpperCase(), + ) + .join(''); + } + + async generateUniqueInviteCode(): Promise { + let code = this.generateInviteCode(); + while (this.isInviteCodeAlreadyUsed(code)) { + code = this.generateInviteCode(); + } + return code; + } } From a86e82a6f0780a7300df280eb44d79ad0e50bc18 Mon Sep 17 00:00:00 2001 From: rfquin Date: Wed, 10 Sep 2025 18:08:27 +1000 Subject: [PATCH 02/17] Fix lint errors --- server/src/auth/oidc.strategy.ts | 1 - server/src/user/user.service.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/server/src/auth/oidc.strategy.ts b/server/src/auth/oidc.strategy.ts index bc79309a3..e406126d7 100644 --- a/server/src/auth/oidc.strategy.ts +++ b/server/src/auth/oidc.strategy.ts @@ -13,7 +13,6 @@ import { import { AuthenticateOptions } from 'openid-client/build/passport'; import { promisify } from 'util'; import { AuthService } from './auth.service'; -import { UserService } from '../user/user.service'; const { Strategy } = // eslint-disable-next-line @typescript-eslint/no-require-imports require('openid-client/passport') as typeof import('openid-client/build/passport'); diff --git a/server/src/user/user.service.ts b/server/src/user/user.service.ts index 902439f4a..f5c415366 100644 --- a/server/src/user/user.service.ts +++ b/server/src/user/user.service.ts @@ -101,7 +101,7 @@ export class UserService { async generateUniqueInviteCode(): Promise { let code = this.generateInviteCode(); - while (this.isInviteCodeAlreadyUsed(code)) { + while (await this.isInviteCodeAlreadyUsed(code)) { code = this.generateInviteCode(); } return code; From 0c2350d7bea61671c0d6a9e308c080b0893fd4f3 Mon Sep 17 00:00:00 2001 From: rfquin Date: Wed, 10 Sep 2025 18:15:30 +1000 Subject: [PATCH 03/17] Fix build error - add inviteCode to createGuest --- server/src/auth/auth.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/auth/auth.service.ts b/server/src/auth/auth.service.ts index a6b19dc66..b014af1f7 100644 --- a/server/src/auth/auth.service.ts +++ b/server/src/auth/auth.service.ts @@ -90,6 +90,7 @@ export class AuthService { firstName: params.firstName, lastName: params.lastName, isGuest: params.isGuest, + inviteCode: await this.user.generateUniqueInviteCode(), settings: { create: {} }, timetables: { create: availableTerms.map((availableTerm) => { From 73f950b251b45311c006a84dc8a9c641978d87d5 Mon Sep 17 00:00:00 2001 From: rfquin Date: Wed, 17 Sep 2025 18:30:15 +1000 Subject: [PATCH 04/17] Add optimisation & format annotations to user schema --- server/prisma/schema.prisma | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma index 8be35a94e..2a016e204 100644 --- a/server/prisma/schema.prisma +++ b/server/prisma/schema.prisma @@ -24,7 +24,7 @@ model User { firstName String lastName String - inviteCode String @unique + inviteCode String @unique @db.Char(6) profilePictureUrl String? timetables Timetable[] settings Settings? @@ -33,6 +33,8 @@ model User { lastLogin DateTime @default(now()) @@unique([authProvider, authSubject]) + @@index([inviteCode], name: "friendsId") + @@index([id], name: "userId") @@map("user") } From 52079ebb3e2de63213399844ae22b6fd07b95131 Mon Sep 17 00:00:00 2001 From: rfquin Date: Wed, 17 Sep 2025 18:57:00 +1000 Subject: [PATCH 05/17] Add friendship schema --- server/prisma/schema.prisma | 33 ++++---- server/src/friend/friend.controller.ts | 42 ++++++++++ server/src/friend/friend.module.ts | 11 +++ server/src/friend/friend.service.ts | 109 +++++++++++++++++++++++++ server/src/friend/types.ts | 18 ++++ 5 files changed, 196 insertions(+), 17 deletions(-) create mode 100644 server/src/friend/friend.controller.ts create mode 100644 server/src/friend/friend.module.ts create mode 100644 server/src/friend/friend.service.ts create mode 100644 server/src/friend/types.ts diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma index 2a016e204..105cab716 100644 --- a/server/prisma/schema.prisma +++ b/server/prisma/schema.prisma @@ -38,23 +38,22 @@ model User { @@map("user") } -// model FriendRequest { -// id String @id @default(uuid()) -// fromId String -// toId String - -// @@unique([fromId, toId]) -// @@map("friend_request") -// } - -// model Friendship { -// id String @id @default(uuid()) -// user1Id String -// user2Id String - -// @@unique([user1Id, user2Id]) -// @@map("friendship") -// } +model Friendship { + id String @id @default(uuid()) + user1Id String + user2Id String + status Status + + @@unique([user1Id, user2Id]) + @@index([id], name: "friendshipId") + @@map("friendship") +} + +enum Status { + REQ_UID1 + REQ_UID2 + FRIEND +} model Settings { user User @relation(fields: [userId], references: [id], onDelete: Cascade) diff --git a/server/src/friend/friend.controller.ts b/server/src/friend/friend.controller.ts new file mode 100644 index 000000000..25584d71b --- /dev/null +++ b/server/src/friend/friend.controller.ts @@ -0,0 +1,42 @@ +import { Body, Controller, Get, Post, Req, UseGuards } from '@nestjs/common'; +import { UserService } from './user.service'; +import { AuthenticatedGuard } from 'src/auth/authenticated.guard'; +import { UserSettings } from './types'; +import { AuthenticatedRequest } from 'src/auth/auth.controller'; + +@Controller('user') +export class UserController { + constructor(private userService: UserService) {} + + @Get('profile') + @UseGuards(AuthenticatedGuard) + async getProfile(@Req() req: AuthenticatedRequest) { + const userInfo = await this.userService.getUserInfo(req.user.id); + return userInfo; + } + + @Post('profile/picture') + @UseGuards(AuthenticatedGuard) + async setProfilePicture( + @Req() req: AuthenticatedRequest, + @Body('url') url: string, + ) { + await this.userService.setProfilePicture(req.user.id, url); + } + + @Get('settings') + @UseGuards(AuthenticatedGuard) + async getSettings(@Req() req: AuthenticatedRequest) { + const settings = await this.userService.getSettings(req.user.id); + return settings; + } + + @Post('settings') + @UseGuards(AuthenticatedGuard) + async setSettings( + @Req() req: AuthenticatedRequest, + @Body() settings: UserSettings, + ) { + await this.userService.setSettings(req.user.id, settings); + } +} diff --git a/server/src/friend/friend.module.ts b/server/src/friend/friend.module.ts new file mode 100644 index 000000000..e93cb4341 --- /dev/null +++ b/server/src/friend/friend.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { GraphqlService } from 'src/graphql/graphql.service'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { UserService } from './user.service'; +import { UserController } from './user.controller'; + +@Module({ + providers: [UserService, PrismaService, GraphqlService], + controllers: [UserController], +}) +export class UserModule {} diff --git a/server/src/friend/friend.service.ts b/server/src/friend/friend.service.ts new file mode 100644 index 000000000..f5c415366 --- /dev/null +++ b/server/src/friend/friend.service.ts @@ -0,0 +1,109 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { UserInfo, UserSettings } from './types'; + +@Injectable({}) +export class UserService { + constructor(private readonly prisma: PrismaService) {} + + async deleteUser(userId: string): Promise { + await this.prisma.user.delete({ + where: { + id: userId, + }, + }); + } + + async getUserInfo(userId: string): Promise { + const data = await this.prisma.user.findUniqueOrThrow({ + where: { + id: userId, + }, + }); + + return { + id: data.id, + firstName: data.firstName, + lastName: data.lastName, + inviteCode: data.inviteCode, + profilePictureUrl: data.profilePictureUrl ?? undefined, + }; + } + + async setProfilePicture( + userId: string, + profilePictureUrl: string, + ): Promise { + await this.prisma.user.update({ + where: { + id: userId, + }, + data: { + profilePictureUrl, + }, + }); + } + + async getSettings(userId: string): Promise { + const data = await this.prisma.user.findUniqueOrThrow({ + where: { + id: userId, + }, + select: { + settings: true, + }, + }); + + if (!data.settings) { + throw new Error('User settings not found'); + } + + return data.settings; + } + + async setSettings(userId: string, settings: UserSettings): Promise { + await this.prisma.user.update({ + where: { + id: userId, + }, + data: { + settings: { + update: settings, + }, + }, + }); + } + + private async isInviteCodeAlreadyUsed(inviteCode: string): Promise { + const code = await this.prisma.user.findFirst({ + where: { + inviteCode: inviteCode, + }, + select: { + inviteCode: true, + }, + }); + + return code !== null; + } + + // Note: this does NOT guarantee uniqueness + private generateInviteCode(): string { + return new Array(6) + .fill(undefined) + .map(() => + Math.floor(Math.random() * 36) + .toString(36) + .toUpperCase(), + ) + .join(''); + } + + async generateUniqueInviteCode(): Promise { + let code = this.generateInviteCode(); + while (await this.isInviteCodeAlreadyUsed(code)) { + code = this.generateInviteCode(); + } + return code; + } +} diff --git a/server/src/friend/types.ts b/server/src/friend/types.ts new file mode 100644 index 000000000..70c6f450c --- /dev/null +++ b/server/src/friend/types.ts @@ -0,0 +1,18 @@ +export class UserInfo { + id: string; + firstName: string; + lastName: string; + inviteCode: string; + profilePictureUrl?: string; +} + +export class UserSettings { + preferredTheme: string; + is12HourMode: boolean; + isDarkMode: boolean; + isSquareEdges: boolean; + hideFullClasses: boolean; + hideClassInfo: boolean; + unscheduleClassesByDefault: boolean; + hideExamClasses: boolean; +} From 7cf33118c778adc341136e0fb190ff87260925c6 Mon Sep 17 00:00:00 2001 From: rfquin Date: Wed, 17 Sep 2025 19:18:44 +1000 Subject: [PATCH 06/17] Remove accidentally added new module --- server/src/friend/friend.controller.ts | 42 ---------- server/src/friend/friend.module.ts | 11 --- server/src/friend/friend.service.ts | 109 ------------------------- server/src/friend/types.ts | 18 ---- 4 files changed, 180 deletions(-) delete mode 100644 server/src/friend/friend.controller.ts delete mode 100644 server/src/friend/friend.module.ts delete mode 100644 server/src/friend/friend.service.ts delete mode 100644 server/src/friend/types.ts diff --git a/server/src/friend/friend.controller.ts b/server/src/friend/friend.controller.ts deleted file mode 100644 index 25584d71b..000000000 --- a/server/src/friend/friend.controller.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Body, Controller, Get, Post, Req, UseGuards } from '@nestjs/common'; -import { UserService } from './user.service'; -import { AuthenticatedGuard } from 'src/auth/authenticated.guard'; -import { UserSettings } from './types'; -import { AuthenticatedRequest } from 'src/auth/auth.controller'; - -@Controller('user') -export class UserController { - constructor(private userService: UserService) {} - - @Get('profile') - @UseGuards(AuthenticatedGuard) - async getProfile(@Req() req: AuthenticatedRequest) { - const userInfo = await this.userService.getUserInfo(req.user.id); - return userInfo; - } - - @Post('profile/picture') - @UseGuards(AuthenticatedGuard) - async setProfilePicture( - @Req() req: AuthenticatedRequest, - @Body('url') url: string, - ) { - await this.userService.setProfilePicture(req.user.id, url); - } - - @Get('settings') - @UseGuards(AuthenticatedGuard) - async getSettings(@Req() req: AuthenticatedRequest) { - const settings = await this.userService.getSettings(req.user.id); - return settings; - } - - @Post('settings') - @UseGuards(AuthenticatedGuard) - async setSettings( - @Req() req: AuthenticatedRequest, - @Body() settings: UserSettings, - ) { - await this.userService.setSettings(req.user.id, settings); - } -} diff --git a/server/src/friend/friend.module.ts b/server/src/friend/friend.module.ts deleted file mode 100644 index e93cb4341..000000000 --- a/server/src/friend/friend.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Module } from '@nestjs/common'; -import { GraphqlService } from 'src/graphql/graphql.service'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { UserService } from './user.service'; -import { UserController } from './user.controller'; - -@Module({ - providers: [UserService, PrismaService, GraphqlService], - controllers: [UserController], -}) -export class UserModule {} diff --git a/server/src/friend/friend.service.ts b/server/src/friend/friend.service.ts deleted file mode 100644 index f5c415366..000000000 --- a/server/src/friend/friend.service.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { UserInfo, UserSettings } from './types'; - -@Injectable({}) -export class UserService { - constructor(private readonly prisma: PrismaService) {} - - async deleteUser(userId: string): Promise { - await this.prisma.user.delete({ - where: { - id: userId, - }, - }); - } - - async getUserInfo(userId: string): Promise { - const data = await this.prisma.user.findUniqueOrThrow({ - where: { - id: userId, - }, - }); - - return { - id: data.id, - firstName: data.firstName, - lastName: data.lastName, - inviteCode: data.inviteCode, - profilePictureUrl: data.profilePictureUrl ?? undefined, - }; - } - - async setProfilePicture( - userId: string, - profilePictureUrl: string, - ): Promise { - await this.prisma.user.update({ - where: { - id: userId, - }, - data: { - profilePictureUrl, - }, - }); - } - - async getSettings(userId: string): Promise { - const data = await this.prisma.user.findUniqueOrThrow({ - where: { - id: userId, - }, - select: { - settings: true, - }, - }); - - if (!data.settings) { - throw new Error('User settings not found'); - } - - return data.settings; - } - - async setSettings(userId: string, settings: UserSettings): Promise { - await this.prisma.user.update({ - where: { - id: userId, - }, - data: { - settings: { - update: settings, - }, - }, - }); - } - - private async isInviteCodeAlreadyUsed(inviteCode: string): Promise { - const code = await this.prisma.user.findFirst({ - where: { - inviteCode: inviteCode, - }, - select: { - inviteCode: true, - }, - }); - - return code !== null; - } - - // Note: this does NOT guarantee uniqueness - private generateInviteCode(): string { - return new Array(6) - .fill(undefined) - .map(() => - Math.floor(Math.random() * 36) - .toString(36) - .toUpperCase(), - ) - .join(''); - } - - async generateUniqueInviteCode(): Promise { - let code = this.generateInviteCode(); - while (await this.isInviteCodeAlreadyUsed(code)) { - code = this.generateInviteCode(); - } - return code; - } -} diff --git a/server/src/friend/types.ts b/server/src/friend/types.ts deleted file mode 100644 index 70c6f450c..000000000 --- a/server/src/friend/types.ts +++ /dev/null @@ -1,18 +0,0 @@ -export class UserInfo { - id: string; - firstName: string; - lastName: string; - inviteCode: string; - profilePictureUrl?: string; -} - -export class UserSettings { - preferredTheme: string; - is12HourMode: boolean; - isDarkMode: boolean; - isSquareEdges: boolean; - hideFullClasses: boolean; - hideClassInfo: boolean; - unscheduleClassesByDefault: boolean; - hideExamClasses: boolean; -} From fc92d144e99413fa02593429093e476e77f53952 Mon Sep 17 00:00:00 2001 From: rfquin Date: Wed, 24 Sep 2025 16:11:58 +1000 Subject: [PATCH 07/17] WIP: Add friendship.controller.ts stubs --- .../src/friendship/friendship.controller.ts | 100 ++++++++++++++++++ server/src/friendship/friendship.module.ts | 10 ++ server/src/friendship/friendship.service.ts | 9 ++ server/src/friendship/types.ts | 32 ++++++ 4 files changed, 151 insertions(+) create mode 100644 server/src/friendship/friendship.controller.ts create mode 100644 server/src/friendship/friendship.module.ts create mode 100644 server/src/friendship/friendship.service.ts create mode 100644 server/src/friendship/types.ts diff --git a/server/src/friendship/friendship.controller.ts b/server/src/friendship/friendship.controller.ts new file mode 100644 index 000000000..78ce591c8 --- /dev/null +++ b/server/src/friendship/friendship.controller.ts @@ -0,0 +1,100 @@ +import { + Controller, + Get, + Body, + Post, + Req, + UseGuards, + Param, +} from '@nestjs/common'; +import { FriendshipService } from './friendship.service'; +import { + CreateFriendRequestDto, + CancelFriendRequestDto, + AcceptFriendRequestDto, + RejectFriendRequestDto, + Status, +} from './types'; +import { AuthenticatedGuard } from 'src/auth/authenticated.guard'; +import { AuthenticatedRequest } from 'src/auth/auth.controller'; + +@Controller('friendships') +export class FriendshipController { + constructor(private readonly friendshipService: FriendshipService) {} + + // [X] @post createFriendRequest + // [X] @post cancelFriendRequest + // [X] @get getOutgoingFriendRequests + // [X] @get getIncomingFriendRequests + // [X] @post acceptFriendRequest + // [X] @post rejectFriendRequest + // [X] @post removeMutualFriend + + @Get() + @UseGuards(AuthenticatedGuard) + getFriendRequest( + @Req() req: AuthenticatedRequest, + @Param('id') friendshipId: string, + ): string { + // todo: call this.friendshipService.getFriendship(userId, friendshipId) + throw new Error('Unimplemented Route: getFriendRequest'); + } + + @Post() + @UseGuards(AuthenticatedGuard) + createFriendRequest( + @Req() req: AuthenticatedRequest, + @Body() createFriendRequest: CreateFriendRequestDto, + ) { + // todo: call this.friendshipService.createFriendship(...) + throw new Error('Unimplemented Route: createFriendRequest'); + } + + @Post() + @UseGuards(AuthenticatedGuard) + cancelFriendRequest( + @Req() req: AuthenticatedRequest, + @Body() cancelFriendRequest: CancelFriendRequestDto, + ) { + // todo: handle this + throw new Error('Unimplemented Route: createFriendRequest'); + } + + @Get() + @UseGuards(AuthenticatedGuard) + getOutgoingFriendRequests(@Req() req: AuthenticatedRequest): string { + // todo: call this.friendshipService.getOutgoingFriendRequests(...) + throw new Error('Unimplemented Route: getOutgoingFriendRequests'); + } + + @Get() + @UseGuards(AuthenticatedGuard) + getFriendRequests(@Req() req: AuthenticatedRequest): string { + // todo: call this.friendshipService.getIncomingFriendRequests(...) + // and other things :D + throw new Error('Unimplemented Route: getIncomingFriendRequests'); + } + + @Post() + @UseGuards(AuthenticatedGuard) + acceptFriendRequest( + @Req() req: AuthenticatedRequest, + @Body() acceptFriendRequest: AcceptFriendRequestDto, + ) { + // todo: call this.friendshipService.createFriendship(...) + throw new Error('Unimplemented Route: acceptFriendRequest'); + } + + @Post() + @UseGuards(AuthenticatedGuard) + rejectFriendRequest( + @Req() req: AuthenticatedRequest, + @Body() rejectFriendRequest: RejectFriendRequestDto, + ) { + // todo: call this.friendshipService.rejectFriendRequest(...) + throw new Error('Unimplemented Route: rejectFriendRequest'); + } + + // Todo: Do we really need a rejectExisting/mutual friend? + // Does this require a seperate rest request? +} diff --git a/server/src/friendship/friendship.module.ts b/server/src/friendship/friendship.module.ts new file mode 100644 index 000000000..dad63dbb7 --- /dev/null +++ b/server/src/friendship/friendship.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { GraphqlService } from 'src/graphql/graphql.service'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { FriendshipController } from './friendship.controller'; + +@Module({ + providers: [PrismaService, GraphqlService], + controllers: [FriendshipController], +}) +export class FriendshipModule {} diff --git a/server/src/friendship/friendship.service.ts b/server/src/friendship/friendship.service.ts new file mode 100644 index 000000000..d31a9ed64 --- /dev/null +++ b/server/src/friendship/friendship.service.ts @@ -0,0 +1,9 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { Friendship } from './types'; + +@Injectable({}) +export class FriendshipService { + constructor(private readonly prisma: PrismaService) {} + // async +} diff --git a/server/src/friendship/types.ts b/server/src/friendship/types.ts new file mode 100644 index 000000000..38de1ff52 --- /dev/null +++ b/server/src/friendship/types.ts @@ -0,0 +1,32 @@ +// todo: Should these be combined or kept explicit? +// Im just doing this explicit but now but will combine them :D +// Union types? + +export class CreateFriendRequestDto { + requesteeCode: string; +} + +export class CancelFriendRequestDto { + requestorCode: string; +} + +export class AcceptFriendRequestDto { + requestorCode: string; +} + +export class RejectFriendRequestDto { + requestorCode: string; +} + +export class Friendship { + id: string; + user1Id: string; + user2Id: string; + status: Status; +} + +export enum Status { + REQ_UID1, + REQ_UID2, + FRIEND, +} From bdb92bd4cf1d2a9e648f9099bf7412beb080e8d9 Mon Sep 17 00:00:00 2001 From: rfquin Date: Wed, 1 Oct 2025 18:02:02 +1000 Subject: [PATCH 08/17] Remove redundant timetable types in timetable/types.ts --- server/src/friendship/types.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/server/src/friendship/types.ts b/server/src/friendship/types.ts index 38de1ff52..87f84a4a1 100644 --- a/server/src/friendship/types.ts +++ b/server/src/friendship/types.ts @@ -17,16 +17,3 @@ export class AcceptFriendRequestDto { export class RejectFriendRequestDto { requestorCode: string; } - -export class Friendship { - id: string; - user1Id: string; - user2Id: string; - status: Status; -} - -export enum Status { - REQ_UID1, - REQ_UID2, - FRIEND, -} From 0269e8cde638f08448e6f8c38852092a10a20513 Mon Sep 17 00:00:00 2001 From: rfquin Date: Wed, 1 Oct 2025 18:06:28 +1000 Subject: [PATCH 09/17] Oop I made an error - please don't kick me off devsoc --- server/src/friendship/friendship.controller.ts | 1 - server/src/friendship/friendship.service.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/server/src/friendship/friendship.controller.ts b/server/src/friendship/friendship.controller.ts index 78ce591c8..d8df2b536 100644 --- a/server/src/friendship/friendship.controller.ts +++ b/server/src/friendship/friendship.controller.ts @@ -13,7 +13,6 @@ import { CancelFriendRequestDto, AcceptFriendRequestDto, RejectFriendRequestDto, - Status, } from './types'; import { AuthenticatedGuard } from 'src/auth/authenticated.guard'; import { AuthenticatedRequest } from 'src/auth/auth.controller'; diff --git a/server/src/friendship/friendship.service.ts b/server/src/friendship/friendship.service.ts index d31a9ed64..0f9b86865 100644 --- a/server/src/friendship/friendship.service.ts +++ b/server/src/friendship/friendship.service.ts @@ -1,6 +1,5 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from 'src/prisma/prisma.service'; -import { Friendship } from './types'; @Injectable({}) export class FriendshipService { From eca1236e9e99d1ee16edf4cde25c7b8af37aaf35 Mon Sep 17 00:00:00 2001 From: rfquin Date: Wed, 15 Oct 2025 18:12:19 +1100 Subject: [PATCH 10/17] Add first pass of service/controller functions --- .../src/friendship/friendship.controller.ts | 78 +++++--- server/src/friendship/friendship.service.ts | 186 +++++++++++++++++- server/src/friendship/types.ts | 4 + server/src/user/user.service.ts | 2 +- 4 files changed, 236 insertions(+), 34 deletions(-) diff --git a/server/src/friendship/friendship.controller.ts b/server/src/friendship/friendship.controller.ts index d8df2b536..29ba01d57 100644 --- a/server/src/friendship/friendship.controller.ts +++ b/server/src/friendship/friendship.controller.ts @@ -13,6 +13,7 @@ import { CancelFriendRequestDto, AcceptFriendRequestDto, RejectFriendRequestDto, + RemoveFriendDto, } from './types'; import { AuthenticatedGuard } from 'src/auth/authenticated.guard'; import { AuthenticatedRequest } from 'src/auth/auth.controller'; @@ -29,71 +30,86 @@ export class FriendshipController { // [X] @post rejectFriendRequest // [X] @post removeMutualFriend - @Get() - @UseGuards(AuthenticatedGuard) - getFriendRequest( - @Req() req: AuthenticatedRequest, - @Param('id') friendshipId: string, - ): string { - // todo: call this.friendshipService.getFriendship(userId, friendshipId) - throw new Error('Unimplemented Route: getFriendRequest'); - } - @Post() @UseGuards(AuthenticatedGuard) - createFriendRequest( + async createFriendRequest( @Req() req: AuthenticatedRequest, @Body() createFriendRequest: CreateFriendRequestDto, ) { - // todo: call this.friendshipService.createFriendship(...) - throw new Error('Unimplemented Route: createFriendRequest'); + this.friendshipService.createRelationship( + await this.friendshipService.fetchUserFriendCode(req.user.id), + createFriendRequest.requesteeCode, + ); } @Post() @UseGuards(AuthenticatedGuard) - cancelFriendRequest( + async cancelFriendRequest( @Req() req: AuthenticatedRequest, @Body() cancelFriendRequest: CancelFriendRequestDto, ) { - // todo: handle this - throw new Error('Unimplemented Route: createFriendRequest'); + this.friendshipService.deleteRelationship( + await this.friendshipService.fetchUserFriendCode(req.user.id), + cancelFriendRequest.requestorCode, + true, + ); } @Get() @UseGuards(AuthenticatedGuard) - getOutgoingFriendRequests(@Req() req: AuthenticatedRequest): string { - // todo: call this.friendshipService.getOutgoingFriendRequests(...) - throw new Error('Unimplemented Route: getOutgoingFriendRequests'); + async getOutgoingFriendRequests(@Req() req: AuthenticatedRequest) { + const userCode = await this.friendshipService.fetchUserFriendCode( + req.user.id, + ); + return this.friendshipService.getUserFriendRequests(userCode); } @Get() @UseGuards(AuthenticatedGuard) - getFriendRequests(@Req() req: AuthenticatedRequest): string { - // todo: call this.friendshipService.getIncomingFriendRequests(...) - // and other things :D - throw new Error('Unimplemented Route: getIncomingFriendRequests'); + async getFriendRequests(@Req() req: AuthenticatedRequest) { + const userCode = await this.friendshipService.fetchUserFriendCode( + req.user.id, + ); + return await this.friendshipService.getUserFriendships(userCode); } @Post() @UseGuards(AuthenticatedGuard) - acceptFriendRequest( + async acceptFriendRequest( @Req() req: AuthenticatedRequest, @Body() acceptFriendRequest: AcceptFriendRequestDto, ) { - // todo: call this.friendshipService.createFriendship(...) - throw new Error('Unimplemented Route: acceptFriendRequest'); + const userCode = await this.friendshipService.fetchUserFriendCode( + req.user.id, + ); + return await this.friendshipService.acceptFriendRequest( + userCode, + acceptFriendRequest.requestorCode, + ); } @Post() @UseGuards(AuthenticatedGuard) - rejectFriendRequest( + async rejectFriendRequest( @Req() req: AuthenticatedRequest, @Body() rejectFriendRequest: RejectFriendRequestDto, ) { - // todo: call this.friendshipService.rejectFriendRequest(...) - throw new Error('Unimplemented Route: rejectFriendRequest'); + this.friendshipService.deleteRelationship( + await this.friendshipService.fetchUserFriendCode(req.user.id), + rejectFriendRequest.requestorCode, + false, + ); } - // Todo: Do we really need a rejectExisting/mutual friend? - // Does this require a seperate rest request? + @Post() + @UseGuards(AuthenticatedGuard) + async removeFriend( + @Req() req: AuthenticatedRequest, + @Body() removeFriendRequest: RemoveFriendDto, + ) { + this.friendshipService.deleteFriendship( + await this.friendshipService.fetchUserFriendCode(req.user.id), + removeFriendRequest.otherCode, + ); + } } diff --git a/server/src/friendship/friendship.service.ts b/server/src/friendship/friendship.service.ts index 0f9b86865..ff74456e7 100644 --- a/server/src/friendship/friendship.service.ts +++ b/server/src/friendship/friendship.service.ts @@ -1,8 +1,190 @@ -import { Injectable } from '@nestjs/common'; +import { HttpStatus, HttpException, Injectable } from '@nestjs/common'; import { PrismaService } from 'src/prisma/prisma.service'; +import { Friendship } from 'src/generated/prisma/client'; +import { Status } from 'src/generated/prisma/client'; @Injectable({}) export class FriendshipService { constructor(private readonly prisma: PrismaService) {} - // async + // send request [x] + // cancel request ==> deleteRelationship + // see outgoing [X] + // see incoming [X] + // accept request [X] + // reject request [x] ==> deleteRelationship + // remove existing [x] ==> deleteRelationship + // reset friendcode ==> in user module + + // Returns the status string of the ORIGINAL parameter. + private async doesRelationshipExist( + user1Id: string, + user2Id: string, + ): Promise { + const doSwap = user1Id < user2Id; + + [user1Id, user2Id] = doSwap ? [user1Id, user2Id] : [user2Id, user1Id]; + + const friendship = await this.prisma.friendship.findUnique({ + where: { + user1Id_user2Id: { user1Id: user1Id, user2Id: user2Id }, + }, + }); + + if (friendship == undefined) return undefined; + if (friendship?.status == 'FRIEND') return 'FRIEND'; + + var ret; + if (!doSwap) ret = friendship?.status; + else ret = friendship?.status == 'REQ_UID1' ? 'REQ_UID2' : 'REQ_UID1'; + return ret; + } + async getUserFriendships(userId: string): Promise { + return await this.prisma.friendship.findMany({ + where: { + status: 'FRIEND', + OR: [{ user1Id: userId }, { user2Id: userId }], + }, + }); + } + + async getUserFriendRequests(userId: string): Promise { + // Infulences the ordering ==> Should re-order in the future + return await this.prisma.friendship.findMany({ + where: { + OR: [ + { + status: 'REQ_UID1', + user1Id: userId, + }, + { + status: 'REQ_UID2', + user2Id: userId, + }, + ], + }, + }); + } + + async getFriendRequestsToUser(userId: string): Promise { + // Infulences the ordering ==> Should re-order in the future + return await this.prisma.friendship.findMany({ + where: { + OR: [ + { + status: 'REQ_UID2', + user1Id: userId, + }, + { + status: 'REQ_UID1', + user2Id: userId, + }, + ], + }, + }); + } + + async acceptFriendRequest(userId: string, otherId: string): Promise { + const relationship = await this.prisma.friendship.findFirst({ + where: { + OR: [ + { + status: 'REQ_UID1', + user1Id: otherId, + user2Id: userId, + }, + { + status: 'REQ_UID2', + user1Id: userId, + user2Id: otherId, + }, + ], + }, + }); + // TODO: How to handle this proplery? + if (relationship === undefined) return; + + await this.prisma.friendship.update({ + where: { id: relationship?.id }, + data: { status: 'FRIEND' }, + }); + } + + async deleteRelationship( + userId: string, + otherId: string, + requestedByUser: boolean, + ): Promise { + const relationship = await this.prisma.friendship.findFirst({ + where: { + OR: [ + { + status: 'REQ_UID1', + user1Id: requestedByUser ? userId : otherId, + user2Id: requestedByUser ? otherId : userId, + }, + { + status: 'REQ_UID2', + user1Id: requestedByUser ? otherId : userId, + user2Id: requestedByUser ? userId : otherId, + }, + ], + }, + }); + + if (relationship === undefined) return; + await this.prisma.friendship.delete({ + where: { id: relationship?.id }, + }); + } + + async deleteFriendship(userId: string, otherId: string): Promise { + const relationship = await this.prisma.friendship.findFirst({ + where: { + status: 'FRIEND', + user1Id: userId < otherId ? userId : otherId, + user2Id: userId < otherId ? otherId : userId, + }, + }); + + if (relationship === undefined) return; + await this.prisma.friendship.delete({ + where: { id: relationship?.id }, + }); + } + + async fetchUserFriendCode(userId: string): Promise { + const inviteCode = await this.prisma.user.findFirst({ + where: { id: userId }, + select: { inviteCode: true }, + }); + + if (inviteCode === null) { + throw new HttpException( + '[fetchUserFriendCode]: no inviteCode found!', + HttpStatus.NOT_FOUND, + ); + } + return inviteCode.inviteCode; + } + + async createRelationship(userId: string, otherId: string): Promise { + const isOrdered = userId < otherId; + const statusIfExists = await this.doesRelationshipExist(userId, otherId); + var status; + + if (statusIfExists !== undefined) { + throw new HttpException( + '[createRelationship]: relationship already exists!', + HttpStatus.BAD_REQUEST, + ); + } + + await this.prisma.friendship.create({ + data: { + user1Id: isOrdered ? userId : otherId, + user2Id: isOrdered ? otherId : userId, + status: isOrdered ? 'REQ_UID1' : 'REQ_UID2', + }, + }); + } } diff --git a/server/src/friendship/types.ts b/server/src/friendship/types.ts index 87f84a4a1..e55e6e2c7 100644 --- a/server/src/friendship/types.ts +++ b/server/src/friendship/types.ts @@ -17,3 +17,7 @@ export class AcceptFriendRequestDto { export class RejectFriendRequestDto { requestorCode: string; } + +export class RemoveFriendDto { + otherCode: string; +} diff --git a/server/src/user/user.service.ts b/server/src/user/user.service.ts index f5c415366..a74471f3d 100644 --- a/server/src/user/user.service.ts +++ b/server/src/user/user.service.ts @@ -87,7 +87,7 @@ export class UserService { return code !== null; } - // Note: this does NOT guarantee uniqueness + // Note: this does NOT guarantee uniqueness, use generateUniqueInviteCode for that. private generateInviteCode(): string { return new Array(6) .fill(undefined) From 1e7987c0617c42acaf7ac41bf67a0081a77076c6 Mon Sep 17 00:00:00 2001 From: rfquin Date: Wed, 15 Oct 2025 18:18:00 +1100 Subject: [PATCH 11/17] Fix lint errors - not awaiting service function calls :D --- .../src/friendship/friendship.controller.ts | 20 ++++++------------- server/src/friendship/friendship.service.ts | 4 +--- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/server/src/friendship/friendship.controller.ts b/server/src/friendship/friendship.controller.ts index 29ba01d57..c7814b944 100644 --- a/server/src/friendship/friendship.controller.ts +++ b/server/src/friendship/friendship.controller.ts @@ -1,12 +1,4 @@ -import { - Controller, - Get, - Body, - Post, - Req, - UseGuards, - Param, -} from '@nestjs/common'; +import { Controller, Get, Body, Post, Req, UseGuards } from '@nestjs/common'; import { FriendshipService } from './friendship.service'; import { CreateFriendRequestDto, @@ -36,7 +28,7 @@ export class FriendshipController { @Req() req: AuthenticatedRequest, @Body() createFriendRequest: CreateFriendRequestDto, ) { - this.friendshipService.createRelationship( + await this.friendshipService.createRelationship( await this.friendshipService.fetchUserFriendCode(req.user.id), createFriendRequest.requesteeCode, ); @@ -48,7 +40,7 @@ export class FriendshipController { @Req() req: AuthenticatedRequest, @Body() cancelFriendRequest: CancelFriendRequestDto, ) { - this.friendshipService.deleteRelationship( + await this.friendshipService.deleteRelationship( await this.friendshipService.fetchUserFriendCode(req.user.id), cancelFriendRequest.requestorCode, true, @@ -61,7 +53,7 @@ export class FriendshipController { const userCode = await this.friendshipService.fetchUserFriendCode( req.user.id, ); - return this.friendshipService.getUserFriendRequests(userCode); + return await this.friendshipService.getUserFriendRequests(userCode); } @Get() @@ -94,7 +86,7 @@ export class FriendshipController { @Req() req: AuthenticatedRequest, @Body() rejectFriendRequest: RejectFriendRequestDto, ) { - this.friendshipService.deleteRelationship( + await this.friendshipService.deleteRelationship( await this.friendshipService.fetchUserFriendCode(req.user.id), rejectFriendRequest.requestorCode, false, @@ -107,7 +99,7 @@ export class FriendshipController { @Req() req: AuthenticatedRequest, @Body() removeFriendRequest: RemoveFriendDto, ) { - this.friendshipService.deleteFriendship( + await this.friendshipService.deleteFriendship( await this.friendshipService.fetchUserFriendCode(req.user.id), removeFriendRequest.otherCode, ); diff --git a/server/src/friendship/friendship.service.ts b/server/src/friendship/friendship.service.ts index ff74456e7..1771619fd 100644 --- a/server/src/friendship/friendship.service.ts +++ b/server/src/friendship/friendship.service.ts @@ -1,7 +1,6 @@ import { HttpStatus, HttpException, Injectable } from '@nestjs/common'; import { PrismaService } from 'src/prisma/prisma.service'; import { Friendship } from 'src/generated/prisma/client'; -import { Status } from 'src/generated/prisma/client'; @Injectable({}) export class FriendshipService { @@ -33,7 +32,7 @@ export class FriendshipService { if (friendship == undefined) return undefined; if (friendship?.status == 'FRIEND') return 'FRIEND'; - var ret; + let ret; if (!doSwap) ret = friendship?.status; else ret = friendship?.status == 'REQ_UID1' ? 'REQ_UID2' : 'REQ_UID1'; return ret; @@ -170,7 +169,6 @@ export class FriendshipService { async createRelationship(userId: string, otherId: string): Promise { const isOrdered = userId < otherId; const statusIfExists = await this.doesRelationshipExist(userId, otherId); - var status; if (statusIfExists !== undefined) { throw new HttpException( From fb58f4c6bf99705533a4f7fbe7b9267a674dd35d Mon Sep 17 00:00:00 2001 From: rfquin Date: Wed, 15 Oct 2025 18:41:14 +1100 Subject: [PATCH 12/17] Comment out some auth.module.ts shtuff --- server/src/auth/auth.module.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/src/auth/auth.module.ts b/server/src/auth/auth.module.ts index aedc63d95..c918311f1 100644 --- a/server/src/auth/auth.module.ts +++ b/server/src/auth/auth.module.ts @@ -29,9 +29,9 @@ const OidcStrategyFactory = { PrismaService, UserService, GraphqlService, - OidcStrategyFactory, - GithubStrategy, - GoogleStrategy, + // OidcStrategyFactory, + // GithubStrategy, + // GoogleStrategy, GuestStrategy, SessionSerializer, AuthService, From 9a6c35011c0b227a50a7de4e66f7fba80ff217c8 Mon Sep 17 00:00:00 2001 From: rfquin Date: Wed, 22 Oct 2025 17:56:52 +1100 Subject: [PATCH 13/17] Add route names --- server/src/app.module.ts | 2 ++ .../src/friendship/friendship.controller.ts | 22 ++++++------------- server/src/friendship/friendship.module.ts | 3 ++- server/src/friendship/friendship.service.ts | 4 ++-- server/src/friendship/types.ts | 2 +- 5 files changed, 14 insertions(+), 19 deletions(-) diff --git a/server/src/app.module.ts b/server/src/app.module.ts index 6654b8744..dad688ebd 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -5,6 +5,7 @@ import { ConfigModule } from '@nestjs/config'; import config from './config'; import { UserModule } from './user/user.module'; import { AuthModule } from './auth/auth.module'; +import { FriendshipModule } from './friendship/friendship.module'; import { TimetableModule } from './timetable/timetable.module'; @Module({ @@ -15,6 +16,7 @@ import { TimetableModule } from './timetable/timetable.module'; expandVariables: true, }), UserModule, + FriendshipModule, TimetableModule, AuthModule, ], diff --git a/server/src/friendship/friendship.controller.ts b/server/src/friendship/friendship.controller.ts index c7814b944..6dd7adfe4 100644 --- a/server/src/friendship/friendship.controller.ts +++ b/server/src/friendship/friendship.controller.ts @@ -14,14 +14,6 @@ import { AuthenticatedRequest } from 'src/auth/auth.controller'; export class FriendshipController { constructor(private readonly friendshipService: FriendshipService) {} - // [X] @post createFriendRequest - // [X] @post cancelFriendRequest - // [X] @get getOutgoingFriendRequests - // [X] @get getIncomingFriendRequests - // [X] @post acceptFriendRequest - // [X] @post rejectFriendRequest - // [X] @post removeMutualFriend - @Post() @UseGuards(AuthenticatedGuard) async createFriendRequest( @@ -34,7 +26,7 @@ export class FriendshipController { ); } - @Post() + @Post('cancel') @UseGuards(AuthenticatedGuard) async cancelFriendRequest( @Req() req: AuthenticatedRequest, @@ -42,12 +34,12 @@ export class FriendshipController { ) { await this.friendshipService.deleteRelationship( await this.friendshipService.fetchUserFriendCode(req.user.id), - cancelFriendRequest.requestorCode, + cancelFriendRequest.requesteeCode, true, ); } - @Get() + @Get('requests') @UseGuards(AuthenticatedGuard) async getOutgoingFriendRequests(@Req() req: AuthenticatedRequest) { const userCode = await this.friendshipService.fetchUserFriendCode( @@ -58,14 +50,14 @@ export class FriendshipController { @Get() @UseGuards(AuthenticatedGuard) - async getFriendRequests(@Req() req: AuthenticatedRequest) { + async getUserFriends(@Req() req: AuthenticatedRequest) { const userCode = await this.friendshipService.fetchUserFriendCode( req.user.id, ); return await this.friendshipService.getUserFriendships(userCode); } - @Post() + @Post('accept') @UseGuards(AuthenticatedGuard) async acceptFriendRequest( @Req() req: AuthenticatedRequest, @@ -80,7 +72,7 @@ export class FriendshipController { ); } - @Post() + @Post('reject') @UseGuards(AuthenticatedGuard) async rejectFriendRequest( @Req() req: AuthenticatedRequest, @@ -93,7 +85,7 @@ export class FriendshipController { ); } - @Post() + @Post('remove') @UseGuards(AuthenticatedGuard) async removeFriend( @Req() req: AuthenticatedRequest, diff --git a/server/src/friendship/friendship.module.ts b/server/src/friendship/friendship.module.ts index dad63dbb7..23608d9fe 100644 --- a/server/src/friendship/friendship.module.ts +++ b/server/src/friendship/friendship.module.ts @@ -2,9 +2,10 @@ import { Module } from '@nestjs/common'; import { GraphqlService } from 'src/graphql/graphql.service'; import { PrismaService } from 'src/prisma/prisma.service'; import { FriendshipController } from './friendship.controller'; +import { FriendshipService } from './friendship.service'; @Module({ - providers: [PrismaService, GraphqlService], + providers: [FriendshipService, PrismaService, GraphqlService], controllers: [FriendshipController], }) export class FriendshipModule {} diff --git a/server/src/friendship/friendship.service.ts b/server/src/friendship/friendship.service.ts index 1771619fd..38f603c81 100644 --- a/server/src/friendship/friendship.service.ts +++ b/server/src/friendship/friendship.service.ts @@ -100,10 +100,10 @@ export class FriendshipService { }, }); // TODO: How to handle this proplery? - if (relationship === undefined) return; + if (relationship === null) return; await this.prisma.friendship.update({ - where: { id: relationship?.id }, + where: { id: relationship.id }, data: { status: 'FRIEND' }, }); } diff --git a/server/src/friendship/types.ts b/server/src/friendship/types.ts index e55e6e2c7..2f2e17976 100644 --- a/server/src/friendship/types.ts +++ b/server/src/friendship/types.ts @@ -7,7 +7,7 @@ export class CreateFriendRequestDto { } export class CancelFriendRequestDto { - requestorCode: string; + requesteeCode: string; } export class AcceptFriendRequestDto { From 75d4ffb2cbbe3956f706ecf8b08549d1518bae9d Mon Sep 17 00:00:00 2001 From: rfquin Date: Wed, 29 Oct 2025 18:02:59 +1100 Subject: [PATCH 14/17] Add friends migration --- .../20251015081258_add_friends/migration.sql | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 server/prisma/migrations/20251015081258_add_friends/migration.sql diff --git a/server/prisma/migrations/20251015081258_add_friends/migration.sql b/server/prisma/migrations/20251015081258_add_friends/migration.sql new file mode 100644 index 000000000..49a302fa9 --- /dev/null +++ b/server/prisma/migrations/20251015081258_add_friends/migration.sql @@ -0,0 +1,37 @@ +/* + Warnings: + + - A unique constraint covering the columns `[inviteCode]` on the table `user` will be added. If there are existing duplicate values, this will fail. + - Added the required column `inviteCode` to the `user` table without a default value. This is not possible if the table is not empty. + +*/ +-- CreateEnum +CREATE TYPE "Status" AS ENUM ('REQ_UID1', 'REQ_UID2', 'FRIEND'); + +-- AlterTable +ALTER TABLE "user" ADD COLUMN "inviteCode" CHAR(6) NOT NULL; + +-- CreateTable +CREATE TABLE "friendship" ( + "id" TEXT NOT NULL, + "user1Id" TEXT NOT NULL, + "user2Id" TEXT NOT NULL, + "status" "Status" NOT NULL, + + CONSTRAINT "friendship_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "friendshipId" ON "friendship"("id"); + +-- CreateIndex +CREATE UNIQUE INDEX "friendship_user1Id_user2Id_key" ON "friendship"("user1Id", "user2Id"); + +-- CreateIndex +CREATE UNIQUE INDEX "user_inviteCode_key" ON "user"("inviteCode"); + +-- CreateIndex +CREATE INDEX "friendsId" ON "user"("inviteCode"); + +-- CreateIndex +CREATE INDEX "userId" ON "user"("id"); From 3d96bb77a8ad1c74d9452ed1ae8fd59c780df7e0 Mon Sep 17 00:00:00 2001 From: jason4193 Date: Wed, 29 Oct 2025 18:32:39 +1100 Subject: [PATCH 15/17] Add Bruno test api folder --- .../AutoTimetable Routes/New Request.bru | 15 +++++++ .../AutoTimetable Routes/folder.bru | 8 ++++ .../Get ClassesID from Course.bru | 24 +++++++++++ .../Class Routes/Remove Class for Course.bru | 25 ++++++++++++ .../Class Routes/Select Class for Course.bru | 31 ++++++++++++++ .../Notangles API/Class Routes/folder.bru | 8 ++++ .../Course Routes/Add Course.bru | 30 ++++++++++++++ .../Course Routes/Get CourseID.bru | 37 +++++++++++++++++ .../Course Routes/Remove Course.bru | 24 +++++++++++ .../Course Routes/Update Course Colour.bru | 30 ++++++++++++++ .../Notangles API/Course Routes/folder.bru | 8 ++++ .../Notangles API/Event Routes/Add event.bru | 40 +++++++++++++++++++ .../Event Routes/Delete event.bru | 15 +++++++ .../Notangles API/Event Routes/Get event.bru | 24 +++++++++++ .../Event Routes/Update event.bru | 32 +++++++++++++++ .../Notangles API/Event Routes/folder.bru | 8 ++++ .../Friend Routes/Accept Friend Request.bru | 15 +++++++ .../Friend Routes/Cancel Friend Request.bru | 15 +++++++ .../Friend Routes/Create Friend Request.bru | 15 +++++++ .../Friend Routes/Get Outgoing Requests.bru | 15 +++++++ .../Notangles API/Friend Routes/folder.bru | 8 ++++ server/bruno/Notangles API/New Request.bru | 15 +++++++ .../Timetable Routes/Clear timetable.bru | 15 +++++++ .../Timetable Routes/Create Timetable.bru | 27 +++++++++++++ .../Timetable Routes/Delete Timetable.bru | 19 +++++++++ .../Timetable Routes/Get Timetable By ID.bru | 24 +++++++++++ .../Timetable Routes/Get User Timetables.bru | 25 ++++++++++++ .../Timetable Routes/Rename Timetable.bru | 25 ++++++++++++ .../Timetable Make Primary.bru | 19 +++++++++ .../Notangles API/Timetable Routes/folder.bru | 8 ++++ .../User Routes/Get User Profile.bru | 20 ++++++++++ .../Notangles API/User Routes/Guest Login.bru | 24 +++++++++++ .../User Routes/Post User Profile Picture.bru | 26 ++++++++++++ .../Notangles API/User Routes/folder.bru | 8 ++++ server/bruno/Notangles API/bruno.json | 9 +++++ server/bruno/Notangles API/collection.bru | 11 +++++ .../environments/Local Guest 1.bru | 4 ++ server/src/graphql/graphql.service.ts | 2 +- 38 files changed, 707 insertions(+), 1 deletion(-) create mode 100644 server/bruno/Notangles API/AutoTimetable Routes/New Request.bru create mode 100644 server/bruno/Notangles API/AutoTimetable Routes/folder.bru create mode 100644 server/bruno/Notangles API/Class Routes/Get ClassesID from Course.bru create mode 100644 server/bruno/Notangles API/Class Routes/Remove Class for Course.bru create mode 100644 server/bruno/Notangles API/Class Routes/Select Class for Course.bru create mode 100644 server/bruno/Notangles API/Class Routes/folder.bru create mode 100644 server/bruno/Notangles API/Course Routes/Add Course.bru create mode 100644 server/bruno/Notangles API/Course Routes/Get CourseID.bru create mode 100644 server/bruno/Notangles API/Course Routes/Remove Course.bru create mode 100644 server/bruno/Notangles API/Course Routes/Update Course Colour.bru create mode 100644 server/bruno/Notangles API/Course Routes/folder.bru create mode 100644 server/bruno/Notangles API/Event Routes/Add event.bru create mode 100644 server/bruno/Notangles API/Event Routes/Delete event.bru create mode 100644 server/bruno/Notangles API/Event Routes/Get event.bru create mode 100644 server/bruno/Notangles API/Event Routes/Update event.bru create mode 100644 server/bruno/Notangles API/Event Routes/folder.bru create mode 100644 server/bruno/Notangles API/Friend Routes/Accept Friend Request.bru create mode 100644 server/bruno/Notangles API/Friend Routes/Cancel Friend Request.bru create mode 100644 server/bruno/Notangles API/Friend Routes/Create Friend Request.bru create mode 100644 server/bruno/Notangles API/Friend Routes/Get Outgoing Requests.bru create mode 100644 server/bruno/Notangles API/Friend Routes/folder.bru create mode 100644 server/bruno/Notangles API/New Request.bru create mode 100644 server/bruno/Notangles API/Timetable Routes/Clear timetable.bru create mode 100644 server/bruno/Notangles API/Timetable Routes/Create Timetable.bru create mode 100644 server/bruno/Notangles API/Timetable Routes/Delete Timetable.bru create mode 100644 server/bruno/Notangles API/Timetable Routes/Get Timetable By ID.bru create mode 100644 server/bruno/Notangles API/Timetable Routes/Get User Timetables.bru create mode 100644 server/bruno/Notangles API/Timetable Routes/Rename Timetable.bru create mode 100644 server/bruno/Notangles API/Timetable Routes/Timetable Make Primary.bru create mode 100644 server/bruno/Notangles API/Timetable Routes/folder.bru create mode 100644 server/bruno/Notangles API/User Routes/Get User Profile.bru create mode 100644 server/bruno/Notangles API/User Routes/Guest Login.bru create mode 100644 server/bruno/Notangles API/User Routes/Post User Profile Picture.bru create mode 100644 server/bruno/Notangles API/User Routes/folder.bru create mode 100644 server/bruno/Notangles API/bruno.json create mode 100644 server/bruno/Notangles API/collection.bru create mode 100644 server/bruno/Notangles API/environments/Local Guest 1.bru diff --git a/server/bruno/Notangles API/AutoTimetable Routes/New Request.bru b/server/bruno/Notangles API/AutoTimetable Routes/New Request.bru new file mode 100644 index 000000000..0edc9e8b4 --- /dev/null +++ b/server/bruno/Notangles API/AutoTimetable Routes/New Request.bru @@ -0,0 +1,15 @@ +meta { + name: New Request + type: http + seq: 1 +} + +get { + url: + body: none + auth: inherit +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/AutoTimetable Routes/folder.bru b/server/bruno/Notangles API/AutoTimetable Routes/folder.bru new file mode 100644 index 000000000..f2190d717 --- /dev/null +++ b/server/bruno/Notangles API/AutoTimetable Routes/folder.bru @@ -0,0 +1,8 @@ +meta { + name: AutoTimetable Routes + seq: 7 +} + +auth { + mode: inherit +} diff --git a/server/bruno/Notangles API/Class Routes/Get ClassesID from Course.bru b/server/bruno/Notangles API/Class Routes/Get ClassesID from Course.bru new file mode 100644 index 000000000..3c377dd35 --- /dev/null +++ b/server/bruno/Notangles API/Class Routes/Get ClassesID from Course.bru @@ -0,0 +1,24 @@ +meta { + name: Get ClassesID from Course + type: http + seq: 1 +} + +get { + url: {{Local Host URL}}/user/timetables/classes/:timetableId/:courseId + body: none + auth: inherit +} + +params:path { + timetableId: 7050d820-fa7f-41e1-806e-88df665340f0 + courseId: COMP2521Undergraduate +} + +headers { + Cookie: {{Session Cookie}} +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Class Routes/Remove Class for Course.bru b/server/bruno/Notangles API/Class Routes/Remove Class for Course.bru new file mode 100644 index 000000000..cb421afad --- /dev/null +++ b/server/bruno/Notangles API/Class Routes/Remove Class for Course.bru @@ -0,0 +1,25 @@ +meta { + name: Remove Class for Course + type: http + seq: 3 +} + +delete { + url: {{Local Host URL}}/user/timetables/class/:timetableId/:courseId/:classId + body: json + auth: inherit +} + +params:path { + timetableId: 1aba85cf-b2bc-41ea-acdf-008e9e335f34 + courseId: ACTL3301Undergraduate + classId: ACTL3301Undergraduate-10135-T2-2025 +} + +headers { + Cookie: {{Session Cookie}} +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Class Routes/Select Class for Course.bru b/server/bruno/Notangles API/Class Routes/Select Class for Course.bru new file mode 100644 index 000000000..f76924f6c --- /dev/null +++ b/server/bruno/Notangles API/Class Routes/Select Class for Course.bru @@ -0,0 +1,31 @@ +meta { + name: Select Class for Course + type: http + seq: 2 +} + +patch { + url: {{Local Host URL}}/user/timetables/class/:timetableId/:courseId + body: json + auth: inherit +} + +params:path { + timetableId: 1aba85cf-b2bc-41ea-acdf-008e9e335f34 + courseId: ACTL3301Undergraduate +} + +headers { + Cookie: {{Session Cookie}} +} + +body:json { + { + "classId": "ACTL3301Undergraduate-10135-T2-2025" + // "classId": "ACTL3301Undergraduate-10138-T2-2025" + } +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Class Routes/folder.bru b/server/bruno/Notangles API/Class Routes/folder.bru new file mode 100644 index 000000000..53584e54a --- /dev/null +++ b/server/bruno/Notangles API/Class Routes/folder.bru @@ -0,0 +1,8 @@ +meta { + name: Class Routes + seq: 4 +} + +auth { + mode: inherit +} diff --git a/server/bruno/Notangles API/Course Routes/Add Course.bru b/server/bruno/Notangles API/Course Routes/Add Course.bru new file mode 100644 index 000000000..2815816d0 --- /dev/null +++ b/server/bruno/Notangles API/Course Routes/Add Course.bru @@ -0,0 +1,30 @@ +meta { + name: Add Course + type: http + seq: 2 +} + +post { + url: {{Local Host URL}}/user/timetables/course/:timetableId/:courseId + body: json + auth: inherit +} + +params:path { + timetableId: d9b19625-0e05-40d0-bef8-cc3bfdf1a6d3 + courseId: COMP6420Undergraduate +} + +headers { + Cookie: {{Session Cookie}} +} + +body:json { + { + "colour": "default-1" + } +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Course Routes/Get CourseID.bru b/server/bruno/Notangles API/Course Routes/Get CourseID.bru new file mode 100644 index 000000000..855de6dc6 --- /dev/null +++ b/server/bruno/Notangles API/Course Routes/Get CourseID.bru @@ -0,0 +1,37 @@ +meta { + name: Get CourseID + type: http + seq: 1 +} + +get { + url: {{Local Host URL}}/user/timetables/courses/:timetableId + body: none + auth: inherit +} + +params:path { + timetableId: d9b19625-0e05-40d0-bef8-cc3bfdf1a6d3 +} + +headers { + Cookie: {{Session Cookie}} +} + +script:post-response { + test("Status code is 200", function () { + expect(res.getStatus()).to.equal(200); + }); + + test("Response is correct format", function () { + var jsonData = res.getBody(); + expect(jsonData).to.be.an('array'); + jsonData.forEach(function(id) { + expect(id).to.be.a('string'); + }); + }); +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Course Routes/Remove Course.bru b/server/bruno/Notangles API/Course Routes/Remove Course.bru new file mode 100644 index 000000000..b11bffe6f --- /dev/null +++ b/server/bruno/Notangles API/Course Routes/Remove Course.bru @@ -0,0 +1,24 @@ +meta { + name: Remove Course + type: http + seq: 4 +} + +delete { + url: {{Local Host URL}}/user/timetables/course/:timetableId/:courseId + body: none + auth: inherit +} + +params:path { + timetableId: 6f4cbeb8-0979-4ec8-95ec-b301eb054715 + courseId: COMP2521Undergraduate +} + +headers { + Cookie: {{Session Cookie}} +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Course Routes/Update Course Colour.bru b/server/bruno/Notangles API/Course Routes/Update Course Colour.bru new file mode 100644 index 000000000..3739ad4e6 --- /dev/null +++ b/server/bruno/Notangles API/Course Routes/Update Course Colour.bru @@ -0,0 +1,30 @@ +meta { + name: Update Course Colour + type: http + seq: 3 +} + +patch { + url: {{Local Host URL}}/user/timetables/course/:timetableId/:courseId/colour + body: json + auth: inherit +} + +params:path { + timetableId: 1aba85cf-b2bc-41ea-acdf-008e9e335f34 + courseId: ACTL3301Undergraduate +} + +headers { + Cookie: {{Session Cookie}} +} + +body:json { + { + "colour": "default-7" + } +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Course Routes/folder.bru b/server/bruno/Notangles API/Course Routes/folder.bru new file mode 100644 index 000000000..1f0392ef6 --- /dev/null +++ b/server/bruno/Notangles API/Course Routes/folder.bru @@ -0,0 +1,8 @@ +meta { + name: Course Routes + seq: 3 +} + +auth { + mode: inherit +} diff --git a/server/bruno/Notangles API/Event Routes/Add event.bru b/server/bruno/Notangles API/Event Routes/Add event.bru new file mode 100644 index 000000000..f884b630a --- /dev/null +++ b/server/bruno/Notangles API/Event Routes/Add event.bru @@ -0,0 +1,40 @@ +meta { + name: Add event + type: http + seq: 2 +} + +post { + url: {{Local Host URL}}/user/timetables/event + body: json + auth: inherit +} + +params:query { + ~timetableId: {} +} + +headers { + Cookie: {{Session Cookie}} +} + +body:json { + { + "timetableId": "225ac72d-6015-40da-b41f-66099f080d99", + "event": { + "title": "2069 LEc", + "description": "Calculus 101", + "start": 10, + "end": 190, + "location": "Room 301", + "colour": "default-7", + "type": "CUSTOM", + "dayOfWeek": 1 + } + } + +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Event Routes/Delete event.bru b/server/bruno/Notangles API/Event Routes/Delete event.bru new file mode 100644 index 000000000..de43223ec --- /dev/null +++ b/server/bruno/Notangles API/Event Routes/Delete event.bru @@ -0,0 +1,15 @@ +meta { + name: Delete event + type: http + seq: 3 +} + +delete { + url: Delete event + body: none + auth: inherit +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Event Routes/Get event.bru b/server/bruno/Notangles API/Event Routes/Get event.bru new file mode 100644 index 000000000..d76af40d1 --- /dev/null +++ b/server/bruno/Notangles API/Event Routes/Get event.bru @@ -0,0 +1,24 @@ +meta { + name: Get event + type: http + seq: 4 +} + +get { + url: {{Local Host URL}}/user/classes/:timetableId/:eventId + body: none + auth: inherit +} + +params:path { + timetableId: + eventId: +} + +headers { + Cookie: {{Session Cookie}} +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Event Routes/Update event.bru b/server/bruno/Notangles API/Event Routes/Update event.bru new file mode 100644 index 000000000..e7fc21f53 --- /dev/null +++ b/server/bruno/Notangles API/Event Routes/Update event.bru @@ -0,0 +1,32 @@ +meta { + name: Update event + type: http + seq: 1 +} + +patch { + url: {{Local Host URL}}/user/timetables/event/:eventid + body: json + auth: inherit +} + +params:path { + eventid: 5 +} + +body:json { + { + "title": "Math Tute", + "description": "Calculus 101", + "start": 10, + "end": 100, + "location": "Room 301", + "colour": "default-7", + "dayOfWeek": 1 + } + +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Event Routes/folder.bru b/server/bruno/Notangles API/Event Routes/folder.bru new file mode 100644 index 000000000..a9b5717b0 --- /dev/null +++ b/server/bruno/Notangles API/Event Routes/folder.bru @@ -0,0 +1,8 @@ +meta { + name: Event Routes + seq: 5 +} + +auth { + mode: inherit +} diff --git a/server/bruno/Notangles API/Friend Routes/Accept Friend Request.bru b/server/bruno/Notangles API/Friend Routes/Accept Friend Request.bru new file mode 100644 index 000000000..9ca56fef4 --- /dev/null +++ b/server/bruno/Notangles API/Friend Routes/Accept Friend Request.bru @@ -0,0 +1,15 @@ +meta { + name: Accept Friend Request + type: http + seq: 4 +} + +get { + url: + body: none + auth: inherit +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Friend Routes/Cancel Friend Request.bru b/server/bruno/Notangles API/Friend Routes/Cancel Friend Request.bru new file mode 100644 index 000000000..6016bc2b2 --- /dev/null +++ b/server/bruno/Notangles API/Friend Routes/Cancel Friend Request.bru @@ -0,0 +1,15 @@ +meta { + name: Cancel Friend Request + type: http + seq: 3 +} + +get { + url: + body: none + auth: inherit +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Friend Routes/Create Friend Request.bru b/server/bruno/Notangles API/Friend Routes/Create Friend Request.bru new file mode 100644 index 000000000..46e14e26a --- /dev/null +++ b/server/bruno/Notangles API/Friend Routes/Create Friend Request.bru @@ -0,0 +1,15 @@ +meta { + name: Create Friend Request + type: http + seq: 1 +} + +get { + url: + body: none + auth: inherit +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Friend Routes/Get Outgoing Requests.bru b/server/bruno/Notangles API/Friend Routes/Get Outgoing Requests.bru new file mode 100644 index 000000000..85571107c --- /dev/null +++ b/server/bruno/Notangles API/Friend Routes/Get Outgoing Requests.bru @@ -0,0 +1,15 @@ +meta { + name: Get Outgoing Requests + type: http + seq: 2 +} + +get { + url: + body: none + auth: inherit +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Friend Routes/folder.bru b/server/bruno/Notangles API/Friend Routes/folder.bru new file mode 100644 index 000000000..6d1f702f1 --- /dev/null +++ b/server/bruno/Notangles API/Friend Routes/folder.bru @@ -0,0 +1,8 @@ +meta { + name: Friend Routes + seq: 6 +} + +auth { + mode: inherit +} diff --git a/server/bruno/Notangles API/New Request.bru b/server/bruno/Notangles API/New Request.bru new file mode 100644 index 000000000..c07f4c48e --- /dev/null +++ b/server/bruno/Notangles API/New Request.bru @@ -0,0 +1,15 @@ +meta { + name: New Request + type: http + seq: 8 +} + +get { + url: + body: none + auth: inherit +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Timetable Routes/Clear timetable.bru b/server/bruno/Notangles API/Timetable Routes/Clear timetable.bru new file mode 100644 index 000000000..d125980c3 --- /dev/null +++ b/server/bruno/Notangles API/Timetable Routes/Clear timetable.bru @@ -0,0 +1,15 @@ +meta { + name: Clear timetable + type: http + seq: 7 +} + +get { + url: + body: none + auth: inherit +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Timetable Routes/Create Timetable.bru b/server/bruno/Notangles API/Timetable Routes/Create Timetable.bru new file mode 100644 index 000000000..060b85736 --- /dev/null +++ b/server/bruno/Notangles API/Timetable Routes/Create Timetable.bru @@ -0,0 +1,27 @@ +meta { + name: Create Timetable + type: http + seq: 3 +} + +post { + url: {{Local Host URL}}/user/timetables + body: json + auth: inherit +} + +headers { + Cookie: {{Session Cookie}} +} + +body:json { + { + "name":"New Timetable 1", + "year":2025, + "term": "T3" + } +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Timetable Routes/Delete Timetable.bru b/server/bruno/Notangles API/Timetable Routes/Delete Timetable.bru new file mode 100644 index 000000000..1b4e32fd3 --- /dev/null +++ b/server/bruno/Notangles API/Timetable Routes/Delete Timetable.bru @@ -0,0 +1,19 @@ +meta { + name: Delete Timetable + type: http + seq: 4 +} + +delete { + url: {{Local Host URL}}/user/timetables/:id + body: none + auth: inherit +} + +params:path { + id: d533e368-5666-4abc-88ca-347e5725b318 +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Timetable Routes/Get Timetable By ID.bru b/server/bruno/Notangles API/Timetable Routes/Get Timetable By ID.bru new file mode 100644 index 000000000..f8d1353d7 --- /dev/null +++ b/server/bruno/Notangles API/Timetable Routes/Get Timetable By ID.bru @@ -0,0 +1,24 @@ +meta { + name: Get Timetable By ID + type: http + seq: 2 +} + +get { + url: {{HostURL}}/user/timetables/:id + body: none + auth: none +} + +params:path { + id: a1380156-e406-4a79-94cc-51f0b83abbc9 +} + +headers { + Set-Cookie: {{Session_Cookie}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/server/bruno/Notangles API/Timetable Routes/Get User Timetables.bru b/server/bruno/Notangles API/Timetable Routes/Get User Timetables.bru new file mode 100644 index 000000000..ff582243a --- /dev/null +++ b/server/bruno/Notangles API/Timetable Routes/Get User Timetables.bru @@ -0,0 +1,25 @@ +meta { + name: Get User Timetables + type: http + seq: 1 +} + +get { + url: {{HostURL}}/user/timetables?year=2025&term=T1 + body: none + auth: none +} + +params:query { + year: 2025 + term: T1 +} + +headers { + Set-Cookie: {{Session_Cookie}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/server/bruno/Notangles API/Timetable Routes/Rename Timetable.bru b/server/bruno/Notangles API/Timetable Routes/Rename Timetable.bru new file mode 100644 index 000000000..93cd22ae5 --- /dev/null +++ b/server/bruno/Notangles API/Timetable Routes/Rename Timetable.bru @@ -0,0 +1,25 @@ +meta { + name: Rename Timetable + type: http + seq: 5 +} + +patch { + url: {{Local Host URL}}/user/timetables/:id/rename + body: json + auth: inherit +} + +params:path { + id: 7050d820-fa7f-41e1-806e-88df665340f0 +} + +body:json { + { + "name": "Test Rename Timetable" + } +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Timetable Routes/Timetable Make Primary.bru b/server/bruno/Notangles API/Timetable Routes/Timetable Make Primary.bru new file mode 100644 index 000000000..7428c99ee --- /dev/null +++ b/server/bruno/Notangles API/Timetable Routes/Timetable Make Primary.bru @@ -0,0 +1,19 @@ +meta { + name: Timetable Make Primary + type: http + seq: 6 +} + +patch { + url: {{Local Host URL}}/user/timetables/:id/change-primary + body: none + auth: inherit +} + +params:path { + id: 3b0cd623-3105-4503-9e0c-0e949134be51 +} + +settings { + encodeUrl: true +} diff --git a/server/bruno/Notangles API/Timetable Routes/folder.bru b/server/bruno/Notangles API/Timetable Routes/folder.bru new file mode 100644 index 000000000..6fed0eab1 --- /dev/null +++ b/server/bruno/Notangles API/Timetable Routes/folder.bru @@ -0,0 +1,8 @@ +meta { + name: Timetable Routes + seq: 2 +} + +auth { + mode: inherit +} diff --git a/server/bruno/Notangles API/User Routes/Get User Profile.bru b/server/bruno/Notangles API/User Routes/Get User Profile.bru new file mode 100644 index 000000000..2ada451a6 --- /dev/null +++ b/server/bruno/Notangles API/User Routes/Get User Profile.bru @@ -0,0 +1,20 @@ +meta { + name: Get User Profile + type: http + seq: 2 +} + +get { + url: {{HostURL}}/user/profile + body: none + auth: none +} + +headers { + Set-Cookie: {{Session_Cookie}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/server/bruno/Notangles API/User Routes/Guest Login.bru b/server/bruno/Notangles API/User Routes/Guest Login.bru new file mode 100644 index 000000000..55c85c09a --- /dev/null +++ b/server/bruno/Notangles API/User Routes/Guest Login.bru @@ -0,0 +1,24 @@ +meta { + name: Guest Login + type: http + seq: 1 +} + +get { + url: {{HostURL}}/auth/login/guest + body: none + auth: inherit +} + +script:post-response { + const sessionCookie = res.getHeader('set-cookie') + bru.setEnvVar("Session_Cookie", sessionCookie); + +} + +settings { + encodeUrl: true + timeout: 0 + followRedirects: false + maxRedirects: 5 +} diff --git a/server/bruno/Notangles API/User Routes/Post User Profile Picture.bru b/server/bruno/Notangles API/User Routes/Post User Profile Picture.bru new file mode 100644 index 000000000..a1f1d0895 --- /dev/null +++ b/server/bruno/Notangles API/User Routes/Post User Profile Picture.bru @@ -0,0 +1,26 @@ +meta { + name: Post User Profile Picture + type: http + seq: 3 +} + +post { + url: {{HostURL}}/user/profile/picture + body: json + auth: none +} + +headers { + Set-Cookie: {{Session_Cookie}} +} + +body:json { + { + "url": "data:image/gif;base64,R0lGODlhPQBEAPeoAJosM//AwO/AwHVYZ/z595kzAP/s7P+goOXMv8+fhw/v739/f+8PD98fH/8mJl+fn/9ZWb8/PzWlwv///6wWGbImAPgTEMImIN9gUFCEm/gDALULDN8PAD6atYdCTX9gUNKlj8wZAKUsAOzZz+UMAOsJAP/Z2ccMDA8PD/95eX5NWvsJCOVNQPtfX/8zM8+QePLl38MGBr8JCP+zs9myn/8GBqwpAP/GxgwJCPny78lzYLgjAJ8vAP9fX/+MjMUcAN8zM/9wcM8ZGcATEL+QePdZWf/29uc/P9cmJu9MTDImIN+/r7+/vz8/P8VNQGNugV8AAF9fX8swMNgTAFlDOICAgPNSUnNWSMQ5MBAQEJE3QPIGAM9AQMqGcG9vb6MhJsEdGM8vLx8fH98AANIWAMuQeL8fABkTEPPQ0OM5OSYdGFl5jo+Pj/+pqcsTE78wMFNGQLYmID4dGPvd3UBAQJmTkP+8vH9QUK+vr8ZWSHpzcJMmILdwcLOGcHRQUHxwcK9PT9DQ0O/v70w5MLypoG8wKOuwsP/g4P/Q0IcwKEswKMl8aJ9fX2xjdOtGRs/Pz+Dg4GImIP8gIH0sKEAwKKmTiKZ8aB/f39Wsl+LFt8dgUE9PT5x5aHBwcP+AgP+WltdgYMyZfyywz78AAAAAAAD///8AAP9mZv///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAKgALAAAAAA9AEQAAAj/AFEJHEiwoMGDCBMqXMiwocAbBww4nEhxoYkUpzJGrMixogkfGUNqlNixJEIDB0SqHGmyJSojM1bKZOmyop0gM3Oe2liTISKMOoPy7GnwY9CjIYcSRYm0aVKSLmE6nfq05QycVLPuhDrxBlCtYJUqNAq2bNWEBj6ZXRuyxZyDRtqwnXvkhACDV+euTeJm1Ki7A73qNWtFiF+/gA95Gly2CJLDhwEHMOUAAuOpLYDEgBxZ4GRTlC1fDnpkM+fOqD6DDj1aZpITp0dtGCDhr+fVuCu3zlg49ijaokTZTo27uG7Gjn2P+hI8+PDPERoUB318bWbfAJ5sUNFcuGRTYUqV/3ogfXp1rWlMc6awJjiAAd2fm4ogXjz56aypOoIde4OE5u/F9x199dlXnnGiHZWEYbGpsAEA3QXYnHwEFliKAgswgJ8LPeiUXGwedCAKABACCN+EA1pYIIYaFlcDhytd51sGAJbo3onOpajiihlO92KHGaUXGwWjUBChjSPiWJuOO/LYIm4v1tXfE6J4gCSJEZ7YgRYUNrkji9P55sF/ogxw5ZkSqIDaZBV6aSGYq/lGZplndkckZ98xoICbTcIJGQAZcNmdmUc210hs35nCyJ58fgmIKX5RQGOZowxaZwYA+JaoKQwswGijBV4C6SiTUmpphMspJx9unX4KaimjDv9aaXOEBteBqmuuxgEHoLX6Kqx+yXqqBANsgCtit4FWQAEkrNbpq7HSOmtwag5w57GrmlJBASEU18ADjUYb3ADTinIttsgSB1oJFfA63bduimuqKB1keqwUhoCSK374wbujvOSu4QG6UvxBRydcpKsav++Ca6G8A6Pr1x2kVMyHwsVxUALDq/krnrhPSOzXG1lUTIoffqGR7Goi2MAxbv6O2kEG56I7CSlRsEFKFVyovDJoIRTg7sugNRDGqCJzJgcKE0ywc0ELm6KBCCJo8DIPFeCWNGcyqNFE06ToAfV0HBRgxsvLThHn1oddQMrXj5DyAQgjEHSAJMWZwS3HPxT/QMbabI/iBCliMLEJKX2EEkomBAUCxRi42VDADxyTYDVogV+wSChqmKxEKCDAYFDFj4OmwbY7bDGdBhtrnTQYOigeChUmc1K3QTnAUfEgGFgAWt88hKA6aCRIXhxnQ1yg3BCayK44EWdkUQcBByEQChFXfCB776aQsG0BIlQgQgE8qO26X1h8cEUep8ngRBnOy74E9QgRgEAC8SvOfQkh7FDBDmS43PmGoIiKUUEGkMEC/PJHgxw0xH74yx/3XnaYRJgMB8obxQW6kL9QYEJ0FIFgByfIL7/IQAlvQwEpnAC7DtLNJCKUoO/w45c44GwCXiAFB/OXAATQryUxdN4LfFiwgjCNYg+kYMIEFkCKDs6PKAIJouyGWMS1FSKJOMRB/BoIxYJIUXFUxNwoIkEKPAgCBZSQHQ1A2EWDfDEUVLyADj5AChSIQW6gu10bE/JG2VnCZGfo4R4d0sdQoBAHhPjhIB94v/wRoRKQWGRHgrhGSQJxCS+0pCZbEhAAOw==" + } +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/server/bruno/Notangles API/User Routes/folder.bru b/server/bruno/Notangles API/User Routes/folder.bru new file mode 100644 index 000000000..ecba7a72d --- /dev/null +++ b/server/bruno/Notangles API/User Routes/folder.bru @@ -0,0 +1,8 @@ +meta { + name: User Routes + seq: 1 +} + +auth { + mode: inherit +} diff --git a/server/bruno/Notangles API/bruno.json b/server/bruno/Notangles API/bruno.json new file mode 100644 index 000000000..ab46cab73 --- /dev/null +++ b/server/bruno/Notangles API/bruno.json @@ -0,0 +1,9 @@ +{ + "version": "1", + "name": "Notangles API", + "type": "collection", + "ignore": [ + "node_modules", + ".git" + ] +} \ No newline at end of file diff --git a/server/bruno/Notangles API/collection.bru b/server/bruno/Notangles API/collection.bru new file mode 100644 index 000000000..76972bec8 --- /dev/null +++ b/server/bruno/Notangles API/collection.bru @@ -0,0 +1,11 @@ +meta { + name: Notangles API +} + +auth { + mode: none +} + +vars:pre-request { + Local_Host_URL: http://localhost:3001/api +} diff --git a/server/bruno/Notangles API/environments/Local Guest 1.bru b/server/bruno/Notangles API/environments/Local Guest 1.bru new file mode 100644 index 000000000..e43d3f42a --- /dev/null +++ b/server/bruno/Notangles API/environments/Local Guest 1.bru @@ -0,0 +1,4 @@ +vars { + HostURL: http://localhost:3001/api + Session_Cookie : cookie.sid= +} diff --git a/server/src/graphql/graphql.service.ts b/server/src/graphql/graphql.service.ts index 1d0168d36..42dbbfd15 100644 --- a/server/src/graphql/graphql.service.ts +++ b/server/src/graphql/graphql.service.ts @@ -3,7 +3,7 @@ import { GraphQLClient } from 'graphql-request'; import { getSdk } from '../generated/graphql'; import type { ClassDetails } from './types'; -const HASURAGRES_GRAPHQL_API = 'https://graphql.csesoc.app/v1/graphql'; +const HASURAGRES_GRAPHQL_API = 'https://graphql.devsoc.app/v1/graphql'; @Injectable() export class GraphqlService { From ff8172b6f3552c9595439d4a5e093c8b4140ae44 Mon Sep 17 00:00:00 2001 From: jason4193 Date: Thu, 30 Oct 2025 01:50:14 +1100 Subject: [PATCH 16/17] Update Bruno API folder to support multiple user environment --- .../Class Routes/Get ClassesID from Course.bru | 6 +++--- .../Class Routes/Remove Class for Course.bru | 6 +++--- .../Class Routes/Select Class for Course.bru | 6 +++--- .../Notangles API/Course Routes/Add Course.bru | 7 ++++--- .../Course Routes/Get CourseID.bru | 7 ++++--- .../Course Routes/Remove Course.bru | 7 ++++--- .../Course Routes/Update Course Colour.bru | 7 ++++--- .../Notangles API/Course Routes/folder.bru | 2 +- .../Notangles API/Event Routes/Add event.bru | 6 +++--- .../Notangles API/Event Routes/Get event.bru | 6 +++--- ...{Delete event.bru => TODO Delete event.bru} | 3 ++- .../Event Routes/Update event.bru | 9 +++++++-- .../Friend Routes/Create Friend Request.bru | 16 ++++++++++++---- .../Friend Routes/Get Outgoing Requests.bru | 9 +++++++-- ...uest.bru => TODO Accept Friend Request.bru} | 3 ++- ...uest.bru => TODO Cancel Friend Request.bru} | 3 ++- server/bruno/Notangles API/New Request.bru | 15 --------------- .../Timetable Routes/Clear timetable.bru | 15 --------------- .../Timetable Routes/Create Timetable.bru | 7 ++++--- .../Timetable Routes/Delete Timetable.bru | 11 ++++++++--- .../Timetable Routes/Get Timetable By ID.bru | 4 ++-- .../Timetable Routes/Get User Timetables.bru | 4 ++-- .../Timetable Routes/Rename Timetable.bru | 11 ++++++++--- .../Timetable Make Primary.bru | 9 +++++++-- .../Notangles API/Timetable Routes/folder.bru | 2 +- .../User Routes/Get User Profile.bru | 2 +- .../Notangles API/User Routes/Guest Login.bru | 4 ++-- .../bruno/Notangles API/User Routes/folder.bru | 2 +- server/bruno/Notangles API/collection.bru | 18 ++++++++++++++++-- .../{Local Guest 1.bru => Alice.bru} | 0 .../bruno/Notangles API/environments/Bob.bru | 4 ++++ 31 files changed, 120 insertions(+), 91 deletions(-) rename server/bruno/Notangles API/Event Routes/{Delete event.bru => TODO Delete event.bru} (76%) rename server/bruno/Notangles API/Friend Routes/{Accept Friend Request.bru => TODO Accept Friend Request.bru} (69%) rename server/bruno/Notangles API/Friend Routes/{Cancel Friend Request.bru => TODO Cancel Friend Request.bru} (69%) delete mode 100644 server/bruno/Notangles API/New Request.bru delete mode 100644 server/bruno/Notangles API/Timetable Routes/Clear timetable.bru rename server/bruno/Notangles API/environments/{Local Guest 1.bru => Alice.bru} (100%) create mode 100644 server/bruno/Notangles API/environments/Bob.bru diff --git a/server/bruno/Notangles API/Class Routes/Get ClassesID from Course.bru b/server/bruno/Notangles API/Class Routes/Get ClassesID from Course.bru index 3c377dd35..66d63c4f5 100644 --- a/server/bruno/Notangles API/Class Routes/Get ClassesID from Course.bru +++ b/server/bruno/Notangles API/Class Routes/Get ClassesID from Course.bru @@ -5,9 +5,9 @@ meta { } get { - url: {{Local Host URL}}/user/timetables/classes/:timetableId/:courseId + url: {{HostURL}}/user/timetables/classes/:timetableId/:courseId body: none - auth: inherit + auth: none } params:path { @@ -16,7 +16,7 @@ params:path { } headers { - Cookie: {{Session Cookie}} + Cookie: {{Session_Cookie}} } settings { diff --git a/server/bruno/Notangles API/Class Routes/Remove Class for Course.bru b/server/bruno/Notangles API/Class Routes/Remove Class for Course.bru index cb421afad..7696861cb 100644 --- a/server/bruno/Notangles API/Class Routes/Remove Class for Course.bru +++ b/server/bruno/Notangles API/Class Routes/Remove Class for Course.bru @@ -5,9 +5,9 @@ meta { } delete { - url: {{Local Host URL}}/user/timetables/class/:timetableId/:courseId/:classId + url: {{HostURL}}/user/timetables/class/:timetableId/:courseId/:classId body: json - auth: inherit + auth: none } params:path { @@ -17,7 +17,7 @@ params:path { } headers { - Cookie: {{Session Cookie}} + Cookie: {{Session_Cookie}} } settings { diff --git a/server/bruno/Notangles API/Class Routes/Select Class for Course.bru b/server/bruno/Notangles API/Class Routes/Select Class for Course.bru index f76924f6c..3c64d9190 100644 --- a/server/bruno/Notangles API/Class Routes/Select Class for Course.bru +++ b/server/bruno/Notangles API/Class Routes/Select Class for Course.bru @@ -5,9 +5,9 @@ meta { } patch { - url: {{Local Host URL}}/user/timetables/class/:timetableId/:courseId + url: {{HostURL}}/user/timetables/class/:timetableId/:courseId body: json - auth: inherit + auth: none } params:path { @@ -16,7 +16,7 @@ params:path { } headers { - Cookie: {{Session Cookie}} + Cookie: {{Session_Cookie}} } body:json { diff --git a/server/bruno/Notangles API/Course Routes/Add Course.bru b/server/bruno/Notangles API/Course Routes/Add Course.bru index 2815816d0..d9dbdb464 100644 --- a/server/bruno/Notangles API/Course Routes/Add Course.bru +++ b/server/bruno/Notangles API/Course Routes/Add Course.bru @@ -5,9 +5,9 @@ meta { } post { - url: {{Local Host URL}}/user/timetables/course/:timetableId/:courseId + url: {{HostURL}}/user/timetables/course/:timetableId/:courseId body: json - auth: inherit + auth: none } params:path { @@ -16,7 +16,7 @@ params:path { } headers { - Cookie: {{Session Cookie}} + Cookie: {{Session_Cookie}} } body:json { @@ -27,4 +27,5 @@ body:json { settings { encodeUrl: true + timeout: 0 } diff --git a/server/bruno/Notangles API/Course Routes/Get CourseID.bru b/server/bruno/Notangles API/Course Routes/Get CourseID.bru index 855de6dc6..eccca5111 100644 --- a/server/bruno/Notangles API/Course Routes/Get CourseID.bru +++ b/server/bruno/Notangles API/Course Routes/Get CourseID.bru @@ -5,9 +5,9 @@ meta { } get { - url: {{Local Host URL}}/user/timetables/courses/:timetableId + url: {{HostURL}}/user/timetables/courses/:timetableId body: none - auth: inherit + auth: none } params:path { @@ -15,7 +15,7 @@ params:path { } headers { - Cookie: {{Session Cookie}} + Cookie: {{Session_Cookie}} } script:post-response { @@ -34,4 +34,5 @@ script:post-response { settings { encodeUrl: true + timeout: 0 } diff --git a/server/bruno/Notangles API/Course Routes/Remove Course.bru b/server/bruno/Notangles API/Course Routes/Remove Course.bru index b11bffe6f..051f7f295 100644 --- a/server/bruno/Notangles API/Course Routes/Remove Course.bru +++ b/server/bruno/Notangles API/Course Routes/Remove Course.bru @@ -5,9 +5,9 @@ meta { } delete { - url: {{Local Host URL}}/user/timetables/course/:timetableId/:courseId + url: {{HostURL}}/user/timetables/course/:timetableId/:courseId body: none - auth: inherit + auth: none } params:path { @@ -16,9 +16,10 @@ params:path { } headers { - Cookie: {{Session Cookie}} + Cookie: {{Session_Cookie}} } settings { encodeUrl: true + timeout: 0 } diff --git a/server/bruno/Notangles API/Course Routes/Update Course Colour.bru b/server/bruno/Notangles API/Course Routes/Update Course Colour.bru index 3739ad4e6..f219ec191 100644 --- a/server/bruno/Notangles API/Course Routes/Update Course Colour.bru +++ b/server/bruno/Notangles API/Course Routes/Update Course Colour.bru @@ -5,9 +5,9 @@ meta { } patch { - url: {{Local Host URL}}/user/timetables/course/:timetableId/:courseId/colour + url: {{HostURL}}/user/timetables/course/:timetableId/:courseId/colour body: json - auth: inherit + auth: none } params:path { @@ -16,7 +16,7 @@ params:path { } headers { - Cookie: {{Session Cookie}} + Cookie: {{Session_Cookie}} } body:json { @@ -27,4 +27,5 @@ body:json { settings { encodeUrl: true + timeout: 0 } diff --git a/server/bruno/Notangles API/Course Routes/folder.bru b/server/bruno/Notangles API/Course Routes/folder.bru index 1f0392ef6..c424ae673 100644 --- a/server/bruno/Notangles API/Course Routes/folder.bru +++ b/server/bruno/Notangles API/Course Routes/folder.bru @@ -4,5 +4,5 @@ meta { } auth { - mode: inherit + mode: none } diff --git a/server/bruno/Notangles API/Event Routes/Add event.bru b/server/bruno/Notangles API/Event Routes/Add event.bru index f884b630a..bfa4fd95c 100644 --- a/server/bruno/Notangles API/Event Routes/Add event.bru +++ b/server/bruno/Notangles API/Event Routes/Add event.bru @@ -5,9 +5,9 @@ meta { } post { - url: {{Local Host URL}}/user/timetables/event + url: {{HostURL}}/user/timetables/event body: json - auth: inherit + auth: none } params:query { @@ -15,7 +15,7 @@ params:query { } headers { - Cookie: {{Session Cookie}} + Cookie: {{Session_Cookie}} } body:json { diff --git a/server/bruno/Notangles API/Event Routes/Get event.bru b/server/bruno/Notangles API/Event Routes/Get event.bru index d76af40d1..8aef49f7c 100644 --- a/server/bruno/Notangles API/Event Routes/Get event.bru +++ b/server/bruno/Notangles API/Event Routes/Get event.bru @@ -5,9 +5,9 @@ meta { } get { - url: {{Local Host URL}}/user/classes/:timetableId/:eventId + url: {{HostURL}}/user/classes/:timetableId/:eventId body: none - auth: inherit + auth: none } params:path { @@ -16,7 +16,7 @@ params:path { } headers { - Cookie: {{Session Cookie}} + Cookie: {{Session_Cookie}} } settings { diff --git a/server/bruno/Notangles API/Event Routes/Delete event.bru b/server/bruno/Notangles API/Event Routes/TODO Delete event.bru similarity index 76% rename from server/bruno/Notangles API/Event Routes/Delete event.bru rename to server/bruno/Notangles API/Event Routes/TODO Delete event.bru index de43223ec..873bfb33b 100644 --- a/server/bruno/Notangles API/Event Routes/Delete event.bru +++ b/server/bruno/Notangles API/Event Routes/TODO Delete event.bru @@ -1,5 +1,5 @@ meta { - name: Delete event + name: TODO Delete event type: http seq: 3 } @@ -12,4 +12,5 @@ delete { settings { encodeUrl: true + timeout: 0 } diff --git a/server/bruno/Notangles API/Event Routes/Update event.bru b/server/bruno/Notangles API/Event Routes/Update event.bru index e7fc21f53..da5216449 100644 --- a/server/bruno/Notangles API/Event Routes/Update event.bru +++ b/server/bruno/Notangles API/Event Routes/Update event.bru @@ -5,15 +5,19 @@ meta { } patch { - url: {{Local Host URL}}/user/timetables/event/:eventid + url: {{HostURL}}/user/timetables/event/:eventid body: json - auth: inherit + auth: none } params:path { eventid: 5 } +headers { + Cookie: {{Session_Cookie}} +} + body:json { { "title": "Math Tute", @@ -29,4 +33,5 @@ body:json { settings { encodeUrl: true + timeout: 0 } diff --git a/server/bruno/Notangles API/Friend Routes/Create Friend Request.bru b/server/bruno/Notangles API/Friend Routes/Create Friend Request.bru index 46e14e26a..7321c2d3c 100644 --- a/server/bruno/Notangles API/Friend Routes/Create Friend Request.bru +++ b/server/bruno/Notangles API/Friend Routes/Create Friend Request.bru @@ -4,12 +4,20 @@ meta { seq: 1 } -get { - url: - body: none - auth: inherit +post { + url: {{HostURL}}/friendships + body: json + auth: none + +} + +body:json { + { + "requesteeCode": "" + } } settings { encodeUrl: true + timeout: 0 } diff --git a/server/bruno/Notangles API/Friend Routes/Get Outgoing Requests.bru b/server/bruno/Notangles API/Friend Routes/Get Outgoing Requests.bru index 85571107c..7e1f35ed8 100644 --- a/server/bruno/Notangles API/Friend Routes/Get Outgoing Requests.bru +++ b/server/bruno/Notangles API/Friend Routes/Get Outgoing Requests.bru @@ -5,11 +5,16 @@ meta { } get { - url: + url: {{HostURL}}/friendships/requests body: none - auth: inherit + auth: none +} + +headers { + Cookie: {{Session_Cookie}} } settings { encodeUrl: true + timeout: 0 } diff --git a/server/bruno/Notangles API/Friend Routes/Accept Friend Request.bru b/server/bruno/Notangles API/Friend Routes/TODO Accept Friend Request.bru similarity index 69% rename from server/bruno/Notangles API/Friend Routes/Accept Friend Request.bru rename to server/bruno/Notangles API/Friend Routes/TODO Accept Friend Request.bru index 9ca56fef4..8081ca2d4 100644 --- a/server/bruno/Notangles API/Friend Routes/Accept Friend Request.bru +++ b/server/bruno/Notangles API/Friend Routes/TODO Accept Friend Request.bru @@ -1,5 +1,5 @@ meta { - name: Accept Friend Request + name: TODO Accept Friend Request type: http seq: 4 } @@ -12,4 +12,5 @@ get { settings { encodeUrl: true + timeout: 0 } diff --git a/server/bruno/Notangles API/Friend Routes/Cancel Friend Request.bru b/server/bruno/Notangles API/Friend Routes/TODO Cancel Friend Request.bru similarity index 69% rename from server/bruno/Notangles API/Friend Routes/Cancel Friend Request.bru rename to server/bruno/Notangles API/Friend Routes/TODO Cancel Friend Request.bru index 6016bc2b2..575dd33bf 100644 --- a/server/bruno/Notangles API/Friend Routes/Cancel Friend Request.bru +++ b/server/bruno/Notangles API/Friend Routes/TODO Cancel Friend Request.bru @@ -1,5 +1,5 @@ meta { - name: Cancel Friend Request + name: TODO Cancel Friend Request type: http seq: 3 } @@ -12,4 +12,5 @@ get { settings { encodeUrl: true + timeout: 0 } diff --git a/server/bruno/Notangles API/New Request.bru b/server/bruno/Notangles API/New Request.bru deleted file mode 100644 index c07f4c48e..000000000 --- a/server/bruno/Notangles API/New Request.bru +++ /dev/null @@ -1,15 +0,0 @@ -meta { - name: New Request - type: http - seq: 8 -} - -get { - url: - body: none - auth: inherit -} - -settings { - encodeUrl: true -} diff --git a/server/bruno/Notangles API/Timetable Routes/Clear timetable.bru b/server/bruno/Notangles API/Timetable Routes/Clear timetable.bru deleted file mode 100644 index d125980c3..000000000 --- a/server/bruno/Notangles API/Timetable Routes/Clear timetable.bru +++ /dev/null @@ -1,15 +0,0 @@ -meta { - name: Clear timetable - type: http - seq: 7 -} - -get { - url: - body: none - auth: inherit -} - -settings { - encodeUrl: true -} diff --git a/server/bruno/Notangles API/Timetable Routes/Create Timetable.bru b/server/bruno/Notangles API/Timetable Routes/Create Timetable.bru index 060b85736..3b8d22f15 100644 --- a/server/bruno/Notangles API/Timetable Routes/Create Timetable.bru +++ b/server/bruno/Notangles API/Timetable Routes/Create Timetable.bru @@ -5,13 +5,13 @@ meta { } post { - url: {{Local Host URL}}/user/timetables + url: {{HostURL}}/user/timetables body: json - auth: inherit + auth: none } headers { - Cookie: {{Session Cookie}} + Cookie: {{Session_Cookie}} } body:json { @@ -24,4 +24,5 @@ body:json { settings { encodeUrl: true + timeout: 0 } diff --git a/server/bruno/Notangles API/Timetable Routes/Delete Timetable.bru b/server/bruno/Notangles API/Timetable Routes/Delete Timetable.bru index 1b4e32fd3..de331f288 100644 --- a/server/bruno/Notangles API/Timetable Routes/Delete Timetable.bru +++ b/server/bruno/Notangles API/Timetable Routes/Delete Timetable.bru @@ -5,15 +5,20 @@ meta { } delete { - url: {{Local Host URL}}/user/timetables/:id + url: {{HostURL}}/user/timetables/:id body: none - auth: inherit + auth: none } params:path { - id: d533e368-5666-4abc-88ca-347e5725b318 + id: 4ed45635-90c6-44cc-b6ce-3d4622c0d0f7 +} + +headers { + Cookie: {{Session_Cookie}} } settings { encodeUrl: true + timeout: 0 } diff --git a/server/bruno/Notangles API/Timetable Routes/Get Timetable By ID.bru b/server/bruno/Notangles API/Timetable Routes/Get Timetable By ID.bru index f8d1353d7..93e7ecedc 100644 --- a/server/bruno/Notangles API/Timetable Routes/Get Timetable By ID.bru +++ b/server/bruno/Notangles API/Timetable Routes/Get Timetable By ID.bru @@ -11,11 +11,11 @@ get { } params:path { - id: a1380156-e406-4a79-94cc-51f0b83abbc9 + id: 4ed45635-90c6-44cc-b6ce-3d4622c0d0f7 } headers { - Set-Cookie: {{Session_Cookie}} + Cookie: {{Session_Cookie}} } settings { diff --git a/server/bruno/Notangles API/Timetable Routes/Get User Timetables.bru b/server/bruno/Notangles API/Timetable Routes/Get User Timetables.bru index ff582243a..68efbb058 100644 --- a/server/bruno/Notangles API/Timetable Routes/Get User Timetables.bru +++ b/server/bruno/Notangles API/Timetable Routes/Get User Timetables.bru @@ -5,14 +5,14 @@ meta { } get { - url: {{HostURL}}/user/timetables?year=2025&term=T1 + url: {{HostURL}}/user/timetables?year=2025&term=T3 body: none auth: none } params:query { year: 2025 - term: T1 + term: T3 } headers { diff --git a/server/bruno/Notangles API/Timetable Routes/Rename Timetable.bru b/server/bruno/Notangles API/Timetable Routes/Rename Timetable.bru index 93cd22ae5..77a5b39ff 100644 --- a/server/bruno/Notangles API/Timetable Routes/Rename Timetable.bru +++ b/server/bruno/Notangles API/Timetable Routes/Rename Timetable.bru @@ -5,13 +5,17 @@ meta { } patch { - url: {{Local Host URL}}/user/timetables/:id/rename + url: {{HostURL}}/user/timetables/:id/rename body: json - auth: inherit + auth: none } params:path { - id: 7050d820-fa7f-41e1-806e-88df665340f0 + id: ab83b07a-a1cf-4579-83a2-f9dcf71eecc4 +} + +headers { + Cookie: {{Session_Cookie}} } body:json { @@ -22,4 +26,5 @@ body:json { settings { encodeUrl: true + timeout: 0 } diff --git a/server/bruno/Notangles API/Timetable Routes/Timetable Make Primary.bru b/server/bruno/Notangles API/Timetable Routes/Timetable Make Primary.bru index 7428c99ee..d414aaf1d 100644 --- a/server/bruno/Notangles API/Timetable Routes/Timetable Make Primary.bru +++ b/server/bruno/Notangles API/Timetable Routes/Timetable Make Primary.bru @@ -5,15 +5,20 @@ meta { } patch { - url: {{Local Host URL}}/user/timetables/:id/change-primary + url: {{HostURL}}/user/timetables/:id/change-primary body: none - auth: inherit + auth: none } params:path { id: 3b0cd623-3105-4503-9e0c-0e949134be51 } +headers { + Cookie: {{Session_Cookie}} +} + settings { encodeUrl: true + timeout: 0 } diff --git a/server/bruno/Notangles API/Timetable Routes/folder.bru b/server/bruno/Notangles API/Timetable Routes/folder.bru index 6fed0eab1..1fa863202 100644 --- a/server/bruno/Notangles API/Timetable Routes/folder.bru +++ b/server/bruno/Notangles API/Timetable Routes/folder.bru @@ -4,5 +4,5 @@ meta { } auth { - mode: inherit + mode: none } diff --git a/server/bruno/Notangles API/User Routes/Get User Profile.bru b/server/bruno/Notangles API/User Routes/Get User Profile.bru index 2ada451a6..656fc3e58 100644 --- a/server/bruno/Notangles API/User Routes/Get User Profile.bru +++ b/server/bruno/Notangles API/User Routes/Get User Profile.bru @@ -11,7 +11,7 @@ get { } headers { - Set-Cookie: {{Session_Cookie}} + Cookie: {{Session_Cookie}} } settings { diff --git a/server/bruno/Notangles API/User Routes/Guest Login.bru b/server/bruno/Notangles API/User Routes/Guest Login.bru index 55c85c09a..7c346063d 100644 --- a/server/bruno/Notangles API/User Routes/Guest Login.bru +++ b/server/bruno/Notangles API/User Routes/Guest Login.bru @@ -7,12 +7,12 @@ meta { get { url: {{HostURL}}/auth/login/guest body: none - auth: inherit + auth: none } script:post-response { const sessionCookie = res.getHeader('set-cookie') - bru.setEnvVar("Session_Cookie", sessionCookie); + bru.setEnvVar("Session_Cookie", sessionCookie[0].split(';')[0]); } diff --git a/server/bruno/Notangles API/User Routes/folder.bru b/server/bruno/Notangles API/User Routes/folder.bru index ecba7a72d..6e605b75d 100644 --- a/server/bruno/Notangles API/User Routes/folder.bru +++ b/server/bruno/Notangles API/User Routes/folder.bru @@ -4,5 +4,5 @@ meta { } auth { - mode: inherit + mode: none } diff --git a/server/bruno/Notangles API/collection.bru b/server/bruno/Notangles API/collection.bru index 76972bec8..97ee1a39d 100644 --- a/server/bruno/Notangles API/collection.bru +++ b/server/bruno/Notangles API/collection.bru @@ -6,6 +6,20 @@ auth { mode: none } -vars:pre-request { - Local_Host_URL: http://localhost:3001/api +docs { + # Notangles API Collection + + This collection contains all **Notangles API** endpoints and usage notes. + + ## Setup Instructions + + Before using the requests, follow these steps to set up your Bruno environment variables: + + 1. Go to **Settings > General** and disable: + - `Store Cookies automatically` + - `Send Cookies automatically` + 2. Select one of the user environments (e.g., **Alice**, **Bob**). + 3. Use the **Guest Login** endpoint to get a session cookie first. + - If you see an error about not finding the FE on port `:5173`, check that the request setting **Automatically Follow Redirects** is disabled. + 4. Use the other endpoints as needed. } diff --git a/server/bruno/Notangles API/environments/Local Guest 1.bru b/server/bruno/Notangles API/environments/Alice.bru similarity index 100% rename from server/bruno/Notangles API/environments/Local Guest 1.bru rename to server/bruno/Notangles API/environments/Alice.bru diff --git a/server/bruno/Notangles API/environments/Bob.bru b/server/bruno/Notangles API/environments/Bob.bru new file mode 100644 index 000000000..e43d3f42a --- /dev/null +++ b/server/bruno/Notangles API/environments/Bob.bru @@ -0,0 +1,4 @@ +vars { + HostURL: http://localhost:3001/api + Session_Cookie : cookie.sid= +} From 0645ac27b9c45ac1d31bef685247f12416c5d40b Mon Sep 17 00:00:00 2001 From: rfquin Date: Sat, 1 Nov 2025 20:47:26 +1100 Subject: [PATCH 17/17] Add test routes to bruno - body codes are empty --- ... Request.bru => Accept Friend Request.bru} | 9 ++++---- .../Friend Routes/Cancel Friend Request.bru | 22 +++++++++++++++++++ .../Friend Routes/Create Friend Request.bru | 1 - ...ept Friend Request.bru => Get Friends.bru} | 5 ++--- .../Friend Routes/Reject Friend Request.bru | 22 +++++++++++++++++++ .../Friend Routes/Remove Friend Request.bru | 22 +++++++++++++++++++ 6 files changed, 72 insertions(+), 9 deletions(-) rename server/bruno/Notangles API/Friend Routes/{TODO Cancel Friend Request.bru => Accept Friend Request.bru} (50%) create mode 100644 server/bruno/Notangles API/Friend Routes/Cancel Friend Request.bru rename server/bruno/Notangles API/Friend Routes/{TODO Accept Friend Request.bru => Get Friends.bru} (64%) create mode 100644 server/bruno/Notangles API/Friend Routes/Reject Friend Request.bru create mode 100644 server/bruno/Notangles API/Friend Routes/Remove Friend Request.bru diff --git a/server/bruno/Notangles API/Friend Routes/TODO Cancel Friend Request.bru b/server/bruno/Notangles API/Friend Routes/Accept Friend Request.bru similarity index 50% rename from server/bruno/Notangles API/Friend Routes/TODO Cancel Friend Request.bru rename to server/bruno/Notangles API/Friend Routes/Accept Friend Request.bru index 575dd33bf..a59612d9a 100644 --- a/server/bruno/Notangles API/Friend Routes/TODO Cancel Friend Request.bru +++ b/server/bruno/Notangles API/Friend Routes/Accept Friend Request.bru @@ -1,16 +1,15 @@ meta { - name: TODO Cancel Friend Request + name: Accept Friend Request type: http - seq: 3 + seq: 5 } -get { - url: +post { + url: {{HostURL}}/friendships/accept body: none auth: inherit } settings { encodeUrl: true - timeout: 0 } diff --git a/server/bruno/Notangles API/Friend Routes/Cancel Friend Request.bru b/server/bruno/Notangles API/Friend Routes/Cancel Friend Request.bru new file mode 100644 index 000000000..fa7c754e6 --- /dev/null +++ b/server/bruno/Notangles API/Friend Routes/Cancel Friend Request.bru @@ -0,0 +1,22 @@ +meta { + name: Cancel Friend Request + type: http + seq: 5 +} + +post { + url: friendships/cancel + body: json + auth: inherit +} + +body:json { + { + "requesteeCode": "" + } +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/server/bruno/Notangles API/Friend Routes/Create Friend Request.bru b/server/bruno/Notangles API/Friend Routes/Create Friend Request.bru index 7321c2d3c..7c0b074e1 100644 --- a/server/bruno/Notangles API/Friend Routes/Create Friend Request.bru +++ b/server/bruno/Notangles API/Friend Routes/Create Friend Request.bru @@ -8,7 +8,6 @@ post { url: {{HostURL}}/friendships body: json auth: none - } body:json { diff --git a/server/bruno/Notangles API/Friend Routes/TODO Accept Friend Request.bru b/server/bruno/Notangles API/Friend Routes/Get Friends.bru similarity index 64% rename from server/bruno/Notangles API/Friend Routes/TODO Accept Friend Request.bru rename to server/bruno/Notangles API/Friend Routes/Get Friends.bru index 8081ca2d4..f6df5f0d9 100644 --- a/server/bruno/Notangles API/Friend Routes/TODO Accept Friend Request.bru +++ b/server/bruno/Notangles API/Friend Routes/Get Friends.bru @@ -1,16 +1,15 @@ meta { - name: TODO Accept Friend Request + name: Get Friends type: http seq: 4 } get { - url: + url: {{HostURL}}/friendships/ body: none auth: inherit } settings { encodeUrl: true - timeout: 0 } diff --git a/server/bruno/Notangles API/Friend Routes/Reject Friend Request.bru b/server/bruno/Notangles API/Friend Routes/Reject Friend Request.bru new file mode 100644 index 000000000..60fcab59d --- /dev/null +++ b/server/bruno/Notangles API/Friend Routes/Reject Friend Request.bru @@ -0,0 +1,22 @@ +meta { + name: Reject Friend Request + type: http + seq: 6 +} + +get { + url: {{HostURL}}/friendships/reject + body: json + auth: inherit +} + +body:json { + { + "requestorCode": "" + } +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/server/bruno/Notangles API/Friend Routes/Remove Friend Request.bru b/server/bruno/Notangles API/Friend Routes/Remove Friend Request.bru new file mode 100644 index 000000000..cfd0d47a1 --- /dev/null +++ b/server/bruno/Notangles API/Friend Routes/Remove Friend Request.bru @@ -0,0 +1,22 @@ +meta { + name: Remove Friend Request + type: http + seq: 6 +} + +get { + url: {{HostURL}}/friendships/remove + body: json + auth: inherit +} + +body:json { + { + "otherCode": "" + } +} + +settings { + encodeUrl: true + timeout: 0 +}