Rename userGoal to goalSub, same for milestones

This commit is contained in:
zkldi
2022-03-29 15:14:39 +01:00
parent 533d043ef4
commit 5860f4e499
10 changed files with 113 additions and 111 deletions
@@ -153,7 +153,7 @@ t.test("#GetRelevantGoals", (t) => {
const res = await GetRelevantGoals("iidx", 1, chartIDs, logger);
t.equal(res.goals.length, 5, "Should correctly resolve to 5 goals.");
t.equal(res.userGoalsMap.size, 5, "Should also return 5 userGoals.");
t.equal(res.goalSubsMap.size, 5, "Should also return 5 goalSubs.");
t.end();
}
@@ -259,12 +259,12 @@ t.test("#UpdateGoalsForUser", (t) => {
const goal = deepmerge(baseGoalDocument, { criteria: { value: 2 } });
await db.goals.insert(goal);
const userGoal = deepmerge(baseUserGoalDocument, {
const goalSub = deepmerge(baseUserGoalDocument, {
outOf: 2,
outOfHuman: "2",
}) as unknown as UserGoalDocument;
await db["goal-subs"].insert(userGoal);
await db["goal-subs"].insert(goalSub);
// we dont delete _id here because updategoalsforuser
// depends on usergoal _id
@@ -272,7 +272,7 @@ t.test("#UpdateGoalsForUser", (t) => {
deepmerge(TestingIIDXSPScorePB, { scoreData: { score: 1 } })
);
const ugMap = new Map([["FAKE_GOAL_ID", userGoal]]);
const ugMap = new Map([["FAKE_GOAL_ID", goalSub]]);
const res = await UpdateGoalsForUser([goal], ugMap, 1, logger);
@@ -413,9 +413,9 @@ t.test("#ProcessGoal", (t) => {
await db["goal-subs"].bulkWrite([firstUpdate!.bwrite]);
const userGoal = await db["goal-subs"].findOne({ userID: 1, goalID: HC511Goal.goalID });
const goalSub = await db["goal-subs"].findOne({ userID: 1, goalID: HC511Goal.goalID });
const secondUpdate = await ProcessGoal(HC511Goal, userGoal!, 1, logger);
const secondUpdate = await ProcessGoal(HC511Goal, goalSub!, 1, logger);
t.equal(secondUpdate, undefined, "Should return undefined.");
@@ -13,7 +13,7 @@ export async function GetAndUpdateUsersGoals(
chartIDs: Set<string>,
logger: KtLogger
) {
const { goals, userGoalsMap } = await GetRelevantGoals(game, userID, chartIDs, logger);
const { goals, goalSubsMap } = await GetRelevantGoals(game, userID, chartIDs, logger);
if (!goals.length) {
// if we hit the below code with an empty array mongodb will flip out on the bulkwrite op
@@ -22,27 +22,27 @@ export async function GetAndUpdateUsersGoals(
logger.verbose(`Found ${goals.length} relevant goals.`);
return UpdateGoalsForUser(goals, userGoalsMap, userID, logger);
return UpdateGoalsForUser(goals, goalSubsMap, userID, logger);
}
export async function UpdateGoalsForUser(
goals: GoalDocument[],
userGoalsMap: Map<string, UserGoalDocument>,
goalSubsMap: Map<string, UserGoalDocument>,
userID: integer,
logger: KtLogger
) {
const returns = await Promise.all(
goals.map((goal: GoalDocument) => {
const userGoal = userGoalsMap.get(goal.goalID);
const goalSub = goalSubsMap.get(goal.goalID);
if (!userGoal) {
if (!goalSub) {
logger.error(
`UserGoal:GoalID mismatch ${goal.goalID} - this user has no userGoal for this, yet it is set.`
`UserGoal:GoalID mismatch ${goal.goalID} - this user has no goalSub for this, yet it is set.`
);
return;
}
return ProcessGoal(goal, userGoal, userID, logger);
return ProcessGoal(goal, goalSub, userID, logger);
})
);
@@ -89,7 +89,7 @@ export async function UpdateGoalsForUser(
*/
export async function ProcessGoal(
goal: GoalDocument,
userGoal: UserGoalDocument,
goalSub: UserGoalDocument,
userID: integer,
logger: KtLogger
) {
@@ -101,7 +101,7 @@ export async function ProcessGoal(
}
// nothing has changed
if (userGoal.progress === res.progress && userGoal.outOf === res.outOf) {
if (goalSub.progress === res.progress && goalSub.outOf === res.outOf) {
return;
}
@@ -114,16 +114,16 @@ export async function ProcessGoal(
};
const oldData = {
progress: userGoal.progress,
progressHuman: userGoal.progressHuman,
outOf: userGoal.outOf,
outOfHuman: userGoal.outOfHuman,
achieved: userGoal.achieved,
progress: goalSub.progress,
progressHuman: goalSub.progressHuman,
outOf: goalSub.outOf,
outOfHuman: goalSub.outOfHuman,
achieved: goalSub.achieved,
};
let webhookEvent = null;
// if this is a newly-achieved goal
if (res.achieved && !userGoal.achieved) {
if (res.achieved && !goalSub.achieved) {
webhookEvent = {
goalID: goal.goalID,
old: oldData,
@@ -134,7 +134,7 @@ export async function ProcessGoal(
const bulkWrite = {
updateOne: {
filter: { _id: userGoal._id! },
filter: { _id: goalSub._id! },
update: {
$set: {
...newData,
@@ -165,23 +165,23 @@ export async function ProcessGoal(
*
* This optimisation allows users to have *lots* of goals, but only ever
* evaluate the ones we need to.
* @returns An array of Goals, and an array of userGoals.
* @returns An array of Goals, and an array of goalSubs.
*/
export async function GetRelevantGoals(
game: Game,
userID: integer,
chartIDs: Set<string>,
logger: KtLogger
): Promise<{ goals: GoalDocument[]; userGoalsMap: Map<string, UserGoalDocument> }> {
const userGoals = await db["goal-subs"].find({ game, userID }, { projectID: true });
): Promise<{ goals: GoalDocument[]; goalSubsMap: Map<string, UserGoalDocument> }> {
const goalSubs = await db["goal-subs"].find({ game, userID }, { projectID: true });
logger.verbose(`Found user has ${userGoals.length} goals.`);
logger.verbose(`Found user has ${goalSubs.length} goals.`);
if (!userGoals.length) {
return { goals: [], userGoalsMap: new Map() };
if (!goalSubs.length) {
return { goals: [], goalSubsMap: new Map() };
}
const goalIDs = userGoals.map((e) => e.goalID);
const goalIDs = goalSubs.map((e) => e.goalID);
const chartIDsArr: string[] = [];
for (const c of chartIDs) {
@@ -204,20 +204,20 @@ export async function GetRelevantGoals(
const goalSet = new Set(goals.map((e) => e.goalID));
const userGoalsMap: Map<string, UserGoalDocument> = new Map();
const goalSubsMap: Map<string, UserGoalDocument> = new Map();
for (const userGoal of userGoals) {
if (!goalSet.has(userGoal.goalID)) {
for (const goalSub of goalSubs) {
if (!goalSet.has(goalSub.goalID)) {
continue;
}
// since these are guaranteed to be unique, lets make a hot map of goalID -> userGoalDocument, so we can
// pull them in for post-processing and filter out the userGoalDocuments that aren't relevant.
userGoalsMap.set(userGoal.goalID, userGoal);
// since these are guaranteed to be unique, lets make a hot map of goalID -> goalSubDocument, so we can
// pull them in for post-processing and filter out the goalSubDocuments that aren't relevant.
goalSubsMap.set(goalSub.goalID, goalSub);
}
return {
goals,
userGoalsMap,
goalSubsMap,
};
}
@@ -56,15 +56,15 @@ export async function UpdateUsersMilestones(
userID: integer,
logger: KtLogger
) {
const userGoalInfoMap: Map<string, GoalImportInfo["new"]> = new Map();
const goalSubInfoMap: Map<string, GoalImportInfo["new"]> = new Map();
const goalIDs = [];
for (const e of importGoalInfo) {
userGoalInfoMap.set(e.goalID, e.new);
goalSubInfoMap.set(e.goalID, e.new);
goalIDs.push(e.goalID);
}
const { milestones, userMilestones } = await GetRelevantMilestones(
const { milestones, milestoneSubs } = await GetRelevantMilestones(
goalIDs,
game,
playtypes,
@@ -74,9 +74,9 @@ export async function UpdateUsersMilestones(
// create a map here to avoid linear searching when
// co-iterating
const userMilestoneMap = new Map();
for (const um of userMilestones) {
userMilestoneMap.set(um.milestoneID, um);
const milestoneSubMap = new Map();
for (const um of milestoneSubs) {
milestoneSubMap.set(um.milestoneID, um);
}
const importGoalMap = new Map();
@@ -92,9 +92,9 @@ export async function UpdateUsersMilestones(
for (const milestone of milestones) {
const { achieved, progress } = ProcessMilestoneFromGII(milestone, importGoalMap);
const userMilestone = userMilestoneMap.get(milestone.milestoneID);
const milestoneSub = milestoneSubMap.get(milestone.milestoneID);
if (!userMilestone) {
if (!milestoneSub) {
logger.severe(
`Invalid state achieved in milestone processing - processed milestone that user did not have? ${milestone.milestoneID}`
);
@@ -115,10 +115,10 @@ export async function UpdateUsersMilestones(
});
const milestoneInfo = {
milestoneID: userMilestone.milestoneID,
milestoneID: milestoneSub.milestoneID,
old: {
progress: userMilestone.progress,
achieved: userMilestone.achieved,
progress: milestoneSub.progress,
achieved: milestoneSub.achieved,
},
new: {
progress,
@@ -126,11 +126,11 @@ export async function UpdateUsersMilestones(
},
};
if (progress !== userMilestone.progress) {
if (progress !== milestoneSub.progress) {
importMilestoneInfo.push(milestoneInfo);
}
if (achieved && !userMilestone.achieved) {
if (achieved && !milestoneSub.achieved) {
EmitWebhookEvent({
type: "milestone-achieved/v1",
content: {
@@ -157,20 +157,20 @@ async function GetRelevantMilestones(
userID: integer,
logger: KtLogger
) {
const userMilestones = await db["milestone-subs"].find({
const milestoneSubs = await db["milestone-subs"].find({
game,
playtype: { $in: playtypes },
userID,
});
logger.debug(`Found ${userMilestones.length} milestone-subs.`);
logger.debug(`Found ${milestoneSubs.length} milestone-subs.`);
const milestones = await db.milestones.find({
milestoneID: { $in: userMilestones.map((e) => e.milestoneID) },
milestoneID: { $in: milestoneSubs.map((e) => e.milestoneID) },
"milestoneData.goals.goalID": { $in: goalIDs },
});
logger.debug(`Found ${milestones.length} relevant milestones.`);
return { userMilestones, milestones };
return { milestoneSubs, milestones };
}
+3 -3
View File
@@ -319,7 +319,7 @@ export async function SubscribeToGoal(
// @ts-expect-error TS can't resolve this.
// because it can't explode out the types.
const userGoal: UserGoalDocument = {
const goalSub: UserGoalDocument = {
outOf: result.outOf,
outOfHuman: result.outOfHuman,
progress: result.progress,
@@ -337,7 +337,7 @@ export async function SubscribeToGoal(
wasInstantlyAchieved: result.achieved,
};
await db["goal-subs"].insert(userGoal);
await db["goal-subs"].insert(goalSub);
return userGoal;
return goalSub;
}
@@ -48,7 +48,7 @@ router.get("/recently-achieved", async (req, res) => {
success: true,
description: `Retrieved ${recentlyAchievedUserGoals.length} recently achieved goals.`,
body: {
userGoals: recentlyAchievedUserGoals,
goalSubs: recentlyAchievedUserGoals,
goals,
users,
},
@@ -86,18 +86,18 @@ const ResolveGoalID: RequestHandler = async (req, res, next) => {
router.get("/:goalID", ResolveGoalID, async (req, res) => {
const goal = req[SYMBOL_TachiData]!.goalDoc!;
const userGoals = await db["goal-subs"].find({
const goalSubs = await db["goal-subs"].find({
goalID: goal.goalID,
});
const users = await GetUsersWithIDs(userGoals.map((e) => e.userID));
const users = await GetUsersWithIDs(goalSubs.map((e) => e.userID));
return res.status(200).json({
success: true,
description: `Retrieved information about ${goal.title}.`,
body: {
goal,
userGoals,
goalSubs,
users,
},
});
@@ -150,20 +150,20 @@ router.get(
const goal = req[SYMBOL_TachiData]!.goalDoc!;
const goalID = goal.goalID;
const userGoal = await db["goal-subs"].findOne({
const goalSub = await db["goal-subs"].findOne({
userID: user.id,
goalID,
});
let goalResults: EvaluatedGoalReturn;
// shortcut evaluation by using the user goal
if (userGoal) {
if (goalSub) {
goalResults = {
achieved: userGoal.achieved,
outOf: userGoal.outOf,
outOfHuman: userGoal.outOfHuman,
progress: userGoal.progress,
progressHuman: userGoal.progressHuman,
achieved: goalSub.achieved,
outOf: goalSub.outOf,
outOfHuman: goalSub.outOfHuman,
progress: goalSub.progress,
progressHuman: goalSub.progressHuman,
};
} else {
const results = await EvaluateGoalForUser(goal, user.id, logger);
@@ -211,7 +211,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/goals", (t) => {
success: true,
description: "Successfully returned 2 goal(s).",
body: {
userGoals: [
goalSubs: [
{
goalID: "foo",
achieved: false,
@@ -233,7 +233,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/goals", (t) => {
success: true,
description: "Successfully returned 1 goal(s).",
body: {
userGoals: [
goalSubs: [
{
goalID: "foo",
achieved: false,
@@ -284,7 +284,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/milestones", (t) => {
success: true,
description: "Successfully returned 2 milestone(s).",
body: {
userMilestones: [
milestoneSubs: [
{
milestoneID: "foo",
achieved: false,
@@ -306,7 +306,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/milestones", (t) => {
success: true,
description: "Successfully returned 1 milestone(s).",
body: {
userMilestones: [
milestoneSubs: [
{
milestoneID: "foo",
achieved: false,
@@ -156,17 +156,17 @@ router.get("/goals", async (req, res) => {
query.achieved = false;
}
const userGoals = await db["goal-subs"].find(query);
const goalSubs = await db["goal-subs"].find(query);
const goals = await db.goals.find({
goalID: { $in: userGoals.map((e) => e.goalID) },
goalID: { $in: goalSubs.map((e) => e.goalID) },
});
return res.status(200).json({
success: true,
description: `Successfully returned ${userGoals.length} goal(s).`,
description: `Successfully returned ${goalSubs.length} goal(s).`,
body: {
userGoals,
goalSubs,
goals,
},
});
@@ -193,17 +193,17 @@ router.get("/milestones", async (req, res) => {
query.achieved = false;
}
const userMilestones = await db["milestone-subs"].find(query);
const milestoneSubs = await db["milestone-subs"].find(query);
const milestones = await db.milestones.find({
milestoneID: { $in: userMilestones.map((e) => e.milestoneID) },
milestoneID: { $in: milestoneSubs.map((e) => e.milestoneID) },
});
return res.status(200).json({
success: true,
description: `Successfully returned ${userMilestones.length} milestone(s).`,
description: `Successfully returned ${milestoneSubs.length} milestone(s).`,
body: {
userMilestones,
milestoneSubs,
milestones,
},
});
@@ -25,22 +25,22 @@ router.get("/", async (req, res) => {
const game = req[SYMBOL_TachiData]!.game!;
const playtype = req[SYMBOL_TachiData]!.playtype!;
const userGoals = await db["goal-subs"].find({
const goalSubs = await db["goal-subs"].find({
userID: user.id,
game,
playtype,
});
const goals = await db.goals.find({
goalID: { $in: userGoals.map((e) => e.goalID) },
goalID: { $in: goalSubs.map((e) => e.goalID) },
});
return res.status(200).json({
success: true,
description: `Retrieved ${userGoals.length} goal(s).`,
description: `Retrieved ${goalSubs.length} goal(s).`,
body: {
goals,
userGoals,
goalSubs,
},
});
});
@@ -158,16 +158,16 @@ router.post(
});
}
const userGoal = await SubscribeToGoal(user.id, goal, { origin: "manual" });
const goalSub = await SubscribeToGoal(user.id, goal, { origin: "manual" });
if (userGoal === SubscribeFailReasons.ALREADY_SUBSCRIBED) {
if (goalSub === SubscribeFailReasons.ALREADY_SUBSCRIBED) {
return res.status(409).json({
success: false,
description: `You are already subscribed to this goal.`,
});
}
if (userGoal === SubscribeFailReasons.ALREADY_ACHIEVED) {
if (goalSub === SubscribeFailReasons.ALREADY_ACHIEVED) {
return res.status(400).json({
success: false,
description: `You can't directly assign goals that you would immediately achieve.`,
@@ -179,7 +179,7 @@ router.post(
description: `Subscribed to ${goal.title}.`,
body: {
goal,
userGoal,
goalSub,
},
});
}
@@ -190,21 +190,21 @@ const GetGoalSubscription: RequestHandler = async (req, res, next) => {
const game = req[SYMBOL_TachiData]!.game!;
const playtype = req[SYMBOL_TachiData]!.playtype!;
const userGoal = await db["goal-subs"].findOne({
const goalSub = await db["goal-subs"].findOne({
userID: user.id,
game,
playtype,
goalID: req.params.goalID,
});
if (!userGoal) {
if (!goalSub) {
return res.status(404).json({
success: false,
description: `${user.username} is not subscribed to this goal.`,
});
}
AssignToReqTachiData(req, { userGoalDoc: userGoal });
AssignToReqTachiData(req, { goalSubDoc: goalSub });
return next();
};
@@ -216,22 +216,22 @@ const GetGoalSubscription: RequestHandler = async (req, res, next) => {
*/
router.get("/:goalID", GetGoalSubscription, async (req, res) => {
const user = req[SYMBOL_TachiData]!.requestedUser!;
const userGoal = req[SYMBOL_TachiData]!.userGoalDoc!;
const goalSub = req[SYMBOL_TachiData]!.goalSubDoc!;
let milestone = null;
if (userGoal.from.origin === "milestone") {
milestone = await GetMilestoneForIDGuaranteed(userGoal.from.milestoneID);
if (goalSub.from.origin === "milestone") {
milestone = await GetMilestoneForIDGuaranteed(goalSub.from.milestoneID);
}
const goal = await GetGoalForIDGuaranteed(userGoal.goalID);
const goal = await GetGoalForIDGuaranteed(goalSub.goalID);
return res.status(200).json({
success: true,
description: `Returned information about goal ${goal.title}.`,
body: {
goal,
userGoal,
goalSub,
milestone,
user,
},
@@ -255,21 +255,21 @@ router.delete(
const game = req[SYMBOL_TachiData]!.game!;
const playtype = req[SYMBOL_TachiData]!.playtype!;
const userGoal = await db["goal-subs"].findOne({
const goalSub = await db["goal-subs"].findOne({
goalID,
userID: user.id,
game,
playtype,
});
if (!userGoal) {
if (!goalSub) {
return res.status(400).json({
success: false,
description: `You aren't subscribed to this goal.`,
});
}
if (userGoal.from.origin === "milestone") {
if (goalSub.from.origin === "milestone") {
return res.status(400).json({
success: false,
description: `This goal is from a milestone. You can't remove it directly, only by removing the parent milestone.`,
@@ -24,22 +24,22 @@ router.get("/", async (req, res) => {
const game = req[SYMBOL_TachiData]!.game!;
const playtype = req[SYMBOL_TachiData]!.playtype!;
const userMilestones = await db["milestone-subs"].find({
const milestoneSubs = await db["milestone-subs"].find({
userID: user.id,
game,
playtype,
});
const milestones = await db.milestones.find({
milestoneID: { $in: userMilestones.map((e) => e.milestoneID) },
milestoneID: { $in: milestoneSubs.map((e) => e.milestoneID) },
});
return res.status(200).json({
success: true,
description: `Retrieved ${userMilestones.length} milestone(s)`,
description: `Retrieved ${milestoneSubs.length} milestone(s)`,
body: {
milestones,
userMilestones,
milestoneSubs,
},
});
});
@@ -49,21 +49,21 @@ const GetMilestoneSubscription: RequestHandler = async (req, res, next) => {
const game = req[SYMBOL_TachiData]!.game!;
const playtype = req[SYMBOL_TachiData]!.playtype!;
const userMilestone = await db["milestone-subs"].findOne({
const milestoneSub = await db["milestone-subs"].findOne({
userID: user.id,
game,
playtype,
milestoneID: req.params.milestoneID,
});
if (!userMilestone) {
if (!milestoneSub) {
return res.status(404).json({
success: false,
description: `${user.username} is not subscribed to this milestone.`,
});
}
AssignToReqTachiData(req, { userMilestoneDoc: userMilestone });
AssignToReqTachiData(req, { milestoneSubDoc: milestoneSub });
return next();
};
@@ -76,9 +76,9 @@ const GetMilestoneSubscription: RequestHandler = async (req, res, next) => {
*/
router.get("/:milestoneID", GetMilestoneSubscription, async (req, res) => {
const user = req[SYMBOL_TachiData]!.requestedUser!;
const userMilestone = req[SYMBOL_TachiData]!.userMilestoneDoc!;
const milestoneSub = req[SYMBOL_TachiData]!.milestoneSubDoc!;
const milestone = await GetMilestoneForIDGuaranteed(userMilestone.milestoneID);
const milestone = await GetMilestoneForIDGuaranteed(milestoneSub.milestoneID);
const { progress, results, goals } = await EvaluateMilestoneProgress(user.id, milestone);
@@ -88,7 +88,7 @@ router.get("/:milestoneID", GetMilestoneSubscription, async (req, res) => {
milestone.name
}.`,
body: {
userMilestone,
milestoneSub,
results,
goals,
},
@@ -152,7 +152,7 @@ router.put("/:milestoneID", RequirePermissions(""), async (req, res) => {
});
}
const userMilestone: UserMilestoneDocument = {
const milestoneSub: UserMilestoneDocument = {
achieved,
game,
};
+4 -2
View File
@@ -11,8 +11,9 @@ import {
SongDocument,
UserSettings,
TachiAPIClientDocument,
UserGoalDocument,
GoalDocument,
MilestoneSubscriptionDocument,
GoalSubscriptionDocument,
} from "tachi-common";
declare module "express-session" {
@@ -65,7 +66,8 @@ export interface TachiRequestData {
tableDoc?: TableDocument;
folderDoc?: FolderDocument;
goalDoc?: GoalDocument;
userGoalDoc?: UserGoalDocument;
goalSubDoc?: GoalSubscriptionDocument;
milestoneSubDoc?: MilestoneSubscriptionDocument;
apiClientDoc: Omit<TachiAPIClientDocument, "clientSecret">;
}