mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-06 22:07:58 +03:00
Support manual de-orphaning.
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user