Skip to content

Commit

Permalink
Merge pull request #73 from game-node-app/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
Lamarcke authored Apr 23, 2024
2 parents fe563fb + 3516639 commit 99d9e5b
Show file tree
Hide file tree
Showing 20 changed files with 197 additions and 24 deletions.
2 changes: 1 addition & 1 deletion server_swagger.json

Large diffs are not rendered by default.

12 changes: 10 additions & 2 deletions src/activities/activities-feed/activities-feed.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { Controller, Get, Query, UseInterceptors } from "@nestjs/common";
import {
Controller,
Get,
Query,
UseGuards,
UseInterceptors,
} from "@nestjs/common";
import { ActivitiesFeedService } from "./activities-feed.service";
import { ApiOkResponse, ApiTags } from "@nestjs/swagger";
import { ActivitiesFeedRequestDto } from "./dto/activities-feed-request.dto";
Expand All @@ -9,9 +15,11 @@ import { SessionContainer } from "supertokens-node/recipe/session";
import { CacheInterceptor, CacheTTL } from "@nestjs/cache-manager";
import { minutes } from "@nestjs/throttler";
import { Public } from "../../auth/public.decorator";
import { AuthGuard } from "../../auth/auth.guard";

@ApiTags("activities-feed")
@Controller("activities/feed")
@UseGuards(AuthGuard)
export class ActivitiesFeedController {
constructor(
private readonly activitiesFeedService: ActivitiesFeedService,
Expand All @@ -27,7 +35,7 @@ export class ActivitiesFeedController {
@Public()
async buildActivitiesFeed(
@Query() dto: ActivitiesFeedRequestDto,
@Session() session?: SessionContainer,
@Session() session: SessionContainer | undefined,
) {
return this.activitiesFeedService.buildActivitiesFeed(
session?.getUserId(),
Expand Down
9 changes: 4 additions & 5 deletions src/activities/activities-feed/activities-feed.service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { HttpException, HttpStatus, Inject, Injectable } from "@nestjs/common";
import { HttpException, HttpStatus, Injectable } from "@nestjs/common";
import { Activity } from "../activities-repository/entities/activity.entity";
import {
ActivitiesFeedRequestDto,
ActivityFeedCriteria,
} 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 { ActivityType } from "../activities-queue/activities-queue.constants";
import { FollowService } from "../../follow/follow.service";
Expand Down Expand Up @@ -84,12 +82,13 @@ export class ActivitiesFeedService {
userId: string,
dto: ActivitiesFeedRequestDto,
): Promise<TPaginationData<Activity>> {
const [followedUsersIds] = await this.followService.getFollowerData({
const [followedUsersIds] = await this.followService.getFollowerInfo({
targetUserId: userId,
criteria: "followers",
offset: 0,
limit: 9999999,
});
console.log(followedUsersIds);

const baseFindOptions = buildBaseFindOptions(dto);

Expand All @@ -114,7 +113,7 @@ export class ActivitiesFeedService {
if (userId == undefined) {
throw new HttpException(
"User must be logged-in to see following user activities.",
HttpStatus.UNAUTHORIZED,
HttpStatus.BAD_REQUEST,
);
}
return this.buildFollowingActivitiesFeed(userId, dto);
Expand Down
5 changes: 5 additions & 0 deletions src/activities/activities-queue/activities-queue.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,10 @@ export enum ActivityType {
export class ActivityCreate {
type: ActivityType;
sourceId: string | number;
/**
* Extra sourceId that may be necessary to persist an activity
* e.g. related collection id when inserting a collection entry activity
*/
complementarySourceId?: string | number;
profileUserId: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
Controller,
Get,
Query,
UseGuards,
UseInterceptors,
} from "@nestjs/common";
import { ApiOkResponse, ApiTags } from "@nestjs/swagger";
import { FindLatestActivitiesDto } from "./dto/find-latest-activities.dto";
import { ActivitiesRepositoryService } from "./activities-repository.service";
import { ActivitiesPaginatedResponseDto } from "./dto/activities-paginated-response.dto";
import { PaginationInterceptor } from "../../interceptor/pagination.interceptor";
import { AuthGuard } from "../../auth/auth.guard";
import { Public } from "../../auth/public.decorator";

@Controller("activities")
@ApiTags("activities")
@UseGuards(AuthGuard)
export class ActivitiesRepositoryController {
constructor(
private readonly activitiesRepositoryService: ActivitiesRepositoryService,
) {}

@Get()
@ApiOkResponse({
type: ActivitiesPaginatedResponseDto,
})
@UseInterceptors(PaginationInterceptor)
@Public()
async findLatest(@Query() dto: FindLatestActivitiesDto) {
return this.activitiesRepositoryService.findLatest(dto);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { ActivitiesRepositoryService } from "./activities-repository.service";
import { TypeOrmModule } from "@nestjs/typeorm";
import { Activity } from "./entities/activity.entity";
import { StatisticsQueueModule } from "../../statistics/statistics-queue/statistics-queue.module";
import { ActivitiesRepositoryController } from './activities-repository.controller';

@Module({
imports: [TypeOrmModule.forFeature([Activity]), StatisticsQueueModule],
providers: [ActivitiesRepositoryService],
exports: [ActivitiesRepositoryService],
controllers: [ActivitiesRepositoryController],
})
export class ActivitiesRepositoryModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
} from "../activities-queue/activities-queue.constants";
import { StatisticsQueueService } from "../../statistics/statistics-queue/statistics-queue.service";
import { StatisticsSourceType } from "../../statistics/statistics.constants";
import { FindLatestActivitiesDto } from "./dto/find-latest-activities.dto";
import { buildBaseFindOptions } from "../../utils/buildBaseFindOptions";

@Injectable()
export class ActivitiesRepositoryService {
Expand All @@ -25,7 +27,7 @@ export class ActivitiesRepositoryService {
) {}

async create(dto: ActivityCreate) {
const { type, sourceId, profileUserId } = dto;
const { type, sourceId, complementarySourceId, profileUserId } = dto;

const activity = this.activitiesRepository.create({
type,
Expand All @@ -36,10 +38,16 @@ export class ActivitiesRepositoryService {
case ActivityType.COLLECTION_ENTRY:
if (typeof sourceId !== "string") {
throw new Error(
"Collection Entry activities should have a string sourceId",
"CollectionEntry activities should have a string sourceId",
);
}
if (typeof complementarySourceId !== "string") {
throw new Error(
"A string complementarySourceId must be specified for CollectionEntry activities",
);
}
activity.collectionEntryId = sourceId;
activity.collectionId = complementarySourceId;
break;
case ActivityType.REVIEW:
if (typeof sourceId !== "string") {
Expand Down Expand Up @@ -101,4 +109,14 @@ export class ActivitiesRepositoryService {
async findOneBy(by: FindOneOptions<Activity>) {
return await this.activitiesRepository.findOne(by);
}

async findLatest(dto: FindLatestActivitiesDto) {
const baseFindOptions = buildBaseFindOptions(dto);
return await this.findLatestBy({
...baseFindOptions,
where: {
profileUserId: dto.userId,
},
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Activity } from "../entities/activity.entity";
import { PaginationInfo } from "../../../utils/pagination/pagination-response.dto";

export class ActivitiesPaginatedResponseDto {
data: Activity[];
pagination: PaginationInfo;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { IsOptional, IsString, Length } from "class-validator";
import { OmitType } from "@nestjs/swagger";
import { BaseFindDto } from "../../../utils/base-find.dto";
import { Activity } from "../entities/activity.entity";

export class FindLatestActivitiesDto extends OmitType(BaseFindDto<Activity>, [
"orderBy",
"search",
]) {
@IsOptional()
@IsString()
@Length(36)
userId?: string;
}
12 changes: 11 additions & 1 deletion src/activities/activities-repository/entities/activity.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import { Profile } from "../../../profile/entities/profile.entity";
import { CollectionEntry } from "../../../collections/collections-entries/entities/collection-entry.entity";
import { Review } from "../../../reviews/entities/review.entity";
import { UserFollow } from "../../../follow/entity/user-follow.entity";
import { Collection } from "../../../collections/entities/collection.entity";

@Entity()
@Unique(["profile", "collectionEntry"])
@Unique(["profile", "collectionEntry", "collection"])
@Unique(["profile", "userFollow"])
@Unique(["profile", "review"])
export class Activity {
Expand Down Expand Up @@ -43,6 +44,15 @@ export class Activity {
nullable: true,
})
collectionEntryId: string | null;
@ManyToOne(() => Collection, {
nullable: true,
onDelete: "CASCADE",
})
collection: Collection | null;
@Column({
nullable: true,
})
collectionId: string | null;
@ManyToOne(() => Review, {
nullable: true,
onDelete: "CASCADE",
Expand Down
2 changes: 1 addition & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ function getRedisConfig() {
autoLoadEntities: true,
// Never turn this on. Use migrations instead.
synchronize: false,
logging: false,

/**
* Allows us to cache select queries using ioredis. Default duration of 1000ms.
* https://orkhan.gitbook.io/typeorm/docs/caching
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ export class CollectionsEntriesController {
);
}

@Get(":id")
async findEntryById(@Param("id") collectionEntryId: string) {
return await this.collectionsEntriesService.findOneByIdOrFail(
collectionEntryId,
);
}

/**
* Returns a specific collection entry based on game ID
* @param session
Expand Down Expand Up @@ -76,6 +83,7 @@ export class CollectionsEntriesController {
collectionEntryId,
);
}

@Delete(":id")
@HttpCode(HttpStatus.NO_CONTENT)
async deleteOwnEntry(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import { CollectionsEntriesController } from "./collections-entries.controller";
import { CollectionsEntriesService } from "./collections-entries.service";
import { GamePlatform } from "../../game/game-repository/entities/game-platform.entity";
import { AchievementsModule } from "../../achievements/achievements.module";
import { LevelModule } from "../../level/level.module";

@Module({
imports: [
TypeOrmModule.forFeature([CollectionEntry, GamePlatform]),
ActivitiesQueueModule,
AchievementsModule,
LevelModule,
],
controllers: [CollectionsEntriesController],
providers: [CollectionsEntriesService],
Expand Down
31 changes: 24 additions & 7 deletions src/collections/collections-entries/collections-entries.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { ActivityType } from "../../activities/activities-queue/activities-queue
import { AchievementsQueueService } from "../../achievements/achievements-queue/achievements-queue.service";
import { AchievementCategory } from "../../achievements/achievements.constants";
import { getIconNamesForPlatformAbbreviations } from "../../game/game-repository/game-repository.utils";
import { LevelService } from "../../level/level.service";
import { LevelIncreaseActivities } from "../../level/level.constants";

@Injectable()
export class CollectionsEntriesService {
Expand All @@ -24,6 +26,7 @@ export class CollectionsEntriesService {
private collectionEntriesRepository: Repository<CollectionEntry>,
private activitiesQueueService: ActivitiesQueueService,
private achievementsQueueService: AchievementsQueueService,
private levelService: LevelService,
) {}

async findOneById(id: string) {
Expand Down Expand Up @@ -219,9 +222,13 @@ export class CollectionsEntriesService {
id: id,
}));

const entry = await this.findOneByUserIdAndGameId(userId, gameId);
const possibleExistingEntry = await this.findOneByUserIdAndGameId(
userId,
gameId,
);

const upsertedEntry = await this.collectionEntriesRepository.save({
...entry,
...possibleExistingEntry,
isFavorite,
collections,
game: {
Expand All @@ -230,11 +237,21 @@ export class CollectionsEntriesService {
ownedPlatforms,
});

this.activitiesQueueService.register({
sourceId: upsertedEntry.id,
type: ActivityType.COLLECTION_ENTRY,
profileUserId: userId,
});
if (!possibleExistingEntry) {
this.levelService.registerLevelExpIncreaseActivity(
userId,
LevelIncreaseActivities.COLLECTION_ENTRY_CREATED,
);
}

for (const uniqueCollectionId of uniqueCollectionIds) {
this.activitiesQueueService.register({
sourceId: upsertedEntry.id,
complementarySourceId: uniqueCollectionId,
type: ActivityType.COLLECTION_ENTRY,
profileUserId: userId,
});
}

this.achievementsQueueService.addTrackingJob({
targetUserId: userId,
Expand Down
13 changes: 12 additions & 1 deletion src/follow/follow.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Get,
HttpCode,
HttpStatus,
Param,
Post,
Query,
UseGuards,
Expand All @@ -22,6 +23,7 @@ import { Public } from "../auth/public.decorator";
import { FollowInfoRequestDto } from "./dto/follow-info-request.dto";
import { PaginationInterceptor } from "../interceptor/pagination.interceptor";
import { FollowInfoResponseDto } from "./dto/follow-info-response.dto";
import { UserFollow } from "./entity/user-follow.entity";

@Controller("follow")
@ApiTags("follow")
Expand All @@ -42,6 +44,15 @@ export class FollowController {
return this.followService.getStatus(followerUserId, followedUserId);
}

@Get(":id")
@ApiOkResponse({
status: 200,
type: UserFollow,
})
async getUserFollowById(@Param("id") userFollowId: number) {
return this.followService.findOneByIdOrFail(userFollowId);
}

@Post("info")
@UseInterceptors(PaginationInterceptor)
@ApiOkResponse({
Expand All @@ -50,7 +61,7 @@ export class FollowController {
@Public()
@HttpCode(HttpStatus.OK)
async getFollowInfo(@Body() dto: FollowInfoRequestDto) {
return await this.followService.getFollowerData(dto);
return await this.followService.getFollowerInfo(dto);
}

@Post()
Expand Down
7 changes: 6 additions & 1 deletion src/follow/follow.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ import { TypeOrmModule } from "@nestjs/typeorm";
import { UserFollow } from "./entity/user-follow.entity";
import { FollowController } from "./follow.controller";
import { NotificationsModule } from "../notifications/notifications.module";
import { ActivitiesQueueModule } from "../activities/activities-queue/activities-queue.module";

@Module({
imports: [TypeOrmModule.forFeature([UserFollow]), NotificationsModule],
imports: [
TypeOrmModule.forFeature([UserFollow]),
NotificationsModule,
ActivitiesQueueModule,
],
providers: [FollowService],
controllers: [FollowController],
exports: [FollowService],
Expand Down
Loading

0 comments on commit 99d9e5b

Please sign in to comment.