From f927794d235f95a6c196b24dcbcbb8396a514de6 Mon Sep 17 00:00:00 2001 From: Max Weng Date: Sat, 5 Oct 2024 13:08:03 -0700 Subject: [PATCH 01/12] add migration --- migrations/0046-add-onboarding-task.ts | 67 ++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 migrations/0046-add-onboarding-task.ts diff --git a/migrations/0046-add-onboarding-task.ts b/migrations/0046-add-onboarding-task.ts new file mode 100644 index 00000000..85542f9f --- /dev/null +++ b/migrations/0046-add-onboarding-task.ts @@ -0,0 +1,67 @@ +import {MigrationInterface, QueryRunner, TableColumn} from "typeorm"; + +const TABLE_NAME = 'Users'; + +export class addOnboardingTask1727933494169 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.addColumns(TABLE_NAME, [ + new TableColumn({ + name: 'onboardingComplete', + type: 'boolean', + default: false, + isNullable: false, + }), + new TableColumn({ + name: 'resumeUpload', + type: 'boolean', + default: false, + isNullable: false, + }), + new TableColumn({ + name: 'profilePicture', + type: 'boolean', + default: false, + isNullable: false, + }), + new TableColumn({ + name: 'addedBio', + type: 'boolean', + default: false, + isNullable: false, + }), + new TableColumn({ + name: 'eventsAttended', + type: 'int', + default: 0, + isNullable: false, + }), + ]); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropColumns(TABLE_NAME, [ + new TableColumn({ + name: 'onboardingComplete', + type: 'boolean', + }), + new TableColumn({ + name: 'resumeUpload', + type: 'boolean', + }), + new TableColumn({ + name: 'profilePicture', + type: 'boolean', + }), + new TableColumn({ + name: 'addedBio', + type: 'boolean', + }), + new TableColumn({ + name: 'eventsAttended', + type: 'int', + }), + ]); + } + +} From 94b636a5d03a69a6da6c00a37884ea494f6c30ad Mon Sep 17 00:00:00 2001 From: Max Weng Date: Mon, 7 Oct 2024 18:19:25 -0700 Subject: [PATCH 02/12] migration and schema changes --- migrations/0046-add-onboarding-task.ts | 86 +++++++------------------- models/UserModel.ts | 8 +++ services/UserAccountService.ts | 6 +- types/ApiResponses.ts | 2 + 4 files changed, 39 insertions(+), 63 deletions(-) diff --git a/migrations/0046-add-onboarding-task.ts b/migrations/0046-add-onboarding-task.ts index 85542f9f..bd1b76df 100644 --- a/migrations/0046-add-onboarding-task.ts +++ b/migrations/0046-add-onboarding-task.ts @@ -1,67 +1,29 @@ -import {MigrationInterface, QueryRunner, TableColumn} from "typeorm"; +import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm'; const TABLE_NAME = 'Users'; -export class addOnboardingTask1727933494169 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.addColumns(TABLE_NAME, [ - new TableColumn({ - name: 'onboardingComplete', - type: 'boolean', - default: false, - isNullable: false, - }), - new TableColumn({ - name: 'resumeUpload', - type: 'boolean', - default: false, - isNullable: false, - }), - new TableColumn({ - name: 'profilePicture', - type: 'boolean', - default: false, - isNullable: false, - }), - new TableColumn({ - name: 'addedBio', - type: 'boolean', - default: false, - isNullable: false, - }), - new TableColumn({ - name: 'eventsAttended', - type: 'int', - default: 0, - isNullable: false, - }), - ]); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropColumns(TABLE_NAME, [ - new TableColumn({ - name: 'onboardingComplete', - type: 'boolean', - }), - new TableColumn({ - name: 'resumeUpload', - type: 'boolean', - }), - new TableColumn({ - name: 'profilePicture', - type: 'boolean', - }), - new TableColumn({ - name: 'addedBio', - type: 'boolean', - }), - new TableColumn({ - name: 'eventsAttended', - type: 'int', - }), - ]); - } +export class AddOnboardingTask1727933494169 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.addColumn( + TABLE_NAME, + new TableColumn({ + name: 'onboardingComplete', + type: 'boolean', + default: false, + isNullable: false, + }), + ); + } + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropColumn( + TABLE_NAME, + new TableColumn({ + name: 'onboardingComplete', + type: 'boolean', + default: false, + isNullable: false, + }), + ); + } } diff --git a/models/UserModel.ts b/models/UserModel.ts index 677ec3f1..c342eb9e 100644 --- a/models/UserModel.ts +++ b/models/UserModel.ts @@ -65,6 +65,9 @@ export class UserModel extends BaseEntity { @Column('integer', { default: 0 }) credits: number; + @Column('boolean', { default: false }) + onboardingCompleted: boolean; + @OneToMany((type) => ActivityModel, (activity) => activity.user, { cascade: true }) activities: ActivityModel[]; @@ -154,10 +157,15 @@ export class UserModel extends BaseEntity { points: this.points, credits: this.credits, isAttendancePublic: this.isAttendancePublic, + attendanceCount: this.attendances.length, + onboardingCompleted: this.onboardingCompleted, }; if (this.userSocialMedia) { fullUserProfile.userSocialMedia = this.userSocialMedia.map((sm) => sm.getPublicSocialMedia()); } + if (this.resumes) { + fullUserProfile.resumes = this.resumes.map((rm) => rm.getPublicResume()); + } return fullUserProfile; } } diff --git a/services/UserAccountService.ts b/services/UserAccountService.ts index ab0dc926..1384aa13 100644 --- a/services/UserAccountService.ts +++ b/services/UserAccountService.ts @@ -188,6 +188,10 @@ export default class UserAccountService { .getAllNamesAndEmails()); } + public async checkOnboarding(user: UserModel) : Promise { + + } + /** * UserAccountService::getFullUserProfile() differs from UserModel::getFullUserProfile() in that it also returns any * user data that needs to be joined from other tables (e.g. resumes and social media URLs) @@ -269,7 +273,7 @@ export default class UserAccountService { .activity(txn) .logActivity(activity); - return updatedUser; + return updatedUser.getFullUserProfile(); })); return updatedUsers; diff --git a/types/ApiResponses.ts b/types/ApiResponses.ts index d3dfa353..aa45be51 100644 --- a/types/ApiResponses.ts +++ b/types/ApiResponses.ts @@ -358,6 +358,8 @@ export interface PrivateProfile extends PublicProfile { state: string, credits: number, resumes?: PublicResume[], + attendanceCount: number, + onboardingCompleted: boolean, } export interface PublicFeedback { From cf1df538ef4d12b813e85b05db63b35b7bf109ec Mon Sep 17 00:00:00 2001 From: Max Weng <73797155+maxwn04@users.noreply.github.com> Date: Wed, 9 Oct 2024 13:29:57 -0700 Subject: [PATCH 03/12] change migration and schema --- migrations/0046-add-onboarding-task.ts | 28 +++++++++++++++++--------- models/UserModel.ts | 8 ++++++-- types/ApiResponses.ts | 3 ++- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/migrations/0046-add-onboarding-task.ts b/migrations/0046-add-onboarding-task.ts index bd1b76df..c13e8741 100644 --- a/migrations/0046-add-onboarding-task.ts +++ b/migrations/0046-add-onboarding-task.ts @@ -4,26 +4,36 @@ const TABLE_NAME = 'Users'; export class AddOnboardingTask1727933494169 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { - await queryRunner.addColumn( - TABLE_NAME, + await queryRunner.addColumns(TABLE_NAME, [ new TableColumn({ - name: 'onboardingComplete', + name: 'onboardingSeen', type: 'boolean', + isNullable: true, default: false, - isNullable: false, }), - ); + new TableColumn({ + name: 'onboardingCollected', + type: 'boolean', + isNullable: true, + default: true, + }), + ]); } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropColumn( - TABLE_NAME, + await queryRunner.dropColumns(TABLE_NAME, [ + new TableColumn({ + name: 'onboardingSeen', + type: 'boolean', + isNullable: true, + default: false, + }), new TableColumn({ - name: 'onboardingComplete', + name: 'onboardingRewarded', type: 'boolean', default: false, isNullable: false, }), - ); + ]); } } diff --git a/models/UserModel.ts b/models/UserModel.ts index c342eb9e..17dd57db 100644 --- a/models/UserModel.ts +++ b/models/UserModel.ts @@ -66,7 +66,10 @@ export class UserModel extends BaseEntity { credits: number; @Column('boolean', { default: false }) - onboardingCompleted: boolean; + onboardingSeen: boolean; + + @Column('boolean', { default: false }) + onboardingRewarded: boolean; @OneToMany((type) => ActivityModel, (activity) => activity.user, { cascade: true }) activities: ActivityModel[]; @@ -158,7 +161,8 @@ export class UserModel extends BaseEntity { credits: this.credits, isAttendancePublic: this.isAttendancePublic, attendanceCount: this.attendances.length, - onboardingCompleted: this.onboardingCompleted, + onboardingSeen: this.onboardingSeen, + onboardingRewarded: this.onboardingRewarded, }; if (this.userSocialMedia) { fullUserProfile.userSocialMedia = this.userSocialMedia.map((sm) => sm.getPublicSocialMedia()); diff --git a/types/ApiResponses.ts b/types/ApiResponses.ts index aa45be51..f17febfe 100644 --- a/types/ApiResponses.ts +++ b/types/ApiResponses.ts @@ -359,7 +359,8 @@ export interface PrivateProfile extends PublicProfile { credits: number, resumes?: PublicResume[], attendanceCount: number, - onboardingCompleted: boolean, + onboardingSeen: boolean, + onboardingRewarded: boolean, } export interface PublicFeedback { From a7e53ebfe6e52afdf0b150456f41bd81c6f41b3a Mon Sep 17 00:00:00 2001 From: Max Weng Date: Sat, 12 Oct 2024 17:33:33 -0700 Subject: [PATCH 04/12] last change on migration and fix profile --- migrations/0046-add-onboarding-task.ts | 4 ++-- models/UserModel.ts | 8 +++++--- services/UserAccountService.ts | 1 + types/ApiResponses.ts | 4 ++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/migrations/0046-add-onboarding-task.ts b/migrations/0046-add-onboarding-task.ts index c13e8741..46b5e317 100644 --- a/migrations/0046-add-onboarding-task.ts +++ b/migrations/0046-add-onboarding-task.ts @@ -15,7 +15,7 @@ export class AddOnboardingTask1727933494169 implements MigrationInterface { name: 'onboardingCollected', type: 'boolean', isNullable: true, - default: true, + default: false, }), ]); } @@ -25,7 +25,7 @@ export class AddOnboardingTask1727933494169 implements MigrationInterface { new TableColumn({ name: 'onboardingSeen', type: 'boolean', - isNullable: true, + isNullable: false, default: false, }), new TableColumn({ diff --git a/models/UserModel.ts b/models/UserModel.ts index 17dd57db..8796c349 100644 --- a/models/UserModel.ts +++ b/models/UserModel.ts @@ -69,7 +69,7 @@ export class UserModel extends BaseEntity { onboardingSeen: boolean; @Column('boolean', { default: false }) - onboardingRewarded: boolean; + onboardingCollected: boolean; @OneToMany((type) => ActivityModel, (activity) => activity.user, { cascade: true }) activities: ActivityModel[]; @@ -160,10 +160,12 @@ export class UserModel extends BaseEntity { points: this.points, credits: this.credits, isAttendancePublic: this.isAttendancePublic, - attendanceCount: this.attendances.length, onboardingSeen: this.onboardingSeen, - onboardingRewarded: this.onboardingRewarded, + onboardingCollected: this.onboardingCollected, }; + if (this.attendances) { + fullUserProfile.attendanceCount = this.attendances.length; + } if (this.userSocialMedia) { fullUserProfile.userSocialMedia = this.userSocialMedia.map((sm) => sm.getPublicSocialMedia()); } diff --git a/services/UserAccountService.ts b/services/UserAccountService.ts index 1384aa13..92d814f4 100644 --- a/services/UserAccountService.ts +++ b/services/UserAccountService.ts @@ -201,6 +201,7 @@ export default class UserAccountService { const userProfile = user.getFullUserProfile(); userProfile.resumes = await Repositories.resume(txn).findAllByUser(user); userProfile.userSocialMedia = await Repositories.userSocialMedia(txn).getSocialMediaForUser(user); + userProfile.attendanceCount = (await Repositories.attendance(txn).getAttendancesForUser(user)).length; return userProfile; }); } diff --git a/types/ApiResponses.ts b/types/ApiResponses.ts index f17febfe..cef331e7 100644 --- a/types/ApiResponses.ts +++ b/types/ApiResponses.ts @@ -358,9 +358,9 @@ export interface PrivateProfile extends PublicProfile { state: string, credits: number, resumes?: PublicResume[], - attendanceCount: number, + attendanceCount?: number, onboardingSeen: boolean, - onboardingRewarded: boolean, + onboardingCollected: boolean, } export interface PublicFeedback { From 893feb13c060e16fc6d792240dd4344c0d16ea08 Mon Sep 17 00:00:00 2001 From: Max Weng Date: Sat, 12 Oct 2024 22:24:02 -0700 Subject: [PATCH 05/12] add new routes --- api/controllers/UserController.ts | 6 ++++++ api/validators/UserControllerRequests.ts | 3 +++ services/UserAccountService.ts | 22 ++++++++++++++++++---- types/ApiRequests.ts | 1 + 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/api/controllers/UserController.ts b/api/controllers/UserController.ts index 1ecb88e5..2e2c6106 100644 --- a/api/controllers/UserController.ts +++ b/api/controllers/UserController.ts @@ -96,6 +96,12 @@ export class UserController { return { error: null, user: userProfile }; } + @Patch('/onboarding/collect') + async collectOnboarding(@AuthenticatedUser() user: UserModel): Promise { + const userProfile = await this.userAccountService.collectOnboarding(user); + return { error: null, user: userProfile }; + } + @Patch() async patchCurrentUser(@Body() patchUserRequest: PatchUserRequest, @AuthenticatedUser() user: UserModel): Promise { diff --git a/api/validators/UserControllerRequests.ts b/api/validators/UserControllerRequests.ts index f0b03f9b..f3ba77ce 100644 --- a/api/validators/UserControllerRequests.ts +++ b/api/validators/UserControllerRequests.ts @@ -47,6 +47,9 @@ export class UserPatches implements IUserPatches { @Allow() isAttendancePublic?: boolean; + @Allow() + onboardingSeen?: boolean; + @Type(() => PasswordUpdate) @ValidateNested() @HasMatchingPasswords() diff --git a/services/UserAccountService.ts b/services/UserAccountService.ts index 92d814f4..12e41af6 100644 --- a/services/UserAccountService.ts +++ b/services/UserAccountService.ts @@ -10,6 +10,7 @@ import { englishDataset, englishRecommendedTransformers, } from 'obscenity'; +import { UserError } from 'utils/Errors'; import Repositories, { TransactionsManager } from '../repositories'; import { Uuid, @@ -166,6 +167,23 @@ export default class UserAccountService { }); } + public async collectOnboarding(user: UserModel): Promise { + if (user.attendances.length < 5 + || user.resumes.length < 1 + || user.profilePicture == null + || user.bio == null) { + throw new UserError('Onboarding tasks not completed'); + } + if (user.onboardingCollected) { + throw new UserError('Onboarding reward already collected'); + } + return this.transactions.readWrite(async (txn) => { + const userRepository = Repositories.user(txn); + await userRepository.addPoints(user, 10); + return userRepository.upsertUser(user, { onboardingCollected: true }); + }); + } + public async grantBonusPoints(emails: string[], description: string, points: number) { return this.transactions.readWrite(async (txn) => { const userRepository = Repositories.user(txn); @@ -188,10 +206,6 @@ export default class UserAccountService { .getAllNamesAndEmails()); } - public async checkOnboarding(user: UserModel) : Promise { - - } - /** * UserAccountService::getFullUserProfile() differs from UserModel::getFullUserProfile() in that it also returns any * user data that needs to be joined from other tables (e.g. resumes and social media URLs) diff --git a/types/ApiRequests.ts b/types/ApiRequests.ts index 816b59f7..4af0f22f 100644 --- a/types/ApiRequests.ts +++ b/types/ApiRequests.ts @@ -68,6 +68,7 @@ export interface UserPatches { graduationYear?: number; bio?: string; isAttendancePublic?: boolean; + onboardingSeen?: boolean; passwordChange?: PasswordUpdate; } From dc4b8cf93aa3a42ff0178d3885dc5af8bdab6002 Mon Sep 17 00:00:00 2001 From: Max Weng Date: Sat, 12 Oct 2024 22:28:27 -0700 Subject: [PATCH 06/12] change error --- services/UserAccountService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/UserAccountService.ts b/services/UserAccountService.ts index 12e41af6..429c9f8c 100644 --- a/services/UserAccountService.ts +++ b/services/UserAccountService.ts @@ -172,10 +172,10 @@ export default class UserAccountService { || user.resumes.length < 1 || user.profilePicture == null || user.bio == null) { - throw new UserError('Onboarding tasks not completed'); + throw new BadRequestError('Onboarding tasks not completed'); } if (user.onboardingCollected) { - throw new UserError('Onboarding reward already collected'); + throw new BadRequestError('Onboarding reward already collected'); } return this.transactions.readWrite(async (txn) => { const userRepository = Repositories.user(txn); From c4aa01c813c0e96e69949b3031f6f1b258d6d6ad Mon Sep 17 00:00:00 2001 From: Max Weng Date: Sat, 12 Oct 2024 22:31:21 -0700 Subject: [PATCH 07/12] lint --- services/UserAccountService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/services/UserAccountService.ts b/services/UserAccountService.ts index 429c9f8c..377fa0d4 100644 --- a/services/UserAccountService.ts +++ b/services/UserAccountService.ts @@ -10,7 +10,6 @@ import { englishDataset, englishRecommendedTransformers, } from 'obscenity'; -import { UserError } from 'utils/Errors'; import Repositories, { TransactionsManager } from '../repositories'; import { Uuid, From 0b8fe138a60443a08eb094d6205aeb72fc69cb34 Mon Sep 17 00:00:00 2001 From: Max Weng Date: Sat, 19 Oct 2024 22:48:43 -0700 Subject: [PATCH 08/12] done --- services/UserAccountService.ts | 15 +++--- tests/onboardingReward.test.ts | 87 ++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 7 deletions(-) create mode 100644 tests/onboardingReward.test.ts diff --git a/services/UserAccountService.ts b/services/UserAccountService.ts index 377fa0d4..669ef9ef 100644 --- a/services/UserAccountService.ts +++ b/services/UserAccountService.ts @@ -167,14 +167,15 @@ export default class UserAccountService { } public async collectOnboarding(user: UserModel): Promise { - if (user.attendances.length < 5 - || user.resumes.length < 1 - || user.profilePicture == null - || user.bio == null) { - throw new BadRequestError('Onboarding tasks not completed'); + const userProfile = await this.getFullUserProfile(user); + if (userProfile.attendanceCount < 5 + || userProfile.resumes.length < 1 + || userProfile.profilePicture == null + || userProfile.bio == null) { + throw new BadRequestError('Onboarding tasks not completed!'); } - if (user.onboardingCollected) { - throw new BadRequestError('Onboarding reward already collected'); + if (userProfile.onboardingCollected) { + throw new BadRequestError('Onboarding reward already collected!'); } return this.transactions.readWrite(async (txn) => { const userRepository = Repositories.user(txn); diff --git a/tests/onboardingReward.test.ts b/tests/onboardingReward.test.ts new file mode 100644 index 00000000..1105b5ce --- /dev/null +++ b/tests/onboardingReward.test.ts @@ -0,0 +1,87 @@ +import { EventModel } from '../models/EventModel'; +import { DatabaseConnection, UserFactory, PortalState, EventFactory, ResumeFactory } from './data'; +import { ControllerFactory } from './controllers'; + +beforeAll(async () => { + await DatabaseConnection.connect(); +}); + +beforeEach(async () => { + await DatabaseConnection.clear(); +}); + +afterAll(async () => { + await DatabaseConnection.clear(); + await DatabaseConnection.close(); +}); + +describe('collect onboarding reward', () => { + test('can collect onboarding reward', async () => { + const conn = await DatabaseConnection.get(); + const member = UserFactory.fake({ + bio: 'this is a bio', + profilePicture: 'https://pfp.com', + }); + const resume = ResumeFactory.fake({ user: member, isResumeVisible: true }); + + const events: EventModel[] = []; + for (let i = 0; i < 5; i += 1) { + events.push(EventFactory.fake({ pointValue: 10 })); + } + + await new PortalState() + .createUsers(member) + .createEvents(events[0], events[1], events[2], events[3], events[4]) + .attendEvents([member], events) + .createResumes(member, resume) + .write(); + + const userController = ControllerFactory.user(conn); + const response = await userController.collectOnboarding(member); + const userProfile = response.user; + + expect(userProfile.onboardingCollected).toBe(true); + expect(userProfile.points).toBe(60); + }); + + test('conditions not fulfilled', async () => { + const conn = await DatabaseConnection.get(); + const member = UserFactory.fake(); + + await new PortalState() + .createUsers(member) + .write(); + + const userController = ControllerFactory.user(conn); + + await expect(userController.collectOnboarding(member)) + .rejects.toThrow('Onboarding tasks not completed'); + }); + + test('can collect onboarding reward', async () => { + const conn = await DatabaseConnection.get(); + const member = UserFactory.fake({ + bio: 'this is a bio', + profilePicture: 'https://pfp.com', + onboardingCollected: true, + }); + const resume = ResumeFactory.fake({ user: member, isResumeVisible: true }); + + const events: EventModel[] = []; + for (let i = 0; i < 5; i += 1) { + events.push(EventFactory.fake({ pointValue: 10 })); + } + + await new PortalState() + .createUsers(member) + .createEvents(events[0], events[1], events[2], events[3], events[4]) + .attendEvents([member], events) + .createResumes(member, resume) + .write(); + + const userController = ControllerFactory.user(conn); + + await expect(userController.collectOnboarding(member)) + .rejects.toThrow('Onboarding reward already collected!'); + }); +}); From 08070e09b7ca9fa9d3f82d46f6a9ee15077a8f40 Mon Sep 17 00:00:00 2001 From: Max Weng Date: Wed, 23 Oct 2024 23:34:24 -0700 Subject: [PATCH 09/12] fix table and add activity --- migrations/0046-add-onboarding-task.ts | 4 ++-- services/UserAccountService.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/migrations/0046-add-onboarding-task.ts b/migrations/0046-add-onboarding-task.ts index 46b5e317..29d426b5 100644 --- a/migrations/0046-add-onboarding-task.ts +++ b/migrations/0046-add-onboarding-task.ts @@ -12,7 +12,7 @@ export class AddOnboardingTask1727933494169 implements MigrationInterface { default: false, }), new TableColumn({ - name: 'onboardingCollected', + name: 'firstTasksCompleted', type: 'boolean', isNullable: true, default: false, @@ -29,7 +29,7 @@ export class AddOnboardingTask1727933494169 implements MigrationInterface { default: false, }), new TableColumn({ - name: 'onboardingRewarded', + name: 'firstTasksCompleted', type: 'boolean', default: false, isNullable: false, diff --git a/services/UserAccountService.ts b/services/UserAccountService.ts index 669ef9ef..c2c4556e 100644 --- a/services/UserAccountService.ts +++ b/services/UserAccountService.ts @@ -180,6 +180,7 @@ export default class UserAccountService { return this.transactions.readWrite(async (txn) => { const userRepository = Repositories.user(txn); await userRepository.addPoints(user, 10); + Repositories.activity(txn).logBonus([user], 'First tasks reward', 10); return userRepository.upsertUser(user, { onboardingCollected: true }); }); } From 94474ae552ea925f3640e959f37aa5adf27ecc42 Mon Sep 17 00:00:00 2001 From: Max Weng Date: Wed, 23 Oct 2024 23:37:41 -0700 Subject: [PATCH 10/12] remove return profile --- services/UserAccountService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/UserAccountService.ts b/services/UserAccountService.ts index c2c4556e..4719b8f3 100644 --- a/services/UserAccountService.ts +++ b/services/UserAccountService.ts @@ -289,7 +289,7 @@ export default class UserAccountService { .activity(txn) .logActivity(activity); - return updatedUser.getFullUserProfile(); + return updatedUser; })); return updatedUsers; From b9acadeaf575000461821af6ae674d2271870456 Mon Sep 17 00:00:00 2001 From: Max Weng Date: Wed, 23 Oct 2024 23:39:49 -0700 Subject: [PATCH 11/12] fix --- models/UserModel.ts | 4 ++-- services/UserAccountService.ts | 4 ++-- types/ApiResponses.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/models/UserModel.ts b/models/UserModel.ts index 8796c349..d2194a8d 100644 --- a/models/UserModel.ts +++ b/models/UserModel.ts @@ -69,7 +69,7 @@ export class UserModel extends BaseEntity { onboardingSeen: boolean; @Column('boolean', { default: false }) - onboardingCollected: boolean; + firstTasksCompleted: boolean; @OneToMany((type) => ActivityModel, (activity) => activity.user, { cascade: true }) activities: ActivityModel[]; @@ -161,7 +161,7 @@ export class UserModel extends BaseEntity { credits: this.credits, isAttendancePublic: this.isAttendancePublic, onboardingSeen: this.onboardingSeen, - onboardingCollected: this.onboardingCollected, + firstTasksCompleted: this.firstTasksCompleted, }; if (this.attendances) { fullUserProfile.attendanceCount = this.attendances.length; diff --git a/services/UserAccountService.ts b/services/UserAccountService.ts index 4719b8f3..e738787c 100644 --- a/services/UserAccountService.ts +++ b/services/UserAccountService.ts @@ -174,14 +174,14 @@ export default class UserAccountService { || userProfile.bio == null) { throw new BadRequestError('Onboarding tasks not completed!'); } - if (userProfile.onboardingCollected) { + if (userProfile.firstTasksCompleted) { throw new BadRequestError('Onboarding reward already collected!'); } return this.transactions.readWrite(async (txn) => { const userRepository = Repositories.user(txn); await userRepository.addPoints(user, 10); Repositories.activity(txn).logBonus([user], 'First tasks reward', 10); - return userRepository.upsertUser(user, { onboardingCollected: true }); + return userRepository.upsertUser(user, { firstTasksCompleted: true }); }); } diff --git a/types/ApiResponses.ts b/types/ApiResponses.ts index cef331e7..aef0277b 100644 --- a/types/ApiResponses.ts +++ b/types/ApiResponses.ts @@ -360,7 +360,7 @@ export interface PrivateProfile extends PublicProfile { resumes?: PublicResume[], attendanceCount?: number, onboardingSeen: boolean, - onboardingCollected: boolean, + firstTasksCompleted: boolean, } export interface PublicFeedback { From de9ffb45073e38bf3fdb2b2f0fb4f02c13dcb2bd Mon Sep 17 00:00:00 2001 From: Max Weng Date: Wed, 23 Oct 2024 23:41:23 -0700 Subject: [PATCH 12/12] fix test --- tests/onboardingReward.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/onboardingReward.test.ts b/tests/onboardingReward.test.ts index 1105b5ce..b1b8ca65 100644 --- a/tests/onboardingReward.test.ts +++ b/tests/onboardingReward.test.ts @@ -40,7 +40,7 @@ describe('collect onboarding reward', () => { const response = await userController.collectOnboarding(member); const userProfile = response.user; - expect(userProfile.onboardingCollected).toBe(true); + expect(userProfile.firstTasksCompleted).toBe(true); expect(userProfile.points).toBe(60); }); @@ -63,7 +63,7 @@ describe('collect onboarding reward', () => { const member = UserFactory.fake({ bio: 'this is a bio', profilePicture: 'https://pfp.com', - onboardingCollected: true, + firstTasksCompleted: true, }); const resume = ResumeFactory.fake({ user: member, isResumeVisible: true });