diff --git a/src/app.ts b/src/app.ts index 827f767..5e97f05 100644 --- a/src/app.ts +++ b/src/app.ts @@ -7,7 +7,7 @@ import {postWarning} from "./routes/postWarning"; import {getIsUserVIP} from "./routes/getIsUserVIP"; import {deleteLockCategoriesEndpoint} from "./routes/deleteLockCategories"; import {postLockCategories} from "./routes/postLockCategories"; -import {getUserInfo} from "./routes/getUserInfo"; +import {endpoint as getUserInfo} from "./routes/getUserInfo"; import {getDaysSavedFormatted} from "./routes/getDaysSavedFormatted"; import {getTotalStats} from "./routes/getTotalStats"; import {getTopUsers} from "./routes/getTopUsers"; diff --git a/src/routes/getUserInfo.ts b/src/routes/getUserInfo.ts index ac2b925..b42d58a 100644 --- a/src/routes/getUserInfo.ts +++ b/src/routes/getUserInfo.ts @@ -6,12 +6,15 @@ import {Logger} from "../utils/logger"; import { HashedUserID, UserID } from "../types/user.model"; import { getReputation } from "../utils/reputation"; import { SegmentUUID } from "../types/segments.model"; +import {config} from "../config"; +const maxRewardTime = config.maxRewardTimePerSegmentInSeconds; async function dbGetSubmittedSegmentSummary(userID: HashedUserID): Promise<{ minutesSaved: number, segmentCount: number }> { try { - const row = await db.prepare("get", `SELECT SUM((("endTime" - "startTime") / 60) * "views") as "minutesSaved", - count(*) as "segmentCount" FROM "sponsorTimes" - WHERE "userID" = ? AND "votes" > -2 AND "shadowHidden" != 1`, [userID]); + const row = await db.prepare("get", + `SELECT SUM(((CASE WHEN "endTime" - "startTime" > ? THEN ? ELSE "endTime" - "startTime" END) / 60) * "views") as "minutesSaved", + count(*) as "segmentCount" FROM "sponsorTimes" + WHERE "userID" = ? AND "votes" > -2 AND "shadowHidden" != 1`, [maxRewardTime, maxRewardTime, userID]); if (row.minutesSaved != null) { return { minutesSaved: row.minutesSaved, @@ -134,7 +137,7 @@ const dbGetValue = async (userID: HashedUserID, property: string): Promise { +async function getUserInfo(req: Request, res: Response): Promise { const userID = req.query.userID as UserID; const hashedUserID: HashedUserID = userID ? getHash(userID) : req.query.publicUserID as HashedUserID; const defaultProperties: string[] = ["userID", "userName", "minutesSaved", "segmentCount", "ignoredSegmentCount", @@ -149,7 +152,7 @@ export async function getUserInfo(req: Request, res: Response): Promise allProperties.includes(param)); @@ -165,15 +168,21 @@ export async function getUserInfo(req: Request, res: Response): Promise; - if (segmentsSummary) { - for (const property of paramValues) { - responseObj[property] = await dbGetValue(hashedUserID, property); - } - // add minutesSaved and segmentCount after to avoid getting overwritten - if (paramValues.includes("minutesSaved")) { responseObj["minutesSaved"] = segmentsSummary.minutesSaved; } - if (paramValues.includes("segmentCount")) responseObj["segmentCount"] = segmentsSummary.segmentCount; - res.send(responseObj); - } else { - return res.sendStatus(404); + for (const property of paramValues) { + responseObj[property] = await dbGetValue(hashedUserID, property); } + // add minutesSaved and segmentCount after to avoid getting overwritten + if (paramValues.includes("minutesSaved")) { responseObj["minutesSaved"] = segmentsSummary.minutesSaved; } + if (paramValues.includes("segmentCount")) responseObj["segmentCount"] = segmentsSummary.segmentCount; + return res.send(responseObj); } + +export async function endpoint(req: Request, res: Response): Promise { + try { + return await getUserInfo(req, res); + } catch (err) { + if (err instanceof SyntaxError) { // catch JSON.parse error + return res.status(400).send("Invalid values JSON"); + } else return res.sendStatus(500); + } +} \ No newline at end of file diff --git a/test/cases/getSegmentInfo.ts b/test/cases/getSegmentInfo.ts index 20545f4..b398665 100644 --- a/test/cases/getSegmentInfo.ts +++ b/test/cases/getSegmentInfo.ts @@ -146,7 +146,7 @@ describe("getSegmentInfo", () => { }); it("Should be able to retreive hidden segment", (done: Done) => { - fetch(`${getbaseURL() }/api/segmentInfo?UUID=${hiddenID}`) + fetch(`${getbaseURL()}/api/segmentInfo?UUID=${hiddenID}`) .then(async res => { assert.strictEqual(res.status, 200); const data = await res.json(); diff --git a/test/cases/getUserInfo.ts b/test/cases/getUserInfo.ts index 8d9ff79..e0a4d65 100644 --- a/test/cases/getUserInfo.ts +++ b/test/cases/getUserInfo.ts @@ -18,6 +18,7 @@ describe("getUserInfo", () => { await db.prepare("run", sponsorTimesQuery, ["zzzxxxyyy", 1, 11, 2, "uuid000006", getHash("getuserinfo_user_02"), 6, 10, "sponsor", 0]); await db.prepare("run", sponsorTimesQuery, ["xxxyyyzzz", 1, 11, 2, "uuid000007", getHash("getuserinfo_user_02"), 7, 10, "sponsor", 1]); await db.prepare("run", sponsorTimesQuery, ["xxxyyyzzz", 1, 11, 2, "uuid000008", getHash("getuserinfo_user_02"), 8, 10, "sponsor", 1]); + await db.prepare("run", sponsorTimesQuery, ["xxxyyyzzz", 0, 36000, 2, "uuid000009", getHash("getuserinfo_user_03"), 8, 2, "sponsor", 0]); const insertWarningQuery = 'INSERT INTO warnings ("userID", "issueTime", "issuerUserID", "enabled", "reason") VALUES (?, ?, ?, ?, ?)'; await db.prepare("run", insertWarningQuery, [getHash("getuserinfo_warning_0"), 10, "getuserinfo_vip", 1, "warning0-0"]); @@ -250,7 +251,7 @@ describe("getUserInfo", () => { .catch(err => done(err)); }); - it("Should get ban data for unbanned user (only appears when specifically requested)", (done: Done) => { + it("Should get ban data for unbanned user (only appears when specifically requested)", (done: Done) => { fetch(`${getbaseURL()}/api/userInfo?userID=getuserinfo_notban_01&value=banned`) .then(async res => { assert.strictEqual(res.status, 200); @@ -263,4 +264,60 @@ describe("getUserInfo", () => { }) .catch(err => done(err)); }); + + it("Should throw 400 on bad json in values", (done: Done) => { + fetch(`${getbaseURL()}/api/userInfo?userID=x&values=[userID]`) + .then(async res => { + assert.strictEqual(res.status, 400); + done(); // pass + }) + .catch(err => done(err)); + }); + + it("Should return 200 on userID not found", (done: Done) => { + fetch(`${getbaseURL()}/api/userInfo?userID=notused-userid`) + .then(async res => { + assert.strictEqual(res.status, 200); + const data = await res.json(); + const expected = { + minutesSaved: 0, + segmentCount: 0, + ignoredSegmentCount: 0, + viewCount: 0, + ignoredViewCount: 0, + warnings: 0, + warningReason: "", + reputation: 0, + vip: false, + }; + assert.ok(partialDeepEquals(data, expected)); + done(); // pass + }) + .catch(err => done(err)); + }); + + it("Should only count long segments as 10 minutes", (done: Done) => { + fetch(`${getbaseURL()}/api/userInfo?userID=getuserinfo_user_03`) + .then(async res => { + assert.strictEqual(res.status, 200); + const expected = { + userName: "807e0a5d0a62c9c4365fae3d403e4618a3226f231314a898fa1555a0e55eab9e", + userID: "807e0a5d0a62c9c4365fae3d403e4618a3226f231314a898fa1555a0e55eab9e", + minutesSaved: 20, + viewCount: 2, + ignoredViewCount: 0, + segmentCount: 1, + ignoredSegmentCount: 0, + reputation: 0, + lastSegmentID: "uuid000009", + vip: false, + warnings: 0, + warningReason: "" + }; + const data = await res.json(); + assert.deepStrictEqual(data, expected); + done(); + }) + .catch(err => done(err)); + }); });