diff options
author | Jack <[email protected]> | 2024-09-13 13:18:06 +0200 |
---|---|---|
committer | GitHub <[email protected]> | 2024-09-13 13:18:06 +0200 |
commit | 955eeae2a7659d9541fc4a54d1dff8a4ea433c40 (patch) | |
tree | ce25c239dd63c27fd4c5725291e38b0bdbe383e8 /backend | |
parent | e19b3e3e8b5f9fa1fb8a0e8e4c37abb28a63c4e2 (diff) | |
download | monkeytype-955eeae2a7659d9541fc4a54d1dff8a4ea433c40.tar.gz monkeytype-955eeae2a7659d9541fc4a54d1dff8a4ea433c40.zip |
refactor: enable no-unsafe-assignment rule (@miodec) (#5874)
Co-authored-by: Nad Alaba <[email protected]>
Co-authored-by: Christian Fehmer <[email protected]>
Co-authored-by: Igor Bedesqui <[email protected]>
Co-authored-by: amarnathsama <[email protected]>
Diffstat (limited to 'backend')
-rw-r--r-- | backend/__tests__/utils/pb.spec.ts | 96 | ||||
-rw-r--r-- | backend/src/api/controllers/user.ts | 2 | ||||
-rw-r--r-- | backend/src/api/routes/swagger.ts | 2 | ||||
-rw-r--r-- | backend/src/api/ts-rest-adapter.ts | 2 | ||||
-rw-r--r-- | backend/src/dal/new-quotes.ts | 26 | ||||
-rw-r--r-- | backend/src/dal/result.ts | 2 | ||||
-rw-r--r-- | backend/src/init/configuration.ts | 6 | ||||
-rw-r--r-- | backend/src/init/email-client.ts | 8 | ||||
-rw-r--r-- | backend/src/init/firebase-admin.ts | 6 | ||||
-rw-r--r-- | backend/src/middlewares/auth.ts | 16 | ||||
-rw-r--r-- | backend/src/middlewares/configuration.ts | 5 | ||||
-rw-r--r-- | backend/src/services/weekly-xp-leaderboard.ts | 12 | ||||
-rw-r--r-- | backend/src/utils/daily-leaderboards.ts | 6 | ||||
-rw-r--r-- | backend/src/utils/misc.ts | 2 | ||||
-rw-r--r-- | backend/src/utils/pb.ts | 39 | ||||
-rw-r--r-- | backend/src/workers/email-worker.ts | 13 | ||||
-rw-r--r-- | backend/src/workers/later-worker.ts | 4 |
17 files changed, 176 insertions, 71 deletions
diff --git a/backend/__tests__/utils/pb.spec.ts b/backend/__tests__/utils/pb.spec.ts index 28c9f77f3..79c80693d 100644 --- a/backend/__tests__/utils/pb.spec.ts +++ b/backend/__tests__/utils/pb.spec.ts @@ -57,10 +57,15 @@ describe("Pb Utils", () => { mode2: "15", } as unknown as Result<Mode>; - const run = pb.checkAndUpdatePb(userPbs, undefined, result); + const run = pb.checkAndUpdatePb( + userPbs, + {} as MonkeyTypes.LbPersonalBests, + result + ); expect(run.isPb).toBe(true); expect(run.personalBests?.["time"]?.["15"]?.[0]).not.toBe(undefined); + expect(run.lbPersonalBests).not.toBe({}); }); it("should not override default pb when saving numbers test", () => { const userPbs: PersonalBests = { @@ -111,4 +116,93 @@ describe("Pb Utils", () => { ); }); }); + describe("updateLeaderboardPersonalBests", () => { + const userPbs: PersonalBests = { + time: { + "15": [ + { + acc: 100, + consistency: 100, + difficulty: "normal", + lazyMode: false, + language: "english", + numbers: false, + punctuation: false, + raw: 100, + timestamp: 0, + wpm: 100, + }, + { + acc: 100, + consistency: 100, + difficulty: "normal", + lazyMode: false, + language: "spanish", + numbers: false, + punctuation: false, + raw: 100, + timestamp: 0, + wpm: 100, + }, + ], + }, + words: {}, + custom: {}, + quote: {}, + zen: {}, + }; + it("should update leaderboard personal bests if they dont exist or the structure is incomplete", () => { + const lbpbstartingvalues = [ + undefined, + {}, + { time: {} }, + { time: { "15": {} } }, + { time: { "15": { english: {} } } }, + ]; + + const result15 = { + mode: "time", + mode2: "15", + } as unknown as Result<Mode>; + + for (const lbPb of lbpbstartingvalues) { + const lbPbPb = pb.updateLeaderboardPersonalBests( + userPbs, + _.cloneDeep(lbPb) as MonkeyTypes.LbPersonalBests, + result15 + ); + + expect(lbPbPb).toEqual({ + time: { + "15": { + english: { + acc: 100, + consistency: 100, + difficulty: "normal", + lazyMode: false, + language: "english", + numbers: false, + punctuation: false, + raw: 100, + timestamp: 0, + wpm: 100, + }, + spanish: { + acc: 100, + consistency: 100, + difficulty: "normal", + lazyMode: false, + language: "spanish", + numbers: false, + punctuation: false, + raw: 100, + timestamp: 0, + wpm: 100, + }, + }, + }, + }); + } + }); + }); }); diff --git a/backend/src/api/controllers/user.ts b/backend/src/api/controllers/user.ts index d4b38a291..33f3e4268 100644 --- a/backend/src/api/controllers/user.ts +++ b/backend/src/api/controllers/user.ts @@ -198,7 +198,7 @@ export async function sendVerificationEmail( JSON.stringify({ decodedTokenEmail: email, userInfoEmail: userInfo.email, - stack: e.stack, + stack: e.stack as unknown, }), userInfo.uid ); diff --git a/backend/src/api/routes/swagger.ts b/backend/src/api/routes/swagger.ts index 0bd008129..ba4b90c5a 100644 --- a/backend/src/api/routes/swagger.ts +++ b/backend/src/api/routes/swagger.ts @@ -8,7 +8,7 @@ function addSwaggerMiddlewares(app: Application): void { const openApiSpec = __dirname + "/../../static/api/openapi.json"; let spec = {}; try { - spec = JSON.parse(readFileSync(openApiSpec, "utf8")); + spec = JSON.parse(readFileSync(openApiSpec, "utf8")) as string; } catch (err) { Logger.warning( `Cannot read openApi specification from ${openApiSpec}. Swagger stats will not fully work.` diff --git a/backend/src/api/ts-rest-adapter.ts b/backend/src/api/ts-rest-adapter.ts index d86575651..a48313473 100644 --- a/backend/src/api/ts-rest-adapter.ts +++ b/backend/src/api/ts-rest-adapter.ts @@ -22,7 +22,7 @@ export function callController< query: all.query as TQuery, params: all.params as TParams, raw: all.req, - ctx: all.req["ctx"], + ctx: all.req["ctx"] as MonkeyTypes.Context, }; const result = await handler(req); diff --git a/backend/src/dal/new-quotes.ts b/backend/src/dal/new-quotes.ts index 357cce367..8814cebf6 100644 --- a/backend/src/dal/new-quotes.ts +++ b/backend/src/dal/new-quotes.ts @@ -8,6 +8,20 @@ import MonkeyError from "../utils/error"; import { compareTwoStrings } from "string-similarity"; import { ApproveQuote, Quote } from "@monkeytype/contracts/schemas/quotes"; +type JsonQuote = { + text: string; + britishText?: string; + source: string; + length: number; + id: number; +}; + +type QuoteData = { + language: string; + quotes: JsonQuote[]; + groups: [number, number][]; +}; + const PATH_TO_REPO = "../../../../monkeytype-new-quotes"; let git; @@ -71,11 +85,11 @@ export async function add( let similarityScore = -1; if (existsSync(fileDir)) { const quoteFile = await readFile(fileDir); - const quoteFileJSON = JSON.parse(quoteFile.toString()); + const quoteFileJSON = JSON.parse(quoteFile.toString()) as QuoteData; quoteFileJSON.quotes.every((old) => { - if (compareTwoStrings(old.text as string, quote.text) > 0.9) { + if (compareTwoStrings(old.text, quote.text) > 0.9) { duplicateId = old.id; - similarityScore = compareTwoStrings(old.text as string, quote.text); + similarityScore = compareTwoStrings(old.text, quote.text); return false; } return true; @@ -155,9 +169,9 @@ export async function approve( await git.pull("upstream", "master"); if (existsSync(fileDir)) { const quoteFile = await readFile(fileDir); - const quoteObject = JSON.parse(quoteFile.toString()); + const quoteObject = JSON.parse(quoteFile.toString()) as QuoteData; quoteObject.quotes.every((old) => { - if (compareTwoStrings(old.text as string, quote.text) > 0.8) { + if (compareTwoStrings(old.text, quote.text) > 0.8) { throw new MonkeyError(409, "Duplicate quote"); } }); @@ -168,7 +182,7 @@ export async function approve( } }); quote.id = maxid + 1; - quoteObject.quotes.push(quote); + quoteObject.quotes.push(quote as JsonQuote); writeFileSync(fileDir, JSON.stringify(quoteObject, null, 2)); message = `Added quote to ${language}.json.`; } else { diff --git a/backend/src/dal/result.ts b/backend/src/dal/result.ts index 57946bf7e..dd1d80364 100644 --- a/backend/src/dal/result.ts +++ b/backend/src/dal/result.ts @@ -87,7 +87,7 @@ export async function getLastResult( export async function getResultByTimestamp( uid: string, - timestamp + timestamp: number ): Promise<MonkeyTypes.DBResult | null> { return await getResultCollection().findOne({ uid, timestamp }); } diff --git a/backend/src/init/configuration.ts b/backend/src/init/configuration.ts index 662a2f191..6a59d4df1 100644 --- a/backend/src/init/configuration.ts +++ b/backend/src/init/configuration.ts @@ -25,14 +25,14 @@ function mergeConfigurations( const commonKeys = _.intersection(_.keys(base), _.keys(source)); commonKeys.forEach((key) => { - const baseValue = base[key]; - const sourceValue = source[key]; + const baseValue = base[key] as object; + const sourceValue = source[key] as object; const isBaseValueObject = _.isPlainObject(baseValue); const isSourceValueObject = _.isPlainObject(sourceValue); if (isBaseValueObject && isSourceValueObject) { - merge(baseValue as object, sourceValue as object); + merge(baseValue, sourceValue); } else if (identity(baseValue) === identity(sourceValue)) { base[key] = sourceValue; } diff --git a/backend/src/init/email-client.ts b/backend/src/init/email-client.ts index 3ccaa2a2e..3c4874392 100644 --- a/backend/src/init/email-client.ts +++ b/backend/src/init/email-client.ts @@ -103,14 +103,16 @@ export async function sendEmail( html: template, }; - let result; + type Result = { response: string; accepted: string[] }; + + let result: Result; try { - result = await transporter.sendMail(mailOptions); + result = (await transporter.sendMail(mailOptions)) as Result; } catch (e) { recordEmail(templateName, "fail"); return { success: false, - message: e.message, + message: e.message as string, }; } diff --git a/backend/src/init/firebase-admin.ts b/backend/src/init/firebase-admin.ts index 1e1687658..d380eab31 100644 --- a/backend/src/init/firebase-admin.ts +++ b/backend/src/init/firebase-admin.ts @@ -29,11 +29,9 @@ export function init(): void { encoding: "utf8", flag: "r", }) - ); + ) as ServiceAccount; admin.initializeApp({ - credential: admin.credential.cert( - serviceAccount as unknown as ServiceAccount - ), + credential: admin.credential.cert(serviceAccount), }); Logger.success("Firebase app initialized"); } diff --git a/backend/src/middlewares/auth.ts b/backend/src/middlewares/auth.ts index 15d1fab99..b2aff30c8 100644 --- a/backend/src/middlewares/auth.ts +++ b/backend/src/middlewares/auth.ts @@ -188,33 +188,27 @@ async function authenticateWithBearerToken( email: decodedToken.email ?? "", }; } catch (error) { - const errorCode = error?.errorInfo?.code; + const errorCode = error?.errorInfo?.code as string | undefined; - if (errorCode?.includes("auth/id-token-expired") as boolean | undefined) { + if (errorCode?.includes("auth/id-token-expired")) { throw new MonkeyError( 401, "Token expired - please login again", "authenticateWithBearerToken" ); - } else if ( - errorCode?.includes("auth/id-token-revoked") as boolean | undefined - ) { + } else if (errorCode?.includes("auth/id-token-revoked")) { throw new MonkeyError( 401, "Token revoked - please login again", "authenticateWithBearerToken" ); - } else if ( - errorCode?.includes("auth/user-not-found") as boolean | undefined - ) { + } else if (errorCode?.includes("auth/user-not-found")) { throw new MonkeyError( 404, "User not found", "authenticateWithBearerToken" ); - } else if ( - errorCode?.includes("auth/argument-error") as boolean | undefined - ) { + } else if (errorCode?.includes("auth/argument-error")) { throw new MonkeyError( 400, "Incorrect Bearer token format", diff --git a/backend/src/middlewares/configuration.ts b/backend/src/middlewares/configuration.ts index 256618efa..07e8f233a 100644 --- a/backend/src/middlewares/configuration.ts +++ b/backend/src/middlewares/configuration.ts @@ -53,11 +53,12 @@ function getValue( path: ConfigurationPath ): boolean { const keys = (path as string).split("."); - let result = configuration; + let result: unknown = configuration; for (const key of keys) { - if (result === undefined || result === null) + if (result === undefined || result === null) { throw new MonkeyError(500, `Invalid configuration path: "${path}"`); + } result = result[key]; } diff --git a/backend/src/services/weekly-xp-leaderboard.ts b/backend/src/services/weekly-xp-leaderboard.ts index e52b56ad8..2933d7f4a 100644 --- a/backend/src/services/weekly-xp-leaderboard.ts +++ b/backend/src/services/weekly-xp-leaderboard.ts @@ -129,14 +129,14 @@ export class WeeklyXpLeaderboard { this.getThisWeeksXpLeaderboardKeys(); // @ts-expect-error - const [results, scores]: string[][] = await connection.getResults( + const [results, scores] = (await connection.getResults( 2, // How many of the arguments are redis keys (https://redis.io/docs/manual/programmability/lua-api/) weeklyXpLeaderboardScoresKey, weeklyXpLeaderboardResultsKey, minRank, maxRank, "true" - ); + )) as string[][]; if (results === undefined) { throw new Error( @@ -183,13 +183,17 @@ export class WeeklyXpLeaderboard { // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error // @ts-ignore - const [[, rank], [, totalXp], [, count], [, result]] = await connection + const [[, rank], [, totalXp], [, count], [, result]] = (await connection .multi() .zrevrank(weeklyXpLeaderboardScoresKey, uid) .zscore(weeklyXpLeaderboardScoresKey, uid) .zcard(weeklyXpLeaderboardScoresKey) .hget(weeklyXpLeaderboardResultsKey, uid) - .exec(); + .exec()) as [ + [null, number | null], + [null, string | null], + [null, number | null] + ]; if (rank === null) { return null; diff --git a/backend/src/utils/daily-leaderboards.ts b/backend/src/utils/daily-leaderboards.ts index b99fed503..e77bfe401 100644 --- a/backend/src/utils/daily-leaderboards.ts +++ b/backend/src/utils/daily-leaderboards.ts @@ -121,14 +121,14 @@ export class DailyLeaderboard { this.getTodaysLeaderboardKeys(); // @ts-expect-error - const [results]: string[][] = await connection.getResults( + const [results] = (await connection.getResults( 2, leaderboardScoresKey, leaderboardResultsKey, minRank, maxRank, "false" - ); + )) as string[][]; if (results === undefined) { throw new Error( @@ -198,7 +198,7 @@ export class DailyLeaderboard { count: count ?? 0, rank: rank + 1, entry: { - ...JSON.parse(result ?? "null"), + ...(JSON.parse(result ?? "null") as LeaderboardEntry), }, }; } diff --git a/backend/src/utils/misc.ts b/backend/src/utils/misc.ts index 985784642..1db2bcafc 100644 --- a/backend/src/utils/misc.ts +++ b/backend/src/utils/misc.ts @@ -285,7 +285,7 @@ export function formatSeconds( } export function intersect<T>(a: T[], b: T[], removeDuplicates = false): T[] { - let t; + let t: T[]; // eslint-disable-next-line @typescript-eslint/no-unused-expressions if (b.length > a.length) (t = b), (b = a), (a = t); // indexOf to loop over shorter const filtered = a.filter(function (e) { diff --git a/backend/src/utils/pb.ts b/backend/src/utils/pb.ts index b46f5e0f2..383daa7cb 100644 --- a/backend/src/utils/pb.ts +++ b/backend/src/utils/pb.ts @@ -59,7 +59,14 @@ export function checkAndUpdatePb( } if (!_.isNil(lbPersonalBests)) { - updateLeaderboardPersonalBests(userPb, lbPersonalBests, result); + const newLbPb = updateLeaderboardPersonalBests( + userPb, + lbPersonalBests, + result + ); + if (newLbPb !== null) { + lbPersonalBests = newLbPb; + } } return { @@ -165,26 +172,20 @@ function buildPersonalBest(result: Result): PersonalBest { }; } -function updateLeaderboardPersonalBests( +export function updateLeaderboardPersonalBests( userPersonalBests: PersonalBests, lbPersonalBests: MonkeyTypes.LbPersonalBests, result: Result -): void { +): MonkeyTypes.LbPersonalBests | null { if (!shouldUpdateLeaderboardPersonalBests(result)) { - return; + return null; } - const mode = result.mode; const mode2 = result.mode2; - - lbPersonalBests[mode] = lbPersonalBests[mode] ?? {}; - const lbMode2 = lbPersonalBests[mode][mode2] as MonkeyTypes.LbPersonalBests; - if (lbMode2 === undefined || Array.isArray(lbMode2)) { - lbPersonalBests[mode][mode2] = {}; - } - + const lbPb = lbPersonalBests ?? {}; + lbPb[mode] ??= {}; + lbPb[mode][mode2] ??= {}; const bestForEveryLanguage = {}; - userPersonalBests[mode][mode2].forEach((pb: PersonalBest) => { const language = pb.language; if ( @@ -194,18 +195,18 @@ function updateLeaderboardPersonalBests( bestForEveryLanguage[language] = pb; } }); - _.each(bestForEveryLanguage, (pb: PersonalBest, language: string) => { - const languageDoesNotExist = - lbPersonalBests[mode][mode2][language] === undefined; - + const languageDoesNotExist = lbPb[mode][mode2][language] === undefined; + const languageIsEmpty = _.isEmpty(lbPb[mode][mode2][language]); if ( languageDoesNotExist || - lbPersonalBests[mode][mode2][language].wpm < pb.wpm + languageIsEmpty || + lbPb[mode][mode2][language].wpm < pb.wpm ) { - lbPersonalBests[mode][mode2][language] = pb; + lbPb[mode][mode2][language] = pb; } }); + return lbPb; } function shouldUpdateLeaderboardPersonalBests(result: Result): boolean { diff --git a/backend/src/workers/email-worker.ts b/backend/src/workers/email-worker.ts index a6d70db56..4cf03915c 100644 --- a/backend/src/workers/email-worker.ts +++ b/backend/src/workers/email-worker.ts @@ -2,18 +2,15 @@ import _ from "lodash"; import IORedis from "ioredis"; import { Worker, Job, type ConnectionOptions } from "bullmq"; import Logger from "../utils/logger"; -import EmailQueue, { - type EmailTaskContexts, - type EmailType, -} from "../queues/email-queue"; +import EmailQueue, { EmailTask, type EmailType } from "../queues/email-queue"; import { sendEmail } from "../init/email-client"; import { recordTimeToCompleteJob } from "../utils/prometheus"; import { addLog } from "../dal/logs"; -async function jobHandler(job: Job): Promise<void> { - const type: EmailType = job.data.type; - const email: string = job.data.email; - const ctx: EmailTaskContexts[typeof type] = job.data.ctx; +async function jobHandler(job: Job<EmailTask<EmailType>>): Promise<void> { + const type = job.data.type; + const email = job.data.email; + const ctx = job.data.ctx; Logger.info(`Starting job: ${type}`); diff --git a/backend/src/workers/later-worker.ts b/backend/src/workers/later-worker.ts index 2621cd53c..a669a3b3e 100644 --- a/backend/src/workers/later-worker.ts +++ b/backend/src/workers/later-worker.ts @@ -180,8 +180,8 @@ async function handleWeeklyXpLeaderboardResults( await addToInboxBulk(mailEntries, inboxConfig); } -async function jobHandler(job: Job): Promise<void> { - const { taskName, ctx }: LaterTask<LaterTaskType> = job.data; +async function jobHandler(job: Job<LaterTask<LaterTaskType>>): Promise<void> { + const { taskName, ctx } = job.data; Logger.info(`Starting job: ${taskName}`); |