Skip to content

Commit

Permalink
Merge pull request #32 from game-node-app/dev
Browse files Browse the repository at this point in the history
Changes to adapt for trending reviews use in the web client
  • Loading branch information
Lamarcke authored Mar 2, 2024
2 parents 5d63317 + 308eb17 commit b09ee62
Show file tree
Hide file tree
Showing 38 changed files with 292 additions and 132 deletions.
Binary file modified public/icons/nintendo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion server_swagger.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/achievements/achievements.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ObtainedAchievement } from "./entities/obtained-achievement.entity";
import { AchievementsQueueProcessor } from "./achievements-queue/achievements-queue.processor";
import { BullModule } from "@nestjs/bull";
import { ACHIEVEMENTS_QUEUE_NAME } from "./achievements-queue/achievements-queue.constants";
import { UserLevelModule } from "../user-level/user-level.module";
import { LevelModule } from "../level/level.module";

@Module({
imports: [
Expand All @@ -18,7 +18,7 @@ import { UserLevelModule } from "../user-level/user-level.module";
removeOnFail: false,
},
}),
UserLevelModule,
LevelModule,
],
controllers: [AchievementsController],
providers: [
Expand Down
8 changes: 4 additions & 4 deletions src/achievements/achievements.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import { AchievementCategory } from "./achievements.constants";
import Mocked = jest.Mocked;
import sleep from "../utils/sleep";
import { CollectionEntry } from "../collections/collections-entries/entities/collection-entry.entity";
import { UserLevelService } from "../user-level/user-level.service";
import { LevelService } from "../level/level.service";

describe("AchievementsService", () => {
let service: AchievementsService;
let dataSource: Mocked<DataSource>;
let obtainedAchievementRepository: Mocked<Repository<ObtainedAchievement>>;
let userLevelService: Mocked<UserLevelService>;
let userLevelService: Mocked<LevelService>;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
Expand All @@ -31,7 +31,7 @@ describe("AchievementsService", () => {
},
},
{
provide: UserLevelService,
provide: LevelService,
useValue: {
increaseExp: jest.fn(),
},
Expand All @@ -44,7 +44,7 @@ describe("AchievementsService", () => {
obtainedAchievementRepository = module.get(
getRepositoryToken(ObtainedAchievement),
);
userLevelService = module.get(UserLevelService);
userLevelService = module.get(LevelService);
});

it("should be defined", () => {
Expand Down
5 changes: 3 additions & 2 deletions src/achievements/achievements.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { AchievementCategory } from "./achievements.constants";
import { Profile } from "../profile/entities/profile.entity";
import { GetAchievementsRequestDto } from "./dto/get-achievements-request.dto";
import { UpdateFeaturedObtainedAchievementDto } from "./dto/update-featured-obtained-achievement.dto";
import { UserLevelService } from "../user-level/user-level.service";
import { LevelService } from "../level/level.service";

function validateAchievements() {
achievementsData.forEach((achievement, index, array) => {
Expand Down Expand Up @@ -42,11 +42,12 @@ function validateAchievements() {
@Injectable()
export class AchievementsService {
private readonly logger = new Logger(AchievementsService.name);

constructor(
@InjectRepository(ObtainedAchievement)
private obtainedAchievementsRepository: Repository<ObtainedAchievement>,
private dataSource: DataSource,
private userLevelService: UserLevelService,
private userLevelService: LevelService,
) {
validateAchievements();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import { Test, TestingModule } from "@nestjs/testing";
import { ActivitiesFeedController } from "./activities-feed.controller";
import { ActivitiesFeedService } from "./activities-feed.service";
import { CACHE_MANAGER } from "@nestjs/cache-manager";

describe("ActivitiesFeedController", () => {
let controller: ActivitiesFeedController;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [ActivitiesFeedController],
providers: [ActivitiesFeedService],
providers: [
{
provide: ActivitiesFeedService,
useValue: {},
},
{
provide: CACHE_MANAGER,
useValue: {},
},
],
}).compile();

controller = module.get<ActivitiesFeedController>(
Expand Down
14 changes: 7 additions & 7 deletions src/activities/activities-feed/activities-feed.service.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { HttpException, HttpStatus, Inject, Injectable } from "@nestjs/common";
import { CollectionsEntriesService } from "src/collections/collections-entries/collections-entries.service";
import { ReviewsService } from "src/reviews/reviews.service";
import {
ActivityCriteria,
ActivityType,
} from "../activities-queue/activities-queue.constants";
import { ProfileService } from "src/profile/profile.service";
import { Activity } from "../activities-repository/entities/activity.entity";
import { ActivitiesFeedRequestDto } from "./dto/activities-feed-request.dto";
import { ActivitiesRepositoryService } from "../activities-repository/activities-repository.service";
import { CACHE_MANAGER } from "@nestjs/cache-manager";
import { Cache } from "cache-manager";
import { TPaginationData } from "../../utils/pagination/pagination-response.dto";
import { StatisticsService } from "../../statistics/statistics.service";
import { ProfileService } from "../../profile/profile.service";
import { ReviewsService } from "../../reviews/reviews.service";
import { CollectionsEntriesService } from "../../collections/collections-entries/collections-entries.service";
import {
ActivityCriteria,
ActivityType,
} from "../activities-queue/activities-queue.constants";

export const ACTIVITY_FEED_CACHE_KEY = "queue-feed";

Expand Down
6 changes: 4 additions & 2 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ import { StatisticsQueueModule } from "./statistics/statistics-queue/statistics-
import { ActivitiesFeedModule } from "./activities/activities-feed/activities-feed.module";
import { seconds, ThrottlerModule } from "@nestjs/throttler";
import { ThrottlerStorageRedisService } from "nestjs-throttler-storage-redis";
import { UserLevelModule } from "./user-level/user-level.module";
import { LevelModule } from "./level/level.module";
import { HealthModule } from "./health/health.module";
import { AchievementsModule } from "./achievements/achievements.module";
import { FollowModule } from "./follow/follow.module";
import { IgdbSyncModule } from "./sync/igdb/igdb-sync.module";
import { NotificationsModule } from './notifications/notifications.module';

/**
* IMPORTANT: For any package that uses the "ioredis" module internally, make sure to use "forRootAsync".
Expand Down Expand Up @@ -118,10 +119,11 @@ import { IgdbSyncModule } from "./sync/igdb/igdb-sync.module";
CollectionsModule,
StatisticsModule,
StatisticsQueueModule,
UserLevelModule,
LevelModule,
HealthModule,
AchievementsModule,
FollowModule,
NotificationsModule,
],
})
export class AppModule implements NestModule {
Expand Down
24 changes: 23 additions & 1 deletion src/auth/auth.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Error as STError } from "supertokens-node";

import { verifySession } from "supertokens-node/recipe/session/framework/express";
import { Reflector } from "@nestjs/core";
import { SessionRequest } from "supertokens-node/lib/build/framework/express";
import UserRoles from "supertokens-node/recipe/userroles";

/**
* Default AuthGuard that checks for a valid session.
Expand All @@ -25,11 +27,31 @@ export class AuthGuard implements CanActivate {
"isPublic",
context.getHandler(),
);
console.log("isPublic: ", isPublic);

const requiredRoles = this.reflector.get<string[] | undefined>(
"roles",
context.getHandler(),
);

// You can create an optional version of this by passing {sessionRequired: false} to verifySession
await verifySession({
sessionRequired: !isPublic,
/**
* Override Supertokens validators to use UserRole validation logic.
* @param globalClaimValidators
*/
overrideGlobalClaimValidators: (globalClaimValidators) => {
const validators = [...globalClaimValidators];
if (requiredRoles && requiredRoles.length > 0) {
for (const role of requiredRoles) {
validators.push(
UserRoles.UserRoleClaim.validators.includes(role),
);
}
}

return validators;
},
})(ctx.getRequest(), resp, (res) => {
err = res;
});
Expand Down
9 changes: 9 additions & 0 deletions src/auth/roles.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { SetMetadata } from "@nestjs/common";

/**
* Decorator that enforces user roles to be verified by SuperTokens.
* MUST be used with the AuthGuard.
* @param roles
* @constructor
*/
export const Roles = (roles: string[]) => SetMetadata("roles", roles);
Original file line number Diff line number Diff line change
Expand Up @@ -104,37 +104,4 @@ describe("CollectionsEntriesService", () => {
}),
);
});

it("should attach a review when re-creating a entry", async () => {
const userId = "1";
const dto: CreateCollectionEntryDto = {
isFavorite: true,
gameId: 1942,
collectionIds: ["111111"],
platformIds: [EGamePlatformIds.PC.valueOf()],
};
const reviewFindSpy = jest.spyOn(
reviewService,
"findOneByUserIdAndGameId",
);
reviewFindSpy.mockImplementation(async () => {
return { id: "review12345" } as Review;
});
jest.spyOn(repository, "save").mockImplementationOnce(async () => {
return { id: "12345" } as CollectionEntry;
});

const createSpy = jest.spyOn(service, "createOrUpdate");
const repositorySaveSpy = jest.spyOn(repository, "save");
await service.createOrUpdate(userId, dto);
expect(reviewFindSpy).toBeCalled();
expect(createSpy).toHaveReturned();
expect(repositorySaveSpy).toBeCalledWith(
expect.objectContaining({
review: {
id: "review12345",
},
}),
);
});
});
3 changes: 3 additions & 0 deletions src/follow/dto/follow-remove.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { FollowRegisterDto } from "./follow-register.dto";

export class FollowRemoveDto extends FollowRegisterDto {}
33 changes: 20 additions & 13 deletions src/follow/follow.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import { Test, TestingModule } from '@nestjs/testing';
import { FollowController } from './follow.controller';
import { Test, TestingModule } from "@nestjs/testing";
import { FollowController } from "./follow.controller";
import { FollowService } from "./follow.service";

describe('FollowController', () => {
let controller: FollowController;
describe("FollowController", () => {
let controller: FollowController;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [FollowController],
}).compile();
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: FollowService,
useValue: {},
},
],
controllers: [FollowController],
}).compile();

controller = module.get<FollowController>(FollowController);
});
controller = module.get<FollowController>(FollowController);
});

