Skip to content

Commit

Permalink
Refactor leaderboard responses
Browse files Browse the repository at this point in the history
  • Loading branch information
Alputer committed Jan 27, 2024
1 parent 92c7c80 commit d7ca272
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 17 deletions.
17 changes: 13 additions & 4 deletions src/controllers/leaderboard.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { ApiBearerAuth, ApiResponse, ApiTags } from '@nestjs/swagger';
import { AuthGuard } from '../services/guards';
import { IAuthorizedRequest } from '../interfaces';
import { LeaderboardService } from '../services';
import {
GlobalLeaderboardResponseDto,
MyRankResponseDto,
TournamentLeaderboardResponseDto,
} from '../dtos/leaderboard/responses';

@ApiBearerAuth()
@Controller('/api/leaderboard')
Expand All @@ -24,7 +29,7 @@ export class LeaderboardController {
description: 'Internal server error, contact with backend team.',
})
@Get('/global')
public async getGlobalLeaderboard(): Promise<any> {
public async getGlobalLeaderboard(): Promise<GlobalLeaderboardResponseDto[]> {
return await this.leaderboardService.getGlobalLeaderboard();
}

Expand Down Expand Up @@ -64,7 +69,7 @@ export class LeaderboardController {
@Get('/tournament')
public async getTournamentLeaderboard(
@Req() req: IAuthorizedRequest,
): Promise<any> {
): Promise<TournamentLeaderboardResponseDto[]> {
const user = req.user;
return await this.leaderboardService.getTournamentLeaderboard(
user.username,
Expand All @@ -87,8 +92,12 @@ export class LeaderboardController {
@Get('/tournament/my-rank')
public async getMyTournamentRank(
@Req() req: IAuthorizedRequest,
): Promise<number> {
): Promise<MyRankResponseDto> {
const user = req.user;
return await this.leaderboardService.getRankOfUserByUsername(user.username);
return {
rank: await this.leaderboardService.getRankOfUserByUsername(
user.username,
),
};
}
}
11 changes: 11 additions & 0 deletions src/dtos/leaderboard/responses/country-leaderboard-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ApiProperty } from '@nestjs/swagger';
import { Expose } from 'class-transformer';

export class CountryLeaderboardResponseDto {
@ApiProperty()
@Expose()
username: string;
@ApiProperty()
@Expose()
levelNum: string;
}
11 changes: 11 additions & 0 deletions src/dtos/leaderboard/responses/global-leaderboard-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ApiProperty } from '@nestjs/swagger';
import { Expose } from 'class-transformer';

export class GlobalLeaderboardResponseDto {
@ApiProperty()
@Expose()
username: string;
@ApiProperty()
@Expose()
levelNum: string;
}
4 changes: 4 additions & 0 deletions src/dtos/leaderboard/responses/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './global-leaderboard-response.dto';
export * from './country-leaderboard-response.dto';
export * from './tournament-leaderboard-response.dto';
export * from './my-rank-response.dto';
8 changes: 8 additions & 0 deletions src/dtos/leaderboard/responses/my-rank-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ApiProperty } from '@nestjs/swagger';
import { Expose } from 'class-transformer';

export class MyRankResponseDto {
@ApiProperty()
@Expose()
rank: number;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ApiProperty } from '@nestjs/swagger';
import { Expose } from 'class-transformer';

export class TournamentLeaderboardResponseDto {
@ApiProperty()
@Expose()
username: string;
@ApiProperty()
@Expose()
tournamentScore: number;
}
3 changes: 1 addition & 2 deletions src/entities/tournamentGroup.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@ export class TournamentGroup {
return result;
}

public static newInstanceFromDynamoDBObjectWithoutTournamentId(
public static newInstanceFromDynamoDBObjectWithoutTournamentAndGroupId(
data: any,
): TournamentGroup {
const result = new TournamentGroup();
result.groupId = data.groupId.S;
result.username = data.username.S;
result.tournamentScore = data.tournamentScore.N;

Expand Down
7 changes: 4 additions & 3 deletions src/repositories/tournamentGroup.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { TournamentRepository } from './tournament.repository';
import { IsOngoing, SortOption } from '../enums';
import { UserRepository } from './user.repository';
import { v4 as uuidv4 } from 'uuid';
import { TournamentLeaderboardResponseDto } from '../dtos/leaderboard/responses';

@Injectable()
export class TournamentGroupRepository {
Expand Down Expand Up @@ -60,22 +61,22 @@ export class TournamentGroupRepository {
public async getGroupMembersByGroupId(
groupId: string,
sortOption: SortOption,
): Promise<any> {
): Promise<TournamentLeaderboardResponseDto[]> {
const queryCommand = new QueryCommand({
TableName: this.tableName,
KeyConditionExpression: 'groupId = :groupId',
ExpressionAttributeValues: {
':groupId': { S: groupId },
},
ProjectionExpression: 'groupId, username, tournamentScore',
ProjectionExpression: 'username, tournamentScore',
});

try {
const queryResult = await this.client.send(queryCommand);

if (queryResult.Items && queryResult.Items.length > 0) {
const result = queryResult.Items.map((item) =>
TournamentGroup.newInstanceFromDynamoDBObjectWithoutTournamentId(
TournamentGroup.newInstanceFromDynamoDBObjectWithoutTournamentAndGroupId(
item,
),
);
Expand Down
17 changes: 13 additions & 4 deletions src/repositories/user.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
import { ConfigService } from '@nestjs/config';
import { User } from '../entities';
import { TournamentGroupRepository } from '.';
import { GlobalLeaderboardResponseDto, CountryLeaderboardResponseDto } from '../dtos/leaderboard/responses';

@Injectable()
export class UserRepository {
Expand Down Expand Up @@ -141,21 +142,25 @@ export class UserRepository {
}
}

public async getGlobalLeaderboard(): Promise<any> {
public async getGlobalLeaderboard(): Promise<GlobalLeaderboardResponseDto[]> {
const queryCommand = new QueryCommand({
TableName: 'Users',
IndexName: 'LevelGSI',
KeyConditionExpression: 'dummyPartitionKey = :dpk',
ExpressionAttributeValues: {
':dpk': { S: '_' },
},
ProjectionExpression: 'username, levelNum',
Limit: 1000,
ScanIndexForward: false,
});

try {
const response = await this.client.send(queryCommand);
return response.Items;
return response.Items.map((item) => ({
levelNum: item.levelNum.N,
username: item.username.S,
}));
} catch (error) {
console.error('Error in get global leaderboard request:', error);
throw new InternalServerErrorException(
Expand All @@ -164,21 +169,25 @@ export class UserRepository {
}
}

public async getCountryLeaderboard(countryCode: string): Promise<any> {
public async getCountryLeaderboard(countryCode: string): Promise<CountryLeaderboardResponseDto[]> {
const queryCommand = new QueryCommand({
TableName: 'Users',
IndexName: 'CountryCodeGSI',
KeyConditionExpression: 'countryCode = :val',
ExpressionAttributeValues: {
':val': { S: countryCode },
},
ProjectionExpression: 'username, levelNum',
Limit: 1000,
ScanIndexForward: false,
});

try {
const response = await this.client.send(queryCommand);
return response.Items;
return response.Items.map((item) => ({
levelNum: item.levelNum.N,
username: item.username.S,
}));
} catch (error) {
console.error('Error in get global leaderboard request:', error);
throw new InternalServerErrorException(
Expand Down
9 changes: 5 additions & 4 deletions src/services/leaderboard.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { BadRequestException, Injectable } from '@nestjs/common';
import { TournamentGroupRepository, UserRepository } from '../repositories';
import { SortOption } from '../enums';
import { IUser } from '../interfaces';
import { GlobalLeaderboardResponseDto, TournamentLeaderboardResponseDto } from '../dtos/leaderboard/responses';

@Injectable()
export class LeaderboardService {
Expand All @@ -10,7 +11,7 @@ export class LeaderboardService {
public readonly userRepository: UserRepository,
) {}

public async getGlobalLeaderboard() {
public async getGlobalLeaderboard(): Promise<GlobalLeaderboardResponseDto[]>{
const users = await this.userRepository.getGlobalLeaderboard();

return users;
Expand All @@ -22,7 +23,7 @@ export class LeaderboardService {
return users;
}

public async getTournamentLeaderboard(username: string) {
public async getTournamentLeaderboard(username: string): Promise<TournamentLeaderboardResponseDto[]> {
const user = await this.userRepository.findUserByUsername(username);

if (user.currGroupId === '') {
Expand Down Expand Up @@ -58,9 +59,9 @@ export class LeaderboardService {
user.currGroupId,
SortOption.NO_SORT,
);
const userScore = members.findIndex(
const userScore = members[members.findIndex(
(item) => item.username === user.username,
).tournamentScore;
)].tournamentScore;

let rank = 1; // calculate the rank of user in the group
for (let member of members) {
Expand Down

0 comments on commit d7ca272

Please sign in to comment.