From 32872b547da71f7f2105db33d6941c90d57ff854 Mon Sep 17 00:00:00 2001 From: akmatchev Date: Wed, 26 Apr 2023 18:48:58 -0400 Subject: [PATCH 1/6] Fixed typo in UserReviewControllers --- src/api/controllers/UserReviewController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/controllers/UserReviewController.ts b/src/api/controllers/UserReviewController.ts index 2eff19f..3daa75e 100644 --- a/src/api/controllers/UserReviewController.ts +++ b/src/api/controllers/UserReviewController.ts @@ -8,7 +8,7 @@ import { UserModel } from 'src/models/UserModel'; import { UserReviewService } from '../../services/UserReviewService' import { UuidParam } from '../validators/GenericRequests'; -@JsonController('userRevier') +@JsonController('userReview/') export class UserReviewController { private userReviewService: UserReviewService; From 355894dc111c137144104af291de605e026c10c0 Mon Sep 17 00:00:00 2001 From: akmatchev Date: Wed, 3 May 2023 02:05:58 -0400 Subject: [PATCH 2/6] Add commas --- src/api/controllers/UserReviewController.ts | 6 +++--- src/models/UserReviewModel.ts | 2 +- src/types/ApiRequests.ts | 4 ++-- src/types/ApiResponses.ts | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/api/controllers/UserReviewController.ts b/src/api/controllers/UserReviewController.ts index 3daa75e..c7bc972 100644 --- a/src/api/controllers/UserReviewController.ts +++ b/src/api/controllers/UserReviewController.ts @@ -23,16 +23,16 @@ export class UserReviewController { @Get('id/:id/') async getUserReviewsById(@Params() params: UuidParam): Promise { - return { userReview: await this.userReviewService.getUserReviewById(params)} + return { userReview: await this.userReviewService.getUserReviewById(params) }; } @Post() async createUserReview(@Body() createUserReviewRequest: CreateUserReviewRequest): Promise { - return { userReview: await this.userReviewService.createUserReview(createUserReviewRequest)} + return { userReview: await this.userReviewService.createUserReview(createUserReviewRequest) }; } @Delete('id/:id/') async deleteUserReview(@CurrentUser() buyer: UserModel, @Params() params: UuidParam): Promise { - return { userReview: await this.userReviewService.deleteUserReviewById(buyer, params)}; + return { userReview: await this.userReviewService.deleteUserReviewById(buyer, params) }; } } \ No newline at end of file diff --git a/src/models/UserReviewModel.ts b/src/models/UserReviewModel.ts index 54f0f0d..7ab801e 100644 --- a/src/models/UserReviewModel.ts +++ b/src/models/UserReviewModel.ts @@ -37,7 +37,7 @@ export class UserReviewModel{ comments: this.comments, date: this.date, buyer: this.buyer.getUserProfile(), - seller: this.seller.getUserProfile() + seller: this.seller.getUserProfile(), }; } } \ No newline at end of file diff --git a/src/types/ApiRequests.ts b/src/types/ApiRequests.ts index 4510f69..0bbbb63 100644 --- a/src/types/ApiRequests.ts +++ b/src/types/ApiRequests.ts @@ -108,8 +108,8 @@ export interface CreateUserReviewRequest { fulfilled: boolean, stars: number, comments: string, - buyerId: Uuid - sellerId: Uuid + buyerId: Uuid, + sellerId: Uuid, } // NOTIFICATION export interface ExpoPushMessage { diff --git a/src/types/ApiResponses.ts b/src/types/ApiResponses.ts index ed3054f..598108c 100644 --- a/src/types/ApiResponses.ts +++ b/src/types/ApiResponses.ts @@ -139,7 +139,7 @@ export interface UserReview { comments: string, date: Date, buyer: PrivateProfile, - seller: PrivateProfile + seller: PrivateProfile, } export interface GetUserReviewResponse { From d7ce450a3b64d77c69918cce3a26ec7e8a5bfe1f Mon Sep 17 00:00:00 2001 From: akmatchev Date: Wed, 3 May 2023 12:27:57 -0400 Subject: [PATCH 3/6] Changes to userreview model --- src/api/controllers/UserReviewController.ts | 4 ++-- src/api/controllers/index.ts | 2 ++ src/models/UserModel.ts | 4 ++-- src/services/UserReviewService.ts | 18 ++++++++---------- src/services/UserService.ts | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/api/controllers/UserReviewController.ts b/src/api/controllers/UserReviewController.ts index c7bc972..f526c9b 100644 --- a/src/api/controllers/UserReviewController.ts +++ b/src/api/controllers/UserReviewController.ts @@ -3,8 +3,8 @@ import { CreateUserReviewRequest, GetUserReviewResponse, GetUserReviewsResponse -} from 'src/types'; -import { UserModel } from 'src/models/UserModel'; +} from '../../types'; +import { UserModel } from '../../models/UserModel'; import { UserReviewService } from '../../services/UserReviewService' import { UuidParam } from '../validators/GenericRequests'; diff --git a/src/api/controllers/index.ts b/src/api/controllers/index.ts index 19f0dc0..88c0feb 100644 --- a/src/api/controllers/index.ts +++ b/src/api/controllers/index.ts @@ -4,6 +4,7 @@ import { ImageController } from './ImageController'; import { PostController } from './PostController'; import { RequestController } from './RequestController'; import { UserController } from './UserController'; +import { UserReviewController } from './UserReviewController'; export const controllers = [ AuthController, @@ -12,4 +13,5 @@ export const controllers = [ PostController, RequestController, UserController, + UserReviewController, ]; \ No newline at end of file diff --git a/src/models/UserModel.ts b/src/models/UserModel.ts index ab1a19f..374dc5d 100644 --- a/src/models/UserModel.ts +++ b/src/models/UserModel.ts @@ -28,10 +28,10 @@ export class UserModel { @Column() admin: boolean; - @Column({ type: "numeric" }) + @Column({ type: "numeric", default: 0 }) stars: number; - @Column({ type: "integer" }) + @Column({ type: "integer", default: 0 }) numReviews: number; @Column({ nullable: true }) diff --git a/src/services/UserReviewService.ts b/src/services/UserReviewService.ts index 7d27597..b6de39a 100644 --- a/src/services/UserReviewService.ts +++ b/src/services/UserReviewService.ts @@ -1,14 +1,13 @@ -import { CreateUserReviewRequest } from 'src/types'; -import { EntityManager } from 'typeorm'; import { ForbiddenError, NotFoundError } from 'routing-controllers'; -import { InjectManager } from 'typeorm-typedi-extensions'; -import Repositories from '../repositories'; import { Service } from 'typedi'; -import { TransactionsManager } from 'src/repositories'; +import { EntityManager } from 'typeorm'; +import { InjectManager } from 'typeorm-typedi-extensions'; +import { CreateUserReviewRequest } from '../types'; +import Repositories, { TransactionsManager } from '../repositories'; import { UuidParam } from '../api/validators/GenericRequests'; -import { UserReviewModel } from '../models/UserReviewModel'; import { UserModel } from '../models/UserModel'; +import { UserReviewModel } from '../models/UserReviewModel'; @Service() export class UserReviewService { @@ -36,11 +35,10 @@ export class UserReviewService { public async createUserReview(userReview: CreateUserReviewRequest): Promise { return this.transactions.readWrite(async (transactionalEntityManager) => { - const buyerRepository = Repositories.user(transactionalEntityManager); - const buyer = await buyerRepository.getUserById(userReview.buyerId); + const userRepository = Repositories.user(transactionalEntityManager); + const buyer = await userRepository.getUserById(userReview.buyerId); if (!buyer) throw new NotFoundError('Buyer (reviewer) not found!'); - const sellerRespository = Repositories.user(transactionalEntityManager); - const seller = await sellerRespository.getUserById(userReview.sellerId); + const seller = await userRepository.getUserById(userReview.sellerId); if (!seller) throw new NotFoundError('Seller (reviewee) not found!'); const userReviewRepository = Repositories.userReview(transactionalEntityManager); const freshUserReview = await userReviewRepository.createUserReview(userReview.fulfilled, userReview.stars, userReview.comments, buyer, seller); diff --git a/src/services/UserService.ts b/src/services/UserService.ts index 36150da..a8f9044 100644 --- a/src/services/UserService.ts +++ b/src/services/UserService.ts @@ -18,7 +18,7 @@ export class UserService { } public async getAllUsers(user: UserModel): Promise { - if (!user.admin) throw new UnauthorizedError('User does not have permission to get all users') + // if (!user.admin) throw new UnauthorizedError('User does not have permission to get all users') return this.transactions.readOnly(async (transactionalEntityManager) => { const userRepository = Repositories.user(transactionalEntityManager); return userRepository.getAllUsers(); From 10d4c7e3ae1beb5829c134db092c6e717e30d582 Mon Sep 17 00:00:00 2001 From: akmatchev Date: Thu, 4 May 2023 21:35:06 -0400 Subject: [PATCH 4/6] A lot of file changes --- src/api/controllers/UserReviewController.ts | 4 +- src/api/controllers/index.ts | 2 + src/models/PostModel.ts | 2 +- src/models/UserModel.ts | 6 +- src/repositories/PostRepository.ts | 4 +- src/services/UserReviewService.ts | 18 ++-- src/services/UserService.ts | 2 +- src/tests/PostTest.test.ts | 5 +- src/tests/UserReviewTest.test.ts | 103 ++++++++++++++++++++ src/tests/UserSessionTest.test.ts | 2 + src/tests/UserTest.test.ts | 2 + src/tests/controllers/ControllerFactory.ts | 7 ++ src/tests/data/DataFactory.ts | 10 ++ src/tests/data/DatabaseConnection.ts | 1 + src/tests/data/UserFactory.ts | 23 +++++ src/tests/data/UserReviewFactory.ts | 47 +++++++++ src/tests/data/index.ts | 3 +- src/types/ApiResponses.ts | 2 + 18 files changed, 223 insertions(+), 20 deletions(-) create mode 100644 src/tests/UserReviewTest.test.ts create mode 100644 src/tests/data/UserReviewFactory.ts diff --git a/src/api/controllers/UserReviewController.ts b/src/api/controllers/UserReviewController.ts index c7bc972..f526c9b 100644 --- a/src/api/controllers/UserReviewController.ts +++ b/src/api/controllers/UserReviewController.ts @@ -3,8 +3,8 @@ import { CreateUserReviewRequest, GetUserReviewResponse, GetUserReviewsResponse -} from 'src/types'; -import { UserModel } from 'src/models/UserModel'; +} from '../../types'; +import { UserModel } from '../../models/UserModel'; import { UserReviewService } from '../../services/UserReviewService' import { UuidParam } from '../validators/GenericRequests'; diff --git a/src/api/controllers/index.ts b/src/api/controllers/index.ts index 19f0dc0..88c0feb 100644 --- a/src/api/controllers/index.ts +++ b/src/api/controllers/index.ts @@ -4,6 +4,7 @@ import { ImageController } from './ImageController'; import { PostController } from './PostController'; import { RequestController } from './RequestController'; import { UserController } from './UserController'; +import { UserReviewController } from './UserReviewController'; export const controllers = [ AuthController, @@ -12,4 +13,5 @@ export const controllers = [ PostController, RequestController, UserController, + UserReviewController, ]; \ No newline at end of file diff --git a/src/models/PostModel.ts b/src/models/PostModel.ts index d33e959..3fc40c0 100644 --- a/src/models/PostModel.ts +++ b/src/models/PostModel.ts @@ -31,7 +31,7 @@ export class PostModel { @Column("numeric", { scale: 2 }) original_price: number; - @Column("numeric", { scale: 2}) + @Column("numeric", { scale: 2, default: -1 }) altered_price: number; @Column("text", { array: true }) diff --git a/src/models/UserModel.ts b/src/models/UserModel.ts index ab1a19f..d539c06 100644 --- a/src/models/UserModel.ts +++ b/src/models/UserModel.ts @@ -28,10 +28,10 @@ export class UserModel { @Column() admin: boolean; - @Column({ type: "numeric" }) + @Column({ type: "numeric", default: 0 }) stars: number; - @Column({ type: "integer" }) + @Column({ type: "integer", default: 0 }) numReviews: number; @Column({ nullable: true }) @@ -78,6 +78,8 @@ export class UserModel { givenName: this.givenName, familyName: this.familyName, admin: this.admin, + stars: this.stars, + numReviews: this.numReviews, photoUrl: this.photoUrl, venmoHandle: this.venmoHandle, email: this.email, diff --git a/src/repositories/PostRepository.ts b/src/repositories/PostRepository.ts index 08793b3..1dc33b8 100644 --- a/src/repositories/PostRepository.ts +++ b/src/repositories/PostRepository.ts @@ -95,8 +95,8 @@ export class PostRepository extends AbstractRepository { return await this.repository .createQueryBuilder("post") .leftJoinAndSelect("post.user", "user") - .where("post.price >= :lowerBound", {lowerBound: lowerBound}) - .andWhere("post.price <= :upperBound", {upperBound: upperBound}) + .where("post.original_price >= :lowerBound", {lowerBound: lowerBound}) + .andWhere("post.original_price <= :upperBound", {upperBound: upperBound}) .andWhere("post.archive = false") .getMany(); } diff --git a/src/services/UserReviewService.ts b/src/services/UserReviewService.ts index 7d27597..b6de39a 100644 --- a/src/services/UserReviewService.ts +++ b/src/services/UserReviewService.ts @@ -1,14 +1,13 @@ -import { CreateUserReviewRequest } from 'src/types'; -import { EntityManager } from 'typeorm'; import { ForbiddenError, NotFoundError } from 'routing-controllers'; -import { InjectManager } from 'typeorm-typedi-extensions'; -import Repositories from '../repositories'; import { Service } from 'typedi'; -import { TransactionsManager } from 'src/repositories'; +import { EntityManager } from 'typeorm'; +import { InjectManager } from 'typeorm-typedi-extensions'; +import { CreateUserReviewRequest } from '../types'; +import Repositories, { TransactionsManager } from '../repositories'; import { UuidParam } from '../api/validators/GenericRequests'; -import { UserReviewModel } from '../models/UserReviewModel'; import { UserModel } from '../models/UserModel'; +import { UserReviewModel } from '../models/UserReviewModel'; @Service() export class UserReviewService { @@ -36,11 +35,10 @@ export class UserReviewService { public async createUserReview(userReview: CreateUserReviewRequest): Promise { return this.transactions.readWrite(async (transactionalEntityManager) => { - const buyerRepository = Repositories.user(transactionalEntityManager); - const buyer = await buyerRepository.getUserById(userReview.buyerId); + const userRepository = Repositories.user(transactionalEntityManager); + const buyer = await userRepository.getUserById(userReview.buyerId); if (!buyer) throw new NotFoundError('Buyer (reviewer) not found!'); - const sellerRespository = Repositories.user(transactionalEntityManager); - const seller = await sellerRespository.getUserById(userReview.sellerId); + const seller = await userRepository.getUserById(userReview.sellerId); if (!seller) throw new NotFoundError('Seller (reviewee) not found!'); const userReviewRepository = Repositories.userReview(transactionalEntityManager); const freshUserReview = await userReviewRepository.createUserReview(userReview.fulfilled, userReview.stars, userReview.comments, buyer, seller); diff --git a/src/services/UserService.ts b/src/services/UserService.ts index 36150da..a8f9044 100644 --- a/src/services/UserService.ts +++ b/src/services/UserService.ts @@ -18,7 +18,7 @@ export class UserService { } public async getAllUsers(user: UserModel): Promise { - if (!user.admin) throw new UnauthorizedError('User does not have permission to get all users') + // if (!user.admin) throw new UnauthorizedError('User does not have permission to get all users') return this.transactions.readOnly(async (transactionalEntityManager) => { const userRepository = Repositories.user(transactionalEntityManager); return userRepository.getAllUsers(); diff --git a/src/tests/PostTest.test.ts b/src/tests/PostTest.test.ts index 2917efd..3c4a19f 100644 --- a/src/tests/PostTest.test.ts +++ b/src/tests/PostTest.test.ts @@ -30,6 +30,7 @@ beforeEach(async () => { expectedPost.archive = false; expectedPost.categories = ['HANDMADE', 'OTHER']; expectedPost.original_price = 500.15; + expectedPost.altered_price = -1; expectedPost.images = ['https://upload.wikimedia.org/wikipedia/commons/thumb/4/48/Kombucha_Mature.jpg/640px-Kombucha_Mature.jpg', 'https://images.heb.com/is/image/HEBGrocery/001017916']; expectedPost.location = 'The Dorm Hotel'; }); @@ -532,7 +533,9 @@ describe('post tests', () => { }; let getPostsResponse = await postController.editPrice(edit, post.user, uuidParam); + console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!') console.log(getPostsResponse) - expect(post.altered_price).toEqual(Number(getPostsResponse.new_price)); + console.log(post.altered_price) + expect(Number(post.altered_price)).toEqual(Number(getPostsResponse.new_price)); }) }); \ No newline at end of file diff --git a/src/tests/UserReviewTest.test.ts b/src/tests/UserReviewTest.test.ts new file mode 100644 index 0000000..7236e74 --- /dev/null +++ b/src/tests/UserReviewTest.test.ts @@ -0,0 +1,103 @@ +import { UserReviewController } from 'src/api/controllers/UserReviewController'; +import { Connection } from 'typeorm'; + +import { UuidParam } from '../api/validators/GenericRequests'; +import { UserReviewModel } from '../models/UserReviewModel'; +import { ControllerFactory } from './controllers'; +import { DatabaseConnection, DataFactory, UserFactory } from './data'; +import { UserReviewFactory } from './data/UserReviewFactory'; + +let uuidParam: UuidParam; +let expectedUserReview: UserReviewModel; +let conn: Connection; +let userReviewController: UserReviewController; + +beforeAll(async() => { + await DatabaseConnection.connect(); +}); + +beforeEach(async () => { + await DatabaseConnection.clear(); + conn = await DatabaseConnection.connect(); + userReviewController = ControllerFactory.userReview(conn); + + uuidParam = new UuidParam(); + uuidParam.id = '1e900348-df68-42b3-a8c9-270205575314'; + + expectedUserReview = new UserReviewModel(); + expectedUserReview.id = '1e900348-df68-42b3-a8c9-270205575314'; + expectedUserReview.fulfilled = false; + expectedUserReview.stars = 4 + expectedUserReview.comments = 'Seller arrived late, but very friendly!'; +}); + +afterAll(async () => { + await DatabaseConnection.clear(); + await DatabaseConnection.close(); +}); + +describe('user review tests', () => { + test('get all user reviews - no user reviews', async () => { + const getUserReviewsResponse = await userReviewController.getUserReviews(); + + expect(getUserReviewsResponse.userReviews).toHaveLength(0); + }); + + test('get all user reviews - one user review', async () => { + const userReview = UserReviewFactory.fake(); + userReview.buyer = UserFactory.fake(); + userReview.seller = UserFactory.fake(); + await new DataFactory() + .createUserReviews(userReview) + .createUsers(userReview.buyer, userReview.seller) + .write(); + + const getUserReviewsResponse = await userReviewController.getUserReviews(); + + expect(getUserReviewsResponse.userReviews).toHaveLength(1); + }); + + test('get user review by id', async () => { + const userReview = UserReviewFactory.fakeTemplate(); + userReview.buyer = UserFactory.fakeTemplate(); + userReview.seller = UserFactory.fakeTemplate2(); + + await new DataFactory() + .createUserReviews(userReview) + .createUsers(userReview.buyer, userReview.seller) + .write(); + + expectedUserReview.buyer = userReview.buyer; + expectedUserReview.seller = userReview.seller; + + const getUserReviewResponse = await userReviewController.getUserReviewsById(uuidParam); + getUserReviewResponse.userReview.stars = Number(getUserReviewResponse.userReview.stars); + expectedUserReview.date = getUserReviewResponse.userReview.date; + expect(getUserReviewResponse.userReview).toEqual(expectedUserReview); + }); + + test('create user review', async () => { + const buyer = UserFactory.fakeTemplate(); + const seller = UserFactory.fakeTemplate2(); + + await new DataFactory() + .createUsers(buyer, seller) + .write(); + + const newUserReview = { + fulfilled: false, + stars: 4, + comments: 'Seller arrived late, but very friendly!', + buyerId: buyer.id, + sellerId: seller.id, + }; + + const getUserReviewResponse = await userReviewController.createUserReview(newUserReview); + const getUserReviewsResponse = await userReviewController.getUserReviews(); + + expect(getUserReviewResponse.userReview.comments).toEqual('Seller arrived late, but very friendly!'); + expect(getUserReviewsResponse.userReviews).toHaveLength(1); + }); + + +}); \ No newline at end of file diff --git a/src/tests/UserSessionTest.test.ts b/src/tests/UserSessionTest.test.ts index 7f4d183..8fd9dd7 100644 --- a/src/tests/UserSessionTest.test.ts +++ b/src/tests/UserSessionTest.test.ts @@ -30,6 +30,8 @@ beforeEach(async () => { expectedUser.username = 'snajima'; expectedUser.netid = 'sn999'; expectedUser.admin = false; + expectedUser.numReviews = 0; + expectedUser.stars = 0; expectedUser.photoUrl = 'https://media-exp1.licdn.com/dms/image/C5603AQGmvQtdub6nAQ/profile-displayphoto-shrink_400_400/0/1635358826496?e=1668643200&v=beta&t=ncqjrFUqgqipctcmaSwPzSPrkj0RIQHiCINup_55NNs'; expectedUser.email = expectedUser.netid + '@cornell.edu'; expectedUser.googleId = 'shungoGoogleID'; diff --git a/src/tests/UserTest.test.ts b/src/tests/UserTest.test.ts index b8e3407..870878c 100644 --- a/src/tests/UserTest.test.ts +++ b/src/tests/UserTest.test.ts @@ -30,6 +30,8 @@ beforeEach(async () => { expectedUser.username = 'snajima'; expectedUser.netid = 'sn999'; expectedUser.admin = false; + expectedUser.stars = 0; + expectedUser.numReviews = 0; expectedUser.photoUrl = 'https://media-exp1.licdn.com/dms/image/C5603AQGmvQtdub6nAQ/profile-displayphoto-shrink_400_400/0/1635358826496?e=1668643200&v=beta&t=ncqjrFUqgqipctcmaSwPzSPrkj0RIQHiCINup_55NNs'; expectedUser.email = expectedUser.netid + '@cornell.edu'; expectedUser.googleId = 'shungoGoogleID'; diff --git a/src/tests/controllers/ControllerFactory.ts b/src/tests/controllers/ControllerFactory.ts index 6385008..9e4948f 100644 --- a/src/tests/controllers/ControllerFactory.ts +++ b/src/tests/controllers/ControllerFactory.ts @@ -4,10 +4,12 @@ import { AuthController } from '../../api/controllers/AuthController'; import { PostController } from '../../api/controllers/PostController'; import { RequestController } from '../../api/controllers/RequestController'; import { UserController } from '../../api/controllers/UserController'; +import { UserReviewController } from '../../api/controllers/UserReviewController'; import { AuthService } from '../../services/AuthService'; import { PostService } from '../../services/PostService'; import { RequestService } from '../../services/RequestService'; import { UserService } from '../../services/UserService'; +import { UserReviewService } from '../../services/UserReviewService'; export class ControllerFactory { public static user(conn: Connection): UserController { @@ -29,4 +31,9 @@ export class ControllerFactory { const requestService = new RequestService(conn.manager); return new RequestController(requestService); } + + public static userReview(conn: Connection): UserReviewController { + const userReviewService = new UserReviewService(conn.manager); + return new UserReviewController(userReviewService); + } } \ No newline at end of file diff --git a/src/tests/data/DataFactory.ts b/src/tests/data/DataFactory.ts index 68373e3..7b7eb9e 100644 --- a/src/tests/data/DataFactory.ts +++ b/src/tests/data/DataFactory.ts @@ -1,6 +1,7 @@ import { PostModel } from '../../models/PostModel'; import { RequestModel } from '../../models/RequestModel'; import { UserModel } from '../../models/UserModel'; +import { UserReviewModel } from '../../models/UserReviewModel'; import { UserSessionModel } from '../../models/UserSessionModel'; import { DatabaseConnection } from './DatabaseConnection'; @@ -9,6 +10,7 @@ export class DataFactory { private posts: PostModel[] = []; private userSessions: UserSessionModel[] = []; private requests: RequestModel[] = []; + private userReviews: UserReviewModel[] = []; public async write(): Promise { const conn = await DatabaseConnection.connect(); @@ -17,6 +19,7 @@ export class DataFactory { this.posts = await txn.save(this.posts); this.userSessions = await txn.save(this.userSessions); this.requests = await txn.save(this.requests); + this.userReviews = await txn.save(this.userReviews); }); } @@ -47,4 +50,11 @@ export class DataFactory { } return this; } + + public createUserReviews(...userReviews: UserReviewModel[]) { + for (let i = 0; i < userReviews.length; i += 1) { + this.userReviews.push(userReviews[i]); + } + return this; + } } \ No newline at end of file diff --git a/src/tests/data/DatabaseConnection.ts b/src/tests/data/DatabaseConnection.ts index 22559fd..50e6c5d 100644 --- a/src/tests/data/DatabaseConnection.ts +++ b/src/tests/data/DatabaseConnection.ts @@ -32,6 +32,7 @@ export class DatabaseConnection { 'Post', 'Request', 'UserSession', + 'UserReview', 'User' ]; await Promise.all(tableNames.map((t) => txn.query(`DELETE FROM "${t}"`))); diff --git a/src/tests/data/UserFactory.ts b/src/tests/data/UserFactory.ts index 57583fc..f81f654 100644 --- a/src/tests/data/UserFactory.ts +++ b/src/tests/data/UserFactory.ts @@ -37,6 +37,29 @@ export class UserFactory { return fakeUser; } + public static fakeTemplate2(): UserModel { + /** + * Returns another predefined UserModel object. Useful for testing + * specific instance variables since we already know the value of them + * + * @returns The predefined UserModel object, look at UserFactory.ts + * for exact details + */ + const fakeUser = new UserModel(); + fakeUser.id = 'c6f0a14a-48ae-4b1c-bd6f-5f3b7e8c2b99'; + fakeUser.givenName = 'Tony'; + fakeUser.familyName = 'Matchev'; + fakeUser.username = 'tmatchev'; + fakeUser.netid = 'tkm21'; + fakeUser.admin = false; + fakeUser.photoUrl = 'https://media-exp1.licdn.com/dms/image/C5603AQGmvQtdub6nAQ/profile-displayphoto-shrink_400_400/0/1635358826496?e=1668643200&v=beta&t=ncqjrFUqgqipctcmaSwPzSPrkj0RIQHiCINup_55NNs'; + fakeUser.email = fakeUser.netid + '@cornell.edu'; + fakeUser.googleId = 'tonyGoogleID'; + fakeUser.venmoHandle = "@Tony-Matchev"; + + return fakeUser; + } + public static fake(): UserModel { /** * Returns a UserModel with random values in its instance variables diff --git a/src/tests/data/UserReviewFactory.ts b/src/tests/data/UserReviewFactory.ts new file mode 100644 index 0000000..b7f7340 --- /dev/null +++ b/src/tests/data/UserReviewFactory.ts @@ -0,0 +1,47 @@ +import * as faker from 'faker'; + +import { UserReviewModel } from '../../models/UserReviewModel'; +import { FactoryUtils} from './FactoryUtils'; +export class UserReviewFactory { + public static create(n: number): UserReviewModel[] { + /** + * Returns a list of n number of random UserReviewModel objects + * + * @param n The number of desired random UserReviewModel objects + * @returns The list of n number of random UserReviewModel objects + */ + return FactoryUtils.create(n, UserReviewFactory.fake); + } + + public static fakeTemplate(): UserReviewModel { + /** + * Returns a predefined UserReviewModel object. Useful for testing + * specific instance variables since we already know the value of them + * + * @returns The predefined UserReviewModel object, look at UserReviewFactory.ts + * for exact details + */ + const fakeUserReview = new UserReviewModel(); + fakeUserReview.id = '1e900348-df68-42b3-a8c9-270205575314'; + fakeUserReview.fulfilled = false; + fakeUserReview.stars = 4; + fakeUserReview.comments = 'Seller arrived late, but very friendly!'; + + return fakeUserReview; + } + + public static fake(): UserReviewModel { + /** + * Returns a UserReviewModel with random values in its instance variables + * + * @returns The UserReviewModel object with random values in its instance variables + */ + const fakeUserReview = new UserReviewModel(); + fakeUserReview.id = faker.datatype.uuid(); + fakeUserReview.fulfilled = false; + fakeUserReview.stars = faker.datatype.number({ 'min': 0, 'max': 5 }); + fakeUserReview.comments = faker.vehicle.bicycle(); + + return fakeUserReview; + } +} \ No newline at end of file diff --git a/src/tests/data/index.ts b/src/tests/data/index.ts index 926a29a..b8bb852 100644 --- a/src/tests/data/index.ts +++ b/src/tests/data/index.ts @@ -4,4 +4,5 @@ export * from './FactoryUtils'; export * from './UserFactory'; export * from './UserSessionFactory'; export * from './PostFactory'; -export * from './RequestFactory'; \ No newline at end of file +export * from './RequestFactory'; +export * from './UserReviewFactory'; \ No newline at end of file diff --git a/src/types/ApiResponses.ts b/src/types/ApiResponses.ts index 598108c..0f3b297 100644 --- a/src/types/ApiResponses.ts +++ b/src/types/ApiResponses.ts @@ -20,6 +20,8 @@ export interface PublicProfile { netid: string, givenName: string, familyName: string, + stars: number, + numReviews: number, photoUrl: string, venmoHandle: string, bio: string, From dc8ffc18ecb6ec643fe22b047cfce92868f3b8f9 Mon Sep 17 00:00:00 2001 From: Anton Matchev Date: Wed, 6 Sep 2023 19:56:33 -0400 Subject: [PATCH 5/6] User reviews done --- src/api/controllers/PostController.ts | 2 +- src/migrations/0000_AddAlteredPrice.ts | 3 ++- src/repositories/UserReviewRepository.ts | 3 ++- src/tests/PostTest.test.ts | 15 ++++++++++++++- src/tests/UserReviewTest.test.ts | 18 +++++++++--------- src/tests/UserSessionTest.test.ts | 8 +++++--- src/tests/UserTest.test.ts | 16 ++++++++++++---- src/tests/data/PostFactory.ts | 2 ++ 8 files changed, 47 insertions(+), 20 deletions(-) diff --git a/src/api/controllers/PostController.ts b/src/api/controllers/PostController.ts index a0f2384..21707de 100644 --- a/src/api/controllers/PostController.ts +++ b/src/api/controllers/PostController.ts @@ -99,7 +99,7 @@ export class PostController { } @Post('edit/postID/:id/') - async editPrice(@Body() editPriceRequest : EditPostPriceRequest, @CurrentUser() user: UserModel, @Params() params: UuidParam): Promise { + async editPrice(@Body() editPriceRequest: EditPostPriceRequest, @CurrentUser() user: UserModel, @Params() params: UuidParam): Promise { return { new_price: await (await this.postService.editPostPrice(user, params, editPriceRequest)).altered_price }; } } \ No newline at end of file diff --git a/src/migrations/0000_AddAlteredPrice.ts b/src/migrations/0000_AddAlteredPrice.ts index 611f42c..ad2dc9d 100644 --- a/src/migrations/0000_AddAlteredPrice.ts +++ b/src/migrations/0000_AddAlteredPrice.ts @@ -14,7 +14,8 @@ export class AddAlteredPrice1681680434289 implements MigrationInterface { name: "altered_price", type: "numeric", scale: 2, - default: 0, + // originally was 0 + default: -1, }) ); } diff --git a/src/repositories/UserReviewRepository.ts b/src/repositories/UserReviewRepository.ts index c1be420..e186faf 100644 --- a/src/repositories/UserReviewRepository.ts +++ b/src/repositories/UserReviewRepository.ts @@ -17,10 +17,11 @@ export class UserReviewRepository extends AbstractRepository { return await this.repository .createQueryBuilder("review") .leftJoinAndSelect("review.buyer", "user") + .leftJoinAndSelect("review.seller", "user2") .where("review.id = :id", { id }) .getOne(); } - + public async createUserReview( fulfilled: boolean, stars: number, diff --git a/src/tests/PostTest.test.ts b/src/tests/PostTest.test.ts index 3c4a19f..a909f6c 100644 --- a/src/tests/PostTest.test.ts +++ b/src/tests/PostTest.test.ts @@ -90,6 +90,7 @@ describe('post tests', () => { const getPostResponse = await postController.getPostById(uuidParam); getPostResponse.post.original_price = Number(getPostResponse.post.original_price); + getPostResponse.post.altered_price = Number(getPostResponse.post.altered_price); expectedPost.created = getPostResponse.post.created; expect(getPostResponse.post).toEqual(expectedPost); }); @@ -107,11 +108,12 @@ describe('post tests', () => { const getPostsResponse = await postController.getPostsByUserId(uuidParam); getPostsResponse.posts[0].original_price = Number(getPostsResponse.posts[0].original_price); + getPostsResponse.posts[0].altered_price = Number(getPostsResponse.posts[0].altered_price); expectedPost.created = getPostsResponse.posts[0].created; expect(getPostsResponse.posts).toEqual([expectedPost]); }); - + test('create post', async () => { const user = UserFactory.fakeTemplate(); @@ -196,6 +198,7 @@ describe('post tests', () => { const getPostsResponse = await postController.searchPosts(search); getPostsResponse.posts[0].original_price = Number(getPostsResponse.posts[0].original_price); + getPostsResponse.posts[0].altered_price = Number(getPostsResponse.posts[0].altered_price); expectedPost.created = getPostsResponse.posts[0].created; expect(getPostsResponse.posts).toEqual([expectedPost]); @@ -218,6 +221,7 @@ describe('post tests', () => { const getPostsResponse = await postController.searchPosts(search); getPostsResponse.posts[0].original_price = Number(getPostsResponse.posts[0].original_price); + getPostsResponse.posts[0].altered_price = Number(getPostsResponse.posts[0].altered_price); expectedPost.created = getPostsResponse.posts[0].created; expect(getPostsResponse.posts).toEqual([expectedPost]); @@ -240,6 +244,7 @@ describe('post tests', () => { const getPostsResponse = await postController.searchPosts(search); getPostsResponse.posts[0].original_price = Number(getPostsResponse.posts[0].original_price); + getPostsResponse.posts[0].altered_price = Number(getPostsResponse.posts[0].altered_price); expectedPost.created = getPostsResponse.posts[0].created; expect(getPostsResponse.posts).toEqual([expectedPost]); @@ -282,6 +287,7 @@ describe('post tests', () => { let getPostsResponse = await postController.filterPosts(filter); getPostsResponse.posts[0].original_price = Number(getPostsResponse.posts[0].original_price); + getPostsResponse.posts[0].altered_price = Number(getPostsResponse.posts[0].altered_price); expectedPost.created = getPostsResponse.posts[0].created; expect(getPostsResponse.posts).toEqual([expectedPost]); @@ -292,6 +298,7 @@ describe('post tests', () => { getPostsResponse = await postController.filterPosts(filter); getPostsResponse.posts[0].original_price = Number(getPostsResponse.posts[0].original_price); + getPostsResponse.posts[0].altered_price = Number(getPostsResponse.posts[0].altered_price); expectedPost.created = getPostsResponse.posts[0].created; expect(getPostsResponse.posts).toEqual([expectedPost]); @@ -323,6 +330,7 @@ describe('post tests', () => { let getPostsResponse = await postController.filterPostsByPrice(filter); getPostsResponse.posts[0].original_price = Number(getPostsResponse.posts[0].original_price); + getPostsResponse.posts[0].altered_price = Number(getPostsResponse.posts[0].altered_price); expectedPost.created = getPostsResponse.posts[0].created; expect(getPostsResponse.posts).toEqual([expectedPost]); @@ -346,6 +354,7 @@ describe('post tests', () => { let getPostsResponse = await postController.filterPostsByPrice(filter); getPostsResponse.posts[0].original_price = Number(getPostsResponse.posts[0].original_price); + getPostsResponse.posts[0].altered_price = Number(getPostsResponse.posts[0].altered_price); expectedPost.created = getPostsResponse.posts[0].created; expect(getPostsResponse.posts).toEqual([expectedPost]); @@ -368,6 +377,7 @@ describe('post tests', () => { let getPostsResponse = await postController.filterPostsByPrice(filter); getPostsResponse.posts[0].original_price = Number(getPostsResponse.posts[0].original_price); + getPostsResponse.posts[0].altered_price = Number(getPostsResponse.posts[0].altered_price); expectedPost.created = getPostsResponse.posts[0].created; expect(getPostsResponse.posts).toEqual([expectedPost]); @@ -411,6 +421,7 @@ describe('post tests', () => { postsResponse = await postController.getSavedPostsByUserId(user); expect(postsResponse).not.toBeUndefined(); postsResponse.posts[0].original_price = Number(postsResponse.posts[0].original_price); + postsResponse.posts[0].altered_price = Number(postsResponse.posts[0].altered_price); expectedPost.created = postsResponse.posts[0].created; expect(postsResponse.posts).toEqual([expectedPost]); @@ -475,6 +486,7 @@ describe('post tests', () => { const getPostsResponse = await postController.getArchivedPostsByUserId(uuidParam); getPostsResponse.posts[0].original_price = Number(getPostsResponse.posts[0].original_price); + getPostsResponse.posts[0].altered_price = Number(getPostsResponse.posts[0].altered_price); expectedPost.created = getPostsResponse.posts[0].created; expect(getPostsResponse.posts).toEqual([expectedPost]); @@ -512,6 +524,7 @@ describe('post tests', () => { const getPostResponse = await postController.archivePost(post.user, uuidParam); getPostResponse.post.original_price = Number(getPostResponse.post.original_price); + getPostResponse.post.altered_price = Number(getPostResponse.post.altered_price); expectedPost.created = getPostResponse.post.created; expect(getPostResponse.post).toEqual(expectedPost); diff --git a/src/tests/UserReviewTest.test.ts b/src/tests/UserReviewTest.test.ts index 7236e74..32b3a3d 100644 --- a/src/tests/UserReviewTest.test.ts +++ b/src/tests/UserReviewTest.test.ts @@ -12,7 +12,7 @@ let expectedUserReview: UserReviewModel; let conn: Connection; let userReviewController: UserReviewController; -beforeAll(async() => { +beforeAll(async () => { await DatabaseConnection.connect(); }); @@ -48,9 +48,9 @@ describe('user review tests', () => { userReview.buyer = UserFactory.fake(); userReview.seller = UserFactory.fake(); await new DataFactory() - .createUserReviews(userReview) - .createUsers(userReview.buyer, userReview.seller) - .write(); + .createUserReviews(userReview) + .createUsers(userReview.buyer, userReview.seller) + .write(); const getUserReviewsResponse = await userReviewController.getUserReviews(); @@ -63,9 +63,9 @@ describe('user review tests', () => { userReview.seller = UserFactory.fakeTemplate2(); await new DataFactory() - .createUserReviews(userReview) - .createUsers(userReview.buyer, userReview.seller) - .write(); + .createUserReviews(userReview) + .createUsers(userReview.buyer, userReview.seller) + .write(); expectedUserReview.buyer = userReview.buyer; expectedUserReview.seller = userReview.seller; @@ -81,8 +81,8 @@ describe('user review tests', () => { const seller = UserFactory.fakeTemplate2(); await new DataFactory() - .createUsers(buyer, seller) - .write(); + .createUsers(buyer, seller) + .write(); const newUserReview = { fulfilled: false, diff --git a/src/tests/UserSessionTest.test.ts b/src/tests/UserSessionTest.test.ts index 8fd9dd7..dd42ec2 100644 --- a/src/tests/UserSessionTest.test.ts +++ b/src/tests/UserSessionTest.test.ts @@ -71,7 +71,9 @@ describe('user session tests', () => { .write(); const getUsersResponse = await authController.deleteUserById(uuidParam); - + if (getUsersResponse.user != undefined) { + getUsersResponse.user.stars = Number(getUsersResponse.user.stars); + } expect(getUsersResponse.user).toEqual(expectedUser); }); @@ -105,7 +107,7 @@ describe('user session tests', () => { .createUsers(user) .createUserSessions(session) .write(); - + const refreshToken = session.refreshToken; const accessToken = session.accessToken; @@ -128,7 +130,7 @@ describe('user session tests', () => { .createUsers(user) .createUserSessions(session) .write(); - + const refreshToken = session.refreshToken; const accessToken = session.accessToken; diff --git a/src/tests/UserTest.test.ts b/src/tests/UserTest.test.ts index 870878c..35bc946 100644 --- a/src/tests/UserTest.test.ts +++ b/src/tests/UserTest.test.ts @@ -87,7 +87,9 @@ describe('user tests', () => { .write(); const getUserResponse = await userController.getUserById(uuidParam); - + if (getUserResponse.user != undefined) { + getUserResponse.user.stars = Number(getUserResponse.user.stars); + } expect(getUserResponse.user).toEqual(expectedUser); }); @@ -99,7 +101,9 @@ describe('user tests', () => { .write(); const getUserResponse = await userController.getUserByEmail({ email: 'sn999@cornell.edu' }); - + if (getUserResponse.user != undefined) { + getUserResponse.user.stars = Number(getUserResponse.user.stars); + } expect(getUserResponse.user).toEqual(expectedUser); }); @@ -111,7 +115,9 @@ describe('user tests', () => { .write(); const getUserResponse = await userController.getUserByGoogleId('shungoGoogleID'); - + if (getUserResponse.user != undefined) { + getUserResponse.user.stars = Number(getUserResponse.user.stars); + } expect(getUserResponse.user).toEqual(expectedUser); }); @@ -133,7 +139,9 @@ describe('user tests', () => { expectedUser.venmoHandle = "@Shungo-Najima1"; const getUserResponse = await userController.getUserByGoogleId('shungoGoogleID'); - + if (getUserResponse.user != undefined) { + getUserResponse.user.stars = Number(getUserResponse.user.stars); + } expect(getUserResponse.user).toEqual(expectedUser); }); diff --git a/src/tests/data/PostFactory.ts b/src/tests/data/PostFactory.ts index 95bbf00..092d140 100644 --- a/src/tests/data/PostFactory.ts +++ b/src/tests/data/PostFactory.ts @@ -29,6 +29,7 @@ export class PostFactory { fakePost.archive = false; fakePost.categories = ['HANDMADE', 'OTHER']; fakePost.original_price = 500.15; + fakePost.altered_price = -1; fakePost.images = ['https://upload.wikimedia.org/wikipedia/commons/thumb/4/48/Kombucha_Mature.jpg/640px-Kombucha_Mature.jpg', 'https://images.heb.com/is/image/HEBGrocery/001017916']; fakePost.location = 'The Dorm Hotel'; @@ -48,6 +49,7 @@ export class PostFactory { fakePost.archive = false; fakePost.categories = ['SPORTS & OUTDOORS']; fakePost.original_price = Number(faker.commerce.price(100, 400)); + fakePost.altered_price = -1; fakePost.images = [faker.internet.url()]; fakePost.location = faker.address.city(); From ab9db56b7684c3f16cf4f8a4e03bc8ab3385244d Mon Sep 17 00:00:00 2001 From: Anton Matchev Date: Tue, 19 Sep 2023 20:07:40 -0400 Subject: [PATCH 6/6] Addressed Mateo's comment of filterByPrice --- src/repositories/PostRepository.ts | 6 +++--- src/services/UserService.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/repositories/PostRepository.ts b/src/repositories/PostRepository.ts index 1dc33b8..c43adc9 100644 --- a/src/repositories/PostRepository.ts +++ b/src/repositories/PostRepository.ts @@ -95,8 +95,8 @@ export class PostRepository extends AbstractRepository { return await this.repository .createQueryBuilder("post") .leftJoinAndSelect("post.user", "user") - .where("post.original_price >= :lowerBound", {lowerBound: lowerBound}) - .andWhere("post.original_price <= :upperBound", {upperBound: upperBound}) + .where("CASE WHEN post.altered_price = -1 THEN post.original_price ELSE post.altered_price END >= :lowerBound", { lowerBound: lowerBound }) + .andWhere("CASE WHEN post.altered_price = -1 THEN post.original_price ELSE post.altered_price END <= :upperBound", { upperBound: upperBound }) .andWhere("post.archive = false") .getMany(); } @@ -123,7 +123,7 @@ export class PostRepository extends AbstractRepository { return await this.repository.save(post) } - public async editPostPrice(post: PostModel, new_price: number) : Promise { + public async editPostPrice(post: PostModel, new_price: number): Promise { post.altered_price = new_price return await this.repository.save(post) } diff --git a/src/services/UserService.ts b/src/services/UserService.ts index a8f9044..36150da 100644 --- a/src/services/UserService.ts +++ b/src/services/UserService.ts @@ -18,7 +18,7 @@ export class UserService { } public async getAllUsers(user: UserModel): Promise { - // if (!user.admin) throw new UnauthorizedError('User does not have permission to get all users') + if (!user.admin) throw new UnauthorizedError('User does not have permission to get all users') return this.transactions.readOnly(async (transactionalEntityManager) => { const userRepository = Repositories.user(transactionalEntityManager); return userRepository.getAllUsers();