Skip to content

Commit

Permalink
impr: use tsrest for quotes endpoints (@fehmer) (#5802)
Browse files Browse the repository at this point in the history
!nuf
  • Loading branch information
fehmer authored Aug 29, 2024
1 parent 1ba4be3 commit 1804ebc
Show file tree
Hide file tree
Showing 29 changed files with 1,383 additions and 691 deletions.
894 changes: 894 additions & 0 deletions backend/__tests__/api/controllers/quotes.spec.ts

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions backend/__tests__/utils/misc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,9 @@ describe("Misc Utils", () => {
number: 1,
});
});
it("ignores null values", () => {
expect(misc.replaceObjectId(null)).toBeNull();
});
});

describe("replaceObjectIds", () => {
Expand Down
6 changes: 6 additions & 0 deletions backend/scripts/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ export function getOpenApi(): OpenAPIObject {
"x-displayName": "PSAs",
"x-public": "yes",
},
{
name: "quotes",
description: "Quote ratings and new quote submissions",
"x-displayName": "Quotes",
"x-public": "yes",
},
{
name: "admin",
description:
Expand Down
2 changes: 1 addition & 1 deletion backend/src/api/controllers/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export async function getPresets(
...preset,
uid: undefined,
}))
.map(replaceObjectId);
.map((it) => replaceObjectId(it));

return new MonkeyResponse2("Presets retrieved", data);
}
Expand Down
102 changes: 50 additions & 52 deletions backend/src/api/controllers/quote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,22 @@ import * as NewQuotesDAL from "../../dal/new-quotes";
import * as QuoteRatingsDAL from "../../dal/quote-ratings";
import MonkeyError from "../../utils/error";
import { verify } from "../../utils/captcha";
import { MonkeyResponse } from "../../utils/monkey-response";
import { MonkeyResponse2 } from "../../utils/monkey-response";
import { ObjectId } from "mongodb";
import { addLog } from "../../dal/logs";
import {
AddQuoteRatingRequest,
AddQuoteRequest,
ApproveQuoteRequest,
ApproveQuoteResponse,
GetQuoteRatingQuery,
GetQuoteRatingResponse,
GetQuotesResponse,
IsSubmissionEnabledResponse,
RejectQuoteRequest,
ReportQuoteRequest,
} from "@monkeytype/contracts/quotes";
import { replaceObjectId, replaceObjectIds } from "../../utils/misc";

