Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release recnet-web v1.15.0 / recnet-api v1.7.1 #329

Merged
merged 19 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/recnet-api/package.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"name": "recnet-api",
"version": "1.7.0"
"version": "1.7.1"
}
23 changes: 13 additions & 10 deletions apps/recnet-api/src/database/repository/rec.repository.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ import { Prisma } from "@prisma/client";
import { article } from "./article.repository.type";
import { userPreview } from "./user.repository.type";

export const recReaction = Prisma.validator<Prisma.RecReactionDefaultArgs>()({
select: {
id: true,
userId: true,
recId: true,
reaction: true,
},
});
export type RecReaction = Prisma.RecReactionGetPayload<typeof recReaction>;

export const rec = Prisma.validator<Prisma.RecommendationDefaultArgs>()({
select: {
id: true,
Expand All @@ -15,20 +25,13 @@ export const rec = Prisma.validator<Prisma.RecommendationDefaultArgs>()({
article: {
select: article.select,
},
reactions: {
select: recReaction.select,
},
},
});
export type Rec = Prisma.RecommendationGetPayload<typeof rec>;

export const recReaction = Prisma.validator<Prisma.RecReactionDefaultArgs>()({
select: {
id: true,
userId: true,
recId: true,
reaction: true,
},
});
export type RecReaction = Prisma.RecReactionGetPayload<typeof recReaction>;

export type DateRange = {
from?: Date;
to?: Date;
Expand Down
18 changes: 3 additions & 15 deletions apps/recnet-api/src/modules/email/email.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@ import groupBy from "lodash.groupby";

import { AppConfig, NodemailerConfig } from "@recnet-api/config/common.config";
import RecRepository from "@recnet-api/database/repository/rec.repository";
import {
Rec as DbRec,
RecFilterBy,
} from "@recnet-api/database/repository/rec.repository.type";
import { RecFilterBy } from "@recnet-api/database/repository/rec.repository.type";
import UserRepository from "@recnet-api/database/repository/user.repository";
import { User as DbUser } from "@recnet-api/database/repository/user.repository.type";
import WeeklyDigestCronLogRepository from "@recnet-api/database/repository/weekly-digest-cron-log.repository";
import { Rec } from "@recnet-api/modules/rec/entities/rec.entity";
import { transformRec } from "@recnet-api/modules/rec/rec.transformer";
import { sleep } from "@recnet-api/utils";

import { getLatestCutOff } from "@recnet/recnet-date-fns";
Expand All @@ -28,8 +26,6 @@ import {
import { SendMailResult, Transporter } from "./email.type";
import WeeklyDigest, { WeeklyDigestSubject } from "./templates/WeeklyDigest";

import { transformUserPreview } from "../user/user.transformer";

@Injectable()
export class EmailService {
constructor(
Expand Down Expand Up @@ -125,7 +121,7 @@ export class EmailService {
MAX_REC_PER_MAIL,
filter
);
return dbRecs.map((dbRec) => this.getRecFromDbRec(dbRec));
return dbRecs.map((dbRec) => transformRec(dbRec));
}

private async sendWeeklyDigest(
Expand Down Expand Up @@ -156,12 +152,4 @@ export class EmailService {

return result;
}

private getRecFromDbRec(dbRec: DbRec): Rec {
return {
...dbRec,
cutoff: dbRec.cutoff.toISOString(),
user: transformUserPreview(dbRec.user),
};
}
}
21 changes: 21 additions & 0 deletions apps/recnet-api/src/modules/rec/entities/rec.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,26 @@ import { ApiProperty } from "@nestjs/swagger";

import { Article } from "@recnet-api/modules/article/entities/article.entity";

import { ReactionType } from "@recnet/recnet-api-model";

import { UserPreview } from "../../user/entities/user.preview.entity";

class NumReaction {
@ApiProperty()
type: ReactionType;

@ApiProperty()
count: number;
}

class Reactions {
@ApiProperty()
selfReactions: ReactionType[];

@ApiProperty()
numReactions: NumReaction[];
}

export class Rec {
@ApiProperty()
id: string;
Expand All @@ -22,4 +40,7 @@ export class Rec {

@ApiProperty()
article: Article;

@ApiProperty()
reactions: Reactions;
}
25 changes: 18 additions & 7 deletions apps/recnet-api/src/modules/rec/rec.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ import {
ApiTags,
} from "@nestjs/swagger";

import { Auth } from "@recnet-api/utils/auth/auth.decorator";
import { AuthUser } from "@recnet-api/utils/auth/auth.type";
import { User } from "@recnet-api/utils/auth/auth.user.decorator";
import { Auth, AuthOptional } from "@recnet-api/utils/auth/auth.decorator";
import { AuthUser, AuthOptionalUser } from "@recnet-api/utils/auth/auth.type";
import { User, UserOptional } from "@recnet-api/utils/auth/auth.user.decorator";
import { RecnetExceptionFilter } from "@recnet-api/utils/filters/recnet.exception.filter";
import {
ZodValidationBodyPipe,
Expand Down Expand Up @@ -63,10 +63,16 @@ export class RecController {
summary: "Get a single rec",
description: "Get a single rec by id.",
})
@ApiBearerAuth()
@ApiOkResponse({ type: GetRecResponse })
@Get("rec/:id")
public async getRec(@Param("id") id: string): Promise<GetRecResponse> {
return this.recService.getRec(id);
@AuthOptional()
public async getRec(
@Param("id") id: string,
@UserOptional() authUser: AuthOptionalUser
): Promise<GetRecResponse> {
const authUserId = authUser?.userId ?? null;
return this.recService.getRec(id, authUserId);
}

@ApiOperation({
Expand All @@ -76,13 +82,18 @@ export class RecController {
@ApiOkResponse({ type: GetRecsResponse })
@ApiBearerAuth()
@Get()
@AuthOptional()
@UsePipes(new ZodValidationQueryPipe(getRecsParamsSchema))
public async getRecs(@Query() dto: QueryRecsDto): Promise<GetRecsResponse> {
public async getRecs(
@Query() dto: QueryRecsDto,
@UserOptional() authUser: AuthOptionalUser
): Promise<GetRecsResponse> {
const { page, pageSize, userId } = dto;
const authUserId = authUser?.userId ?? null;

// Get the Recs to current date to avoid upcoming rec from showing in a user's profile page
const to = new Date();
return this.recService.getRecs(page, pageSize, userId, to);
return this.recService.getRecs(page, pageSize, userId, to, authUserId);
}

@ApiOperation({
Expand Down
40 changes: 17 additions & 23 deletions apps/recnet-api/src/modules/rec/rec.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { ErrorCode } from "@recnet-api/utils/error/recnet.error.const";

import { getCutOff } from "@recnet/recnet-date-fns";

import { Rec } from "./entities/rec.entity";
import {
CreateRecResponse,
GetFeedsResponse,
Expand All @@ -24,8 +23,7 @@ import {
GetUpcomingRecResponse,
UpdateRecResponse,
} from "./rec.response";

import { transformUserPreview } from "../user/user.transformer";
import { transformRec } from "./rec.transformer";

@Injectable()
export class RecService {
Expand All @@ -38,16 +36,24 @@ export class RecService {
private readonly articleRepository: ArticleRepository
) {}

public async getRec(recId: string): Promise<GetRecResponse> {
public async getRec(
recId: string,
authUserId: string | null
): Promise<GetRecResponse> {
const dbRec = await this.recRepository.findRecById(recId);
return { rec: this.getRecFromDbRec(dbRec) };
return { rec: transformRec(dbRec, authUserId) };
}

/**
* @param userId is the user id of the user whose recs are being fetched
* @param authUserId is the user id of the user who is making the request
*/
public async getRecs(
page: number,
pageSize: number,
userId: string,
to: Date
to: Date,
authUserId: string | null
): Promise<GetRecsResponse> {
// validate if the user exists and is activated
const user = await this.userRepository.findUserById(userId);
Expand All @@ -64,7 +70,7 @@ export class RecService {
};
const recCount = await this.recRepository.countRecs(filter);
const dbRecs = await this.recRepository.findRecs(page, pageSize, filter);
const recs = this.getRecsFromDbRecs(dbRecs);
const recs = dbRecs.map((dbRec) => transformRec(dbRec, authUserId));

return {
hasNext: recs.length + getOffset(page, pageSize) < recCount,
Expand All @@ -89,7 +95,7 @@ export class RecService {
};
const recCount = await this.recRepository.countRecs(filter);
const dbRecs = await this.recRepository.findRecs(page, pageSize, filter);
const recs = this.getRecsFromDbRecs(dbRecs);
const recs = dbRecs.map((dbRec) => transformRec(dbRec, userId));

return {
hasNext: recs.length + getOffset(page, pageSize) < recCount,
Expand All @@ -105,7 +111,7 @@ export class RecService {
};
}
return {
rec: this.getRecFromDbRec(dbRec),
rec: transformRec(dbRec, userId),
};
}

Expand Down Expand Up @@ -146,7 +152,7 @@ export class RecService {
articleIdToConnect
);
return {
rec: this.getRecFromDbRec(newRec),
rec: transformRec(newRec, userId),
};
}

Expand Down Expand Up @@ -196,7 +202,7 @@ export class RecService {
);
}
return {
rec: this.getRecFromDbRec(updatedRec),
rec: transformRec(updatedRec, userId),
};
}

Expand Down Expand Up @@ -240,18 +246,6 @@ export class RecService {
);
}

private getRecsFromDbRecs(dbRec: DbRec[]): Rec[] {
return dbRec.map(this.getRecFromDbRec);
}

private getRecFromDbRec(dbRec: DbRec): Rec {
return {
...dbRec,
cutoff: dbRec.cutoff.toISOString(),
user: transformUserPreview(dbRec.user),
};
}

/**
* @param article - CreateArticleInput | null
* @param articleId - string | null
Expand Down
46 changes: 46 additions & 0 deletions apps/recnet-api/src/modules/rec/rec.transformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { ReactionType } from "@prisma/client";

import {
Rec as DbRec,
RecReaction as DbRecReaction,
} from "@recnet-api/database/repository/rec.repository.type";
import { transformUserPreview } from "@recnet-api/modules/user/user.transformer";

import { Rec } from "./entities/rec.entity";

export const transformRec = (
dbRec: DbRec,
authUserId: string | null = null
): Rec => {
let selfReactions: ReactionType[] = [];
if (authUserId) {
selfReactions = dbRec.reactions
.filter((reaction) => reaction.userId == authUserId)
.map((reaction) => reaction.reaction);
}

const reactionCounts = dbRec.reactions.reduce(
(acc: Record<ReactionType, number>, reaction: DbRecReaction) => {
if (!acc[reaction.reaction]) {
acc[reaction.reaction] = 0;
}
acc[reaction.reaction] += 1;
return acc;
},
{} as Record<ReactionType, number>
);
const numReactions = Object.keys(reactionCounts).map((reactionType) => ({
type: reactionType as ReactionType,
count: reactionCounts[reactionType as ReactionType],
}));

return {
...dbRec,
cutoff: dbRec.cutoff.toISOString(),
user: transformUserPreview(dbRec.user),
reactions: {
selfReactions,
numReactions,
},
};
};
13 changes: 11 additions & 2 deletions apps/recnet-api/src/utils/auth/auth.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,21 @@ export const Auth = (
} = {}
) => {
const { allowNonActivated = false, allowedRoles = ["USER", "ADMIN"] } = opts;
const isOptional = false;

return applyDecorators(
UseGuards(new AuthGuard(verifyRecnetJwt)),
UseGuards(AuthGuard(verifyRecnetJwt, isOptional)),
UseGuards(ActivatedGuard(allowNonActivated)),
UseGuards(RoleGuard(allowedRoles))
);
};

export const AuthFirebase = () => UseGuards(new AuthGuard(verifyFirebaseJwt));
export const AuthOptional = () => {
const isOptional = true;
return UseGuards(AuthGuard(verifyRecnetJwt, isOptional));
};

export const AuthFirebase = () => {
const isOptional = false;
return UseGuards(AuthGuard(verifyFirebaseJwt, isOptional));
};
Loading
Loading