diff --git a/src/__tests__/acceptance/user.acceptance.ts b/src/__tests__/acceptance/user.acceptance.ts index 0387aa088..6db7b35e3 100644 --- a/src/__tests__/acceptance/user.acceptance.ts +++ b/src/__tests__/acceptance/user.acceptance.ts @@ -170,6 +170,7 @@ describe('UserApplication', function () { }); delete updatedUser.id; + delete updatedUser.username; await client .patch(`/users/${persistedUser.id}`) @@ -179,17 +180,13 @@ describe('UserApplication', function () { expect(result).to.containEql(updatedUser); }); - it('returns 422 when updating a user username more than once', async () => { + it('returns 422 when updating a username', async () => { const updatedUser: Partial = givenUser({ username: 'abdulhakim', }); delete updatedUser.id; - await client - .patch(`/users/${persistedUser.id}`) - .send(updatedUser) - .expect(204); await client .patch(`/users/${persistedUser.id}`) .send(updatedUser) @@ -200,6 +197,7 @@ describe('UserApplication', function () { const updatedUser: Partial = givenUser(); delete updatedUser.id; + delete updatedUser.username; return client.patch('/users/99999').send(updatedUser).expect(404); }); diff --git a/src/__tests__/helpers/given-instances.ts b/src/__tests__/helpers/given-instances.ts index a6694068e..bd2560db0 100644 --- a/src/__tests__/helpers/given-instances.ts +++ b/src/__tests__/helpers/given-instances.ts @@ -62,6 +62,7 @@ export function givenUser(user?: Partial) { { id: '0x06cc7ed22ebd12ccc28fb9c0d14a5c4420a331d89a5fef48b915e8449ee61859', name: 'Abdul Hakim', + username: 'abdulhakim', }, user, ); @@ -83,6 +84,7 @@ export async function givenMultipleUserInstances( givenUserInstance(userRepository, { id: '0x06cc7ed22ebd12ccc28fb9c0d14a5c4420a331d89a5fef48b915e8449ee61863', name: 'irman', + username: 'irman', }), ]); } diff --git a/src/__tests__/unit/user.controller.unit.ts b/src/__tests__/unit/user.controller.unit.ts index 385be9f7c..8c9bbc935 100644 --- a/src/__tests__/unit/user.controller.unit.ts +++ b/src/__tests__/unit/user.controller.unit.ts @@ -89,6 +89,7 @@ describe('UserControllers', () => { givenUser({ id: '0x06cc7ed22ebd12ccc28fb9c0d14a5c4420a331d89a5fef48b915e8449ee61861', name: 'husni', + username: 'husni', bio: 'Hello, my name is husni!', }), ] as User[]; diff --git a/src/controllers/user.controller.ts b/src/controllers/user.controller.ts index 4a49d945c..2dd8522de 100644 --- a/src/controllers/user.controller.ts +++ b/src/controllers/user.controller.ts @@ -3,7 +3,6 @@ import {Filter, FilterExcludingWhere, repository} from '@loopback/repository'; import { get, getModelSchemaRef, - HttpErrors, param, patch, post, @@ -11,7 +10,6 @@ import { response, } from '@loopback/rest'; import {BcryptHasher} from '../services/authentication/hash.password.service'; -import {ActivityLogType, ReferenceType} from '../enums'; import {DeletedDocument, PaginationInterceptor} from '../interceptors'; import {User} from '../models'; import {UserRepository} from '../repositories'; @@ -120,31 +118,4 @@ export class UserController { ): Promise { await this.userRepository.updateById(id, user); } - - @post('/users/{id}/skip-username') - @response(200, { - description: 'Skip username success', - }) - async skipUsername(@param.path.string('id') id: string): Promise { - const found = await this.userRepository.activityLogs(id).find({ - where: { - type: ActivityLogType.SKIPUSERNAME, - }, - }); - - if (found.length >= 1) { - throw new HttpErrors.UnprocessableEntity( - 'You have already skip updating username', - ); - } - - await this.userRepository.activityLogs(id).create({ - type: ActivityLogType.SKIPUSERNAME, - userId: id, - referenceType: ReferenceType.USER, - referenceId: id, - }); - - return; - } } diff --git a/src/enums/activity-log-type.enum.ts b/src/enums/activity-log-type.enum.ts index 7081b95a0..e68826c68 100644 --- a/src/enums/activity-log-type.enum.ts +++ b/src/enums/activity-log-type.enum.ts @@ -1,9 +1,10 @@ export enum ActivityLogType { CREATEUSERNAME = 'username', + SKIPUSERNAME = 'skip_new_user_username', + NEWUSER = 'new_user', UPLOADPROFILEPICTURE = 'upload_profile_picture', FILLBIO = 'fill_in_bio', UPLOADBANNER = 'upload_banner', - SKIPUSERNAME = 'skip_new_user_username', SENDTIP = 'send_tip', IMPORTPOST = 'import_post', CLAIMSOCIAL = 'claim_social_media', diff --git a/src/interceptors/initial-creation.interceptor.ts b/src/interceptors/initial-creation.interceptor.ts index 1d1bea57c..97decdf02 100644 --- a/src/interceptors/initial-creation.interceptor.ts +++ b/src/interceptors/initial-creation.interceptor.ts @@ -148,56 +148,22 @@ export class InitialCreationInterceptor implements Provider { ): Promise { switch (className) { case ControllerType.USER: { - const newUser = new User(invocationCtx.args[0]); - const user = await this.userRepository.findOne({ - where: { - id: newUser.id, - }, - }); + const {id, username} = invocationCtx.args[0]; + + this.validateUsername(username); + + let user = await this.userRepository.findOne({where: {id}}); if (user) throw new HttpErrors.UnprocessableEntity('User already exist!'); - const flag = true; - const name = newUser.name.substring(0, 22); - const usernameBase = newUser.name - .replace(/[^A-Za-z0-9]/g, ' ') - .replace(/\s+/g, ' ') - .trim() - .split(' ')[0] - .toLowerCase(); - - let username = usernameBase.substring(0, 16); - - while (flag) { - const found = await this.userRepository.findOne({ - where: { - username: username, - }, - }); - - let count = 2; - - if (found) { - let newUsername = usernameBase + this.generateRandomCharacter(); - newUsername = newUsername.substring(0, 16); - - if (newUsername === found.username) { - username = - newUsername.substring(0, 16 - count) + - this.generateRandomCharacter(); - username = username.substring(0, 16); - count++; - } else { - username = newUsername; - } - } else break; - } + user = new User(invocationCtx.args[0]); + + const name = user.name.substring(0, 22); - newUser.name = name; - newUser.username = username; + user.name = name; - invocationCtx.args[0] = newUser; + invocationCtx.args[0] = user; return; } @@ -238,6 +204,13 @@ export class InitialCreationInterceptor implements Provider { await this.friendService.defaultFriend(result.id); await this.currencyService.defaultCurrency(result.id); await this.currencyService.defaultAcalaTips(result.id); // TODO: removed default acala tips + await this.activityLogService.createLog( + ActivityLogType.NEWUSER, + result.id, + result.id, + ReferenceType.USER, + ); + return result; } @@ -332,14 +305,7 @@ export class InitialCreationInterceptor implements Provider { user: Partial, ): Promise { if (user.username) { - this.validateUsername(user.username); - - await this.activityLogService.createLog( - ActivityLogType.CREATEUSERNAME, - userId, - userId, - ReferenceType.USER, - ); + throw new HttpErrors.UnprocessableEntity('Cannot update username'); } if (user.profilePictureURL) { @@ -391,13 +357,6 @@ export class InitialCreationInterceptor implements Provider { throw new HttpErrors.UnprocessableEntity('Cannot added comment anymore'); } - generateRandomCharacter(): string { - const randomCharOne = Math.random().toString(36).substring(2); - const randomCharTwo = Math.random().toString(36).substring(2); - - return '.' + randomCharOne + randomCharTwo; - } - validateUsername(username: string): void { if ( username[username.length - 1] === '.' || diff --git a/src/migrations/0.0.0.migration.ts b/src/migrations/0.0.0.migration.ts index c7fb35e4f..90b7efc37 100644 --- a/src/migrations/0.0.0.migration.ts +++ b/src/migrations/0.0.0.migration.ts @@ -60,12 +60,6 @@ export class MigrationScript000 implements MigrationScript { user.createdAt = new Date().toString(); user.updatedAt = new Date().toString(); - user.username = - user.username ?? - user.name.replace(/\s+/g, '').toLowerCase() + - '.' + - Math.random().toString(36).substr(2, 9); - return this.userRepository.create(user); }), ); diff --git a/src/models/user.model.ts b/src/models/user.model.ts index 80593b779..5df92cb7d 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -85,7 +85,7 @@ export class User extends Entity { @property({ type: 'string', - required: false, + required: true, index: { unique: true, }, @@ -135,7 +135,7 @@ export class User extends Entity { @property({ type: 'string', required: false, - default: DefaultCurrencyType.AUSD, + default: DefaultCurrencyType.MYRIA, }) defaultCurrency?: string; diff --git a/src/services/activity-log.service.ts b/src/services/activity-log.service.ts index 8aacf8c0f..21c3cea5b 100644 --- a/src/services/activity-log.service.ts +++ b/src/services/activity-log.service.ts @@ -1,6 +1,5 @@ import {BindingScope, injectable} from '@loopback/core'; import {repository} from '@loopback/repository'; -import {HttpErrors} from '@loopback/rest'; import {ActivityLogType, ReferenceType} from '../enums'; import {ActivityLog} from '../models'; import {ActivityLogRepository, LeaderBoardRepository} from '../repositories'; @@ -34,15 +33,8 @@ export class ActivityLogService { referenceType, }); - if (count >= 1 && type === ActivityLogType.CREATEUSERNAME) { - throw new HttpErrors.UnprocessableEntity( - 'You can only updated username once', - ); - } - await this.activityLogRepository.create(activityLog); - if (type === ActivityLogType.CREATEUSERNAME) return; if (count === 0) { // count activity const found = await this.leaderboardRepository.findOne({