async function verifyCaptcha(captcha: string): Promise<void> {
if (!(await verify(captcha))) {
Expand All @@ -17,49 +30,45 @@ async function verifyCaptcha(captcha: string): Promise<void> {
}

export async function getQuotes(
req: MonkeyTypes.Request
): Promise<MonkeyResponse> {
req: MonkeyTypes.Request2
): Promise<GetQuotesResponse> {
const { uid } = req.ctx.decodedToken;
const quoteMod: boolean | undefined | string = (
await getPartialUser(uid, "get quotes", ["quoteMod"])
).quoteMod;
let quoteModString: string;
if (quoteMod === true) {
quoteModString = "all";
} else if (quoteMod !== false && quoteMod !== undefined) {
quoteModString = quoteMod;
} else {
throw new MonkeyError(403, "You are not allowed to view submitted quotes");
}
const quoteMod = (await getPartialUser(uid, "get quotes", ["quoteMod"]))
.quoteMod;
const quoteModString = quoteMod === true ? "all" : (quoteMod as string);

const data = await NewQuotesDAL.get(quoteModString);
return new MonkeyResponse("Quote submissions retrieved", data);
return new MonkeyResponse2(
"Quote submissions retrieved",
replaceObjectIds(data)
);
}

export async function isSubmissionEnabled(
req: MonkeyTypes.Request
): Promise<MonkeyResponse> {
req: MonkeyTypes.Request2
): Promise<IsSubmissionEnabledResponse> {
const { submissionsEnabled } = req.ctx.configuration.quotes;
return new MonkeyResponse(
return new MonkeyResponse2(
"Quote submission " + (submissionsEnabled ? "enabled" : "disabled"),
{ isEnabled: submissionsEnabled }
);
}

export async function addQuote(
req: MonkeyTypes.Request
): Promise<MonkeyResponse> {
req: MonkeyTypes.Request2<undefined, AddQuoteRequest>
): Promise<MonkeyResponse2> {
const { uid } = req.ctx.decodedToken;
const { text, source, language, captcha } = req.body;

await verifyCaptcha(captcha);

await NewQuotesDAL.add(text, source, language, uid);
return new MonkeyResponse("Quote submission added");
return new MonkeyResponse2("Quote submission added", null);
}

export async function approveQuote(
req: MonkeyTypes.Request
): Promise<MonkeyResponse> {
req: MonkeyTypes.Request2<undefined, ApproveQuoteRequest>
): Promise<ApproveQuoteResponse> {
const { uid } = req.ctx.decodedToken;
const { quoteId, editText, editSource } = req.body;

Expand All @@ -72,46 +81,40 @@ export async function approveQuote(
const data = await NewQuotesDAL.approve(quoteId, editText, editSource, name);
void addLog("system_quote_approved", data, uid);

return new MonkeyResponse(data.message, data.quote);
return new MonkeyResponse2(data.message, data.quote);
}

export async function refuseQuote(
req: MonkeyTypes.Request
): Promise<MonkeyResponse> {
req: MonkeyTypes.Request2<undefined, RejectQuoteRequest>
): Promise<MonkeyResponse2> {
const { quoteId } = req.body;

await NewQuotesDAL.refuse(quoteId);
return new MonkeyResponse("Quote refused");
return new MonkeyResponse2("Quote refused", null);
}

export async function getRating(
req: MonkeyTypes.Request
): Promise<MonkeyResponse> {
req: MonkeyTypes.Request2<GetQuoteRatingQuery>
): Promise<GetQuoteRatingResponse> {
const { quoteId, language } = req.query;

const data = await QuoteRatingsDAL.get(
parseInt(quoteId as string, 10),
language as string
);
const data = await QuoteRatingsDAL.get(quoteId, language);

return new MonkeyResponse("Rating retrieved", data);
return new MonkeyResponse2("Rating retrieved", replaceObjectId(data));
}

export async function submitRating(
req: MonkeyTypes.Request
): Promise<MonkeyResponse> {
req: MonkeyTypes.Request2<undefined, AddQuoteRatingRequest>
): Promise<MonkeyResponse2> {
const { uid } = req.ctx.decodedToken;
const { quoteId, rating, language } = req.body;

const user = await getPartialUser(uid, "submit rating", ["quoteRatings"]);

const normalizedQuoteId = parseInt(quoteId as string, 10);
const normalizedRating = Math.round(parseInt(rating as string, 10));

const userQuoteRatings = user.quoteRatings ?? {};
const currentRating = userQuoteRatings[language]?.[normalizedQuoteId] ?? 0;
const currentRating = userQuoteRatings[language]?.[quoteId] ?? 0;

const newRating = normalizedRating - currentRating;
const newRating = rating - currentRating;
const shouldUpdateRating = currentRating !== 0;

await QuoteRatingsDAL.submit(
Expand All @@ -121,24 +124,19 @@ export async function submitRating(
shouldUpdateRating
);

_.setWith(
userQuoteRatings,
`[${language}][${normalizedQuoteId}]`,
normalizedRating,
Object
);
_.setWith(userQuoteRatings, `[${language}][${quoteId}]`, rating, Object);

await updateQuoteRatings(uid, userQuoteRatings);

const responseMessage = `Rating ${
shouldUpdateRating ? "updated" : "submitted"
}`;
return new MonkeyResponse(responseMessage);
return new MonkeyResponse2(responseMessage, null);
}

export async function reportQuote(
req: MonkeyTypes.Request
): Promise<MonkeyResponse> {
req: MonkeyTypes.Request2<undefined, ReportQuoteRequest>
): Promise<MonkeyResponse2> {
const { uid } = req.ctx.decodedToken;
const {
reporting: { maxReports, contentReportLimit },
Expand All @@ -156,10 +154,10 @@ export async function reportQuote(
uid,
contentId: `${quoteLanguage}-${quoteId}`,
reason,
comment,
comment: comment ?? "",
};

await ReportDAL.createReport(newReport, maxReports, contentReportLimit);

return new MonkeyResponse("Quote reported");
return new MonkeyResponse2("Quote reported", null);
}
2 changes: 1 addition & 1 deletion backend/src/api/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ const APP_START_TIME = Date.now();

const API_ROUTE_MAP = {
"/users": users,
"/quotes": quotes,
"/webhooks": webhooks,
"/docs": docs,
};
Expand All @@ -58,6 +57,7 @@ const router = s.router(contract, {
results,
configuration,
dev,
quotes,
});

export function addApiRoutes(app: Application): void {
Expand Down
Loading

0 comments on commit 1804ebc

Please sign in to comment.