it('should be defined', () => {
expect(controller).toBeDefined();
});
it("should be defined", () => {
expect(controller).toBeDefined();
});
});
47 changes: 35 additions & 12 deletions src/follow/follow.controller.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
import { Body, Controller, Get, Post, Query, UseGuards } from "@nestjs/common";
import {
Body,
Controller,
Delete,
Get,
Post,
Query,
UseGuards,
} from "@nestjs/common";
import { AuthGuard } from "../auth/auth.guard";
import { FollowService } from "./follow.service";
import { FollowRegisterDto } from "./dto/follow-register.dto";
import { SessionContainer } from "supertokens-node/recipe/session";
import { Session } from "../auth/session.decorator";
import { ApiOkResponse, ApiTags } from "@nestjs/swagger";
import { FollowStatusDto } from "./dto/follow-status.dto";
import { FollowRemoveDto } from "./dto/follow-remove.dto";
import { Public } from "../auth/public.decorator";

@Controller("follow")
@ApiTags("follow")
@UseGuards(AuthGuard)
export class FollowController {
constructor(private followService: FollowService) {}

@Post()
async registerFollow(
@Session() session: SessionContainer,
@Body() dto: FollowRegisterDto,
) {
return await this.followService.registerFollow(
session.getUserId(),
dto.followedUserId,
);
}

@Get("status")
@ApiOkResponse({
status: 200,
type: FollowStatusDto,
})
@Public()
async getFollowerStatus(
@Query("followerUserId") followerUserId: string,
@Query("followedUserId") followedUserId: string,
Expand All @@ -37,7 +37,30 @@ export class FollowController {
}

@Get("count")
@Public()
async getFollowersCount(@Query("targetUserId") targetUserId: string) {
return await this.followService.getFollowersCount(targetUserId);
}

@Post()
async registerFollow(
@Session() session: SessionContainer,
@Body() dto: FollowRegisterDto,
) {
return await this.followService.registerFollow(
session.getUserId(),
dto.followedUserId,
);
}

@Delete()
async removeFollow(
@Session() session: SessionContainer,
@Body() dto: FollowRemoveDto,
) {
return await this.followService.removeFollow(
session.getUserId(),
dto.followedUserId,
);
}
}
Loading

0 comments on commit b09ee62

Please sign in to comment.