Support manual de-orphaning.

This commit is contained in:
zkldi
2021-11-05 18:49:50 +00:00
parent 62addb5c9d
commit 5a88c45672
3 changed files with 126 additions and 2 deletions
@@ -1,6 +1,5 @@
// Various Errors that can occur during processing.
// @todo #116 Resolve circular dependency between converter-failures.ts and import-types/common/types.
import { ImportTypes } from "tachi-common";
import { ImportTypeDataMap, ImportTypeContextMap } from "../../import-types/common/types";
@@ -332,3 +332,70 @@ t.test("POST /api/v1/import/file", async (t) => {
t.end();
});
t.test("POST /api/v1/import/orphans", async (t) => {
const cookie = await CreateFakeAuthCookie(mockApi);
t.beforeEach(ResetDBState);
t.test("Should force a reprocessing of orphan scores.", async (t) => {
await db["orphan-scores"].insert([
{
userID: 1,
timeInserted: 1000,
orphanID: "asdf",
importType: "ir/direct-manual",
errMsg: "foo",
context: {
game: "iidx",
playtype: "SP",
service: "foo",
version: null,
},
data: {
score: 500,
lamp: "HARD CLEAR",
matchType: "songTitle",
identifier: "5.1.1.",
difficulty: "ANOTHER",
},
},
{
userID: 1,
timeInserted: 1000,
orphanID: "asdf2",
importType: "ir/direct-manual",
errMsg: "foo",
context: {
game: "iidx",
playtype: "SP",
service: "foo",
version: null,
},
data: {
score: 500,
lamp: "HARD CLEAR",
matchType: "songTitle",
identifier: "TITLE NOBODY WILL USE",
difficulty: "ANOTHER",
},
},
]);
const res = await mockApi.post("/api/v1/import/orphans").set("Cookie", cookie);
t.equal(res.statusCode, 200, "Should return 200.");
t.equal(res.body.body.success, 1, "Should successfully reprocess one orphan.");
t.equal(res.body.body.done, 2, "Should reprocess two orphans.");
t.equal(res.body.body.failed, 1, "Should fail in de-orphaning one orphan.");
const dbCount = await db["orphan-scores"].count({});
t.equal(dbCount, 1, "Should only leave one orphan-score in the database.");
t.end();
});
t.end();
});
@@ -1,7 +1,7 @@
import { Router } from "express";
import { APIImportTypes, FileUploadImportTypes, integer } from "tachi-common";
import Prudence from "prudence";
import { GetUserWithIDGuaranteed } from "utils/user";
import { FormatUserDoc, GetUserWithIDGuaranteed } from "utils/user";
import CreateLogCtx, { KtLogger } from "lib/logger/logger";
import prValidate from "server/middleware/prudence-validate";
import ScoreImportFatalError from "lib/score-import/framework/score-importing/score-import-error";
@@ -23,6 +23,8 @@ import { ParseFloSDVX } from "lib/score-import/import-types/api/flo-sdvx/parser"
import { ParseMinSDVX } from "lib/score-import/import-types/api/min-sdvx/parser";
import { ParseArcSDVX } from "lib/score-import/import-types/api/arc-sdvx/parser";
import { ParseArcIIDX } from "lib/score-import/import-types/api/arc-iidx/parser";
import db from "external/mongo/db";
import { ReprocessOrphan } from "lib/score-import/framework/orphans/orphans";
const logger = CreateLogCtx(__filename);
@@ -120,6 +122,62 @@ router.post(
}
);
/**
* Force Tachi to reprocess your orphanned scores. This is automatically done
* daily, but this endpoint allows users to speed that up.
*
* @name POST /api/v1/import/orphans
*/
router.post("/orphans", RequirePermissions("submit_score"), async (req, res) => {
const userDoc = await GetUserWithIDGuaranteed(req.session.tachi!.user.id);
logger.info(`User ${FormatUserDoc(userDoc)} forced an orphan sync.`);
const orphans = await db["orphan-scores"].find({
userID: userDoc.id,
});
// ScoreIDs are essentially userID dependent, so this is fine.
const blacklist = (await db["score-blacklist"].find({ userID: userDoc.id })).map(
(e) => e.scoreID
);
let done = 0;
let failed = 0;
let success = 0;
let removed = 0;
await Promise.all(
orphans.map((or) =>
ReprocessOrphan(or, blacklist, logger).then((r) => {
done++;
if (r === null) {
removed++;
} else if (r === false) {
failed++;
} else {
success++;
}
})
)
);
logger.info(`Finished attempting deorphaning.`);
logger.info(`Success: ${success} | Failed ${failed} | Removed ${removed}.`);
return res.status(200).json({
success: true,
description: `Reprocessed ${done} orphan scores.`,
body: {
done,
failed,
success,
removed,
},
});
});
/**
* Resolves the data from a file upload into an iterable,
* The appropriate processing function to map that iterable over,