Skip to content

Commit 1f26621

Browse files
authored
Merge pull request #128 from game-node-app/dev
Changes to the IGDB sync system and notifications system
2 parents 6a1aa6d + 413a21c commit 1f26621

File tree

9 files changed

+105
-28
lines changed

9 files changed

+105
-28
lines changed

server_swagger.json

+1-1
Large diffs are not rendered by default.

src/activities/activities-repository/activities-repository.controller.ts

+15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
Controller,
33
Get,
4+
Param,
45
Query,
56
UseGuards,
67
UseInterceptors,
@@ -12,6 +13,7 @@ import { ActivitiesPaginatedResponseDto } from "./dto/activities-paginated-respo
1213
import { PaginationInterceptor } from "../../interceptor/pagination.interceptor";
1314
import { AuthGuard } from "../../auth/auth.guard";
1415
import { Public } from "../../auth/public.decorator";
16+
import { Activity } from "./entities/activity.entity";
1517

1618
@Controller("activities")
1719
@ApiTags("activities")
@@ -30,4 +32,17 @@ export class ActivitiesRepositoryController {
3032
async findLatest(@Query() dto: FindLatestActivitiesDto) {
3133
return this.activitiesRepositoryService.findLatest(dto);
3234
}
35+
36+
@Get("detail/:id")
37+
@ApiOkResponse({
38+
type: Activity,
39+
})
40+
@Public()
41+
async findOneById(@Param("id") activityId: string) {
42+
return this.activitiesRepositoryService.findOneByOrFail({
43+
where: {
44+
id: activityId,
45+
},
46+
});
47+
}
3348
}

src/comment/comment.controller.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
UseGuards,
1313
UseInterceptors,
1414
} from "@nestjs/common";
15-
import { ApiOkResponse, ApiTags } from "@nestjs/swagger";
15+
import { ApiOkResponse, ApiTags, getSchemaPath } from "@nestjs/swagger";
1616
import { CommentService } from "./comment.service";
1717
import { CreateCommentDto } from "./dto/create-comment.dto";
1818
import { Session } from "../auth/session.decorator";
@@ -28,6 +28,8 @@ import { DeleteCommentDto } from "./dto/delete-comment.dto";
2828
import { SuspensionGuard } from "../suspension/suspension.guard";
2929
import { BaseFindDto } from "../utils/base-find.dto";
3030
import { UserComment } from "./entity/user-comment.entity";
31+
import { ReviewComment } from "./entity/review-comment.entity";
32+
import { ActivityComment } from "./entity/activity-comment.entity";
3133

3234
@Controller("comment")
3335
@ApiTags("comment")
@@ -48,6 +50,14 @@ export class CommentController {
4850
}
4951

5052
@Get(":sourceType/:id")
53+
@ApiOkResponse({
54+
schema: {
55+
oneOf: [
56+
{ type: getSchemaPath(ReviewComment) },
57+
{ type: getSchemaPath(ActivityComment) },
58+
],
59+
},
60+
})
5161
@Public()
5262
async findOneById(
5363
@Param("sourceType") sourceType: CommentSourceType,

src/game/game-repository/game-repository-create.service.ts

+42-13
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { GameEngineLogo } from "./entities/game-engine-logo.entity";
2424
import { PartialGame } from "./game-repository.types";
2525
import { StatisticsQueueService } from "../../statistics/statistics-queue/statistics-queue.service";
2626
import { StatisticsSourceType } from "../../statistics/statistics.constants";
27+
import { days } from "@nestjs/throttler";
2728

2829
/**
2930
* Service responsible for data inserting and updating for all game-related models.
@@ -101,6 +102,39 @@ export class GameRepositoryCreateService {
101102
private readonly statisticsQueueService: StatisticsQueueService,
102103
) {}
103104

105+
async shouldUpdate(game: PartialGame) {
106+
if (game.id == null || typeof game.id !== "number") {
107+
return false;
108+
} else if (
109+
game.name == undefined &&
110+
(game.alternativeNames == undefined ||
111+
game.alternativeNames.length === 0)
112+
) {
113+
return false;
114+
} else if (
115+
(await this.gameRepository.existsBy({ id: game.id })) &&
116+
game.updatedAt != undefined
117+
) {
118+
const now = new Date();
119+
const lastUpdateDate = game.updatedAt as Date;
120+
const dayInMs = 1000 * 3600 * 24;
121+
122+
const differenceInTime = now.getTime() - lastUpdateDate.getTime();
123+
const approximateDifferenceInDays = Math.round(
124+
differenceInTime / dayInMs,
125+
);
126+
127+
// game already exists and it has been more than thirty days since it's last update
128+
// this logic only works if the 'updated_at' property is being returned by igdb-sync.
129+
if (approximateDifferenceInDays >= 30) {
130+
return false;
131+
}
132+
const one = 2;
133+
}
134+
135+
return true;
136+
}
137+
104138
/**
105139
* Creates or updates a game in our database. <br>
106140
* ManyToMany models can't be easily upserted, since the junction table is not inserted/updated automatically (without .save).
@@ -109,16 +143,11 @@ export class GameRepositoryCreateService {
109143
* @param game
110144
*/
111145
async createOrUpdate(game: PartialGame) {
112-
if (game.id == null || typeof game.id !== "number") {
113-
throw new Error("Game ID must be a number.");
114-
} else if (
115-
game.name == undefined &&
116-
(game.alternativeNames == undefined ||
117-
game.alternativeNames.length === 0)
118-
) {
119-
throw new Error(
120-
"Game name or alternative names must be specified.",
121-
);
146+
const shouldProcess = await this.shouldUpdate(game);
147+
148+
if (!shouldProcess) {
149+
// Do not log here, as most games are skipped after the first run.
150+
return;
122151
}
123152

124153
const isUpdateAction = await this.gameRepository.existsBy({
@@ -163,9 +192,9 @@ export class GameRepositoryCreateService {
163192

164193
/**
165194
* Builds child relationships which depend on the game being saved (e.g. alternative names, cover).
166-
e.g. Relationships where Game is on the OneToMany or ManyToMany side.<br>
167-
168-
<strong> We assume the game is already persisted at this point, so we can use it as a parent.</strong>
195+
* e.g. Relationships where Game is on the OneToMany or ManyToMany side.<br>
196+
*
197+
* <strong> We assume the game is already persisted at this point, so we can use it as a parent.</strong>
169198
* @param game
170199
*/
171200
async buildChildRelationships(game: PartialGame) {

src/game/game-repository/game-repository.types.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { DeepPartial } from "typeorm";
22
import { Game } from "./entities/game.entity";
3-
import { PickType } from "@nestjs/swagger";
43

54
export type PartialGame = DeepPartial<Game> & {
65
id: number;

src/notifications/notifications.service.ts

+19-5
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,20 @@ export class NotificationsService {
113113
comparedNotification.category,
114114
);
115115

116-
const comparableProperties: (keyof Notification)[] = [
116+
const comparableSourceIds: (keyof Notification)[] = [
117117
"reviewId",
118118
"activityId",
119+
"reviewCommentId",
120+
"activityCommentId",
119121
];
120122

121-
const isSameSource = comparableProperties.some(
123+
const hasSameCategory =
124+
notification.category === comparedNotification.category;
125+
const hasSameSourceType =
126+
notification.sourceType ===
127+
comparedNotification.sourceType;
128+
129+
const hasSameSourceId = comparableSourceIds.some(
122130
(property) => {
123131
return (
124132
comparedNotification[property] != undefined &&
@@ -129,7 +137,11 @@ export class NotificationsService {
129137
);
130138

131139
const isSimilar =
132-
!isAlreadyProcessed && hasValidCategory && isSameSource;
140+
!isAlreadyProcessed &&
141+
hasValidCategory &&
142+
hasSameCategory &&
143+
hasSameSourceType &&
144+
hasSameSourceId;
133145

134146
if (isSimilar) {
135147
processedEntities.set(
@@ -154,11 +166,13 @@ export class NotificationsService {
154166
sourceId:
155167
notification.reviewId! ||
156168
notification.activityId! ||
157-
notification.profileUserId! ||
158169
notification.importerNotificationId! ||
159170
notification.reportId! ||
160171
notification.reviewCommentId! ||
161-
notification.activityCommentId!,
172+
notification.activityCommentId! ||
173+
// profileUserId should be last, otherwise you will get weird issues.
174+
notification.profileUserId!,
175+
162176
sourceType: notification.sourceType,
163177
notifications: aggregationNotifications,
164178
});

src/statistics/statistics-queue/statistics-queue.processor.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { StatisticsService } from "../statistics.types";
1616
import { CommentStatisticsService } from "../comment-statistics.service";
1717

1818
@Processor(STATISTICS_QUEUE_NAME, {
19-
concurrency: 300,
19+
concurrency: 5,
2020
})
2121
export class StatisticsQueueProcessor extends WorkerHostProcessor {
2222
logger = new Logger(StatisticsQueueProcessor.name);

src/statistics/statistics.controller.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,15 @@ export class StatisticsController {
6161
return this.activityStatisticsService.findOne(
6262
dto.sourceId as string,
6363
);
64+
case StatisticsSourceType.ACTIVITY_COMMENT:
65+
return this.commentStatisticsService.findOne(
66+
dto.sourceId as string,
67+
StatisticsSourceType.ACTIVITY_COMMENT,
68+
);
6469
case StatisticsSourceType.REVIEW_COMMENT:
6570
return this.commentStatisticsService.findOne(
6671
dto.sourceId as string,
67-
dto.sourceType,
72+
StatisticsSourceType.REVIEW_COMMENT,
6873
);
6974
default:
7075
throw new HttpException(
@@ -149,6 +154,11 @@ export class StatisticsController {
149154
dto.statisticsId,
150155
session?.getUserId(),
151156
);
157+
case StatisticsSourceType.ACTIVITY_COMMENT:
158+
return this.commentStatisticsService.getStatus(
159+
dto.statisticsId,
160+
session?.getUserId(),
161+
);
152162
case StatisticsSourceType.REVIEW_COMMENT:
153163
return this.commentStatisticsService.getStatus(
154164
dto.statisticsId,

src/sync/igdb/utils/game-conversor-utils.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,17 @@ export const parseGameDates = (game: PartialGame) => {
5050
"start_date",
5151
"startDate",
5252
];
53+
5354
for (const [key, value] of Object.entries(parsedGame)) {
5455
if (dateFields.includes(key) && typeof value === "number") {
56+
// IGDB Dates are always returned in seconds, even if they are
57+
// unix timestamps.
5558
let asDate: Date | undefined = new Date(value * 1000);
5659

57-
// Dates can't be invalid or in the future.
58-
if (
59-
asDate.toString() === "Invalid Date" ||
60-
asDate.getTime() > Date.now()
61-
) {
60+
if (asDate.toString() === "Invalid Date") {
6261
asDate = undefined;
6362
}
63+
6464
parsedGame[key] = asDate;
6565
}
6666
}

0 commit comments

Comments
 (0)