Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#365 이벤트 관련 MongoDB Query에 Transactions 도입 #374

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Refactor: add eventConfig property to loadenv.js
kmc7468 committed Sep 19, 2023
commit 98b9761ed9cd39bc617e0fb9a3000e247f928027
9 changes: 6 additions & 3 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// 모듈 require
const express = require("express");
const http = require("http");
const { port: httpPort, eventMode } = require("./loadenv");
const { port: httpPort, eventConfig } = require("./loadenv");
const logger = require("./src/modules/logger");
const { connectDatabase } = require("./src/modules/stores/mongo");
const { startSocketServer } = require("./src/modules/socket");
@@ -43,8 +43,11 @@ app.use(require("./src/middlewares/limitRate"));
app.use("/docs", require("./src/routes/docs"));

// 2023 추석 이벤트 전용 라우터입니다.
eventMode &&
app.use(`/events/${eventMode}`, require("./src/lottery").lotteryRouter);
eventConfig &&
app.use(
`/events/${eventConfig.mode}`,
require("./src/lottery").lotteryRouter
);

// [Middleware] 모든 API 요청에 대하여 origin 검증
app.use(require("./src/middlewares/originValidator"));
2 changes: 1 addition & 1 deletion loadenv.js
Original file line number Diff line number Diff line change
@@ -38,5 +38,5 @@ module.exports = {
slackWebhookUrl: {
report: process.env.SLACK_REPORT_WEBHOOK_URL || "", // optional
},
eventMode: undefined,
eventConfig: process.env.EVENT_CONFIG && JSON.parse(process.env.EVENT_CONFIG), // optional
};
7 changes: 4 additions & 3 deletions src/lottery/index.js
Original file line number Diff line number Diff line change
@@ -6,11 +6,11 @@ const {
transactionModel,
} = require("./modules/stores/mongo");

const { eventMode } = require("../../loadenv");
const { eventConfig } = require("../../loadenv");
const { buildResource } = require("../modules/adminResource");

// [Routes] 기존 docs 라우터의 docs extend
require("./routes/docs")();
eventConfig && require("./routes/docs")();

const lotteryRouter = express.Router();

@@ -30,7 +30,8 @@ const resources = [
transactionModel,
].map(buildResource());

const contracts = eventMode && require(`./modules/contracts/${eventMode}`);
const contracts =
eventConfig && require(`./modules/contracts/${eventConfig.mode}`);

module.exports = {
lotteryRouter,
41 changes: 7 additions & 34 deletions src/lottery/modules/contracts/2023fall.js
Original file line number Diff line number Diff line change
@@ -76,11 +76,6 @@ const quests = buildQuests({
},
});

const eventPeriod = {
start: new Date("2023-09-25T00:00:00+09:00"), // Inclusive
end: new Date("2023-10-10T00:00:00+09:00"), // Exclusive
};

/**
* firstLogin 퀘스트의 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
@@ -89,7 +84,7 @@ const eventPeriod = {
* @usage lottery/globalState/createUserGlobalStateHandler
*/
const completeFirstLoginQuest = async (userId, timestamp) => {
return await completeQuest(userId, timestamp, eventPeriod, quests.firstLogin);
return await completeQuest(userId, timestamp, quests.firstLogin);
};

/**
@@ -112,7 +107,6 @@ const completePayingAndSendingQuest = async (timestamp, roomObject) => {
await completeQuest(
participant.user._id,
timestamp,
eventPeriod,
quests.payingAndSending
)
)
@@ -128,12 +122,7 @@ const completePayingAndSendingQuest = async (timestamp, roomObject) => {
* @usage rooms/createHandler
*/
const completeFirstRoomCreationQuest = async (userId, timestamp) => {
return await completeQuest(
userId,
timestamp,
eventPeriod,
quests.firstRoomCreation
);
return await completeQuest(userId, timestamp, quests.firstRoomCreation);
};

const completeRoomSharingQuest = async () => {
@@ -153,7 +142,7 @@ const completeRoomSharingQuest = async () => {
const completePayingQuest = async (userId, timestamp, roomObject) => {
if (roomObject.part.length < 2) return null;

return await completeQuest(userId, timestamp, eventPeriod, quests.paying);
return await completeQuest(userId, timestamp, quests.paying);
};

/**
@@ -169,7 +158,7 @@ const completePayingQuest = async (userId, timestamp, roomObject) => {
const completeSendingQuest = async (userId, timestamp, roomObject) => {
if (roomObject.part.length < 2) return null;

return await completeQuest(userId, timestamp, eventPeriod, quests.sending);
return await completeQuest(userId, timestamp, quests.sending);
};

/**
@@ -181,12 +170,7 @@ const completeSendingQuest = async (userId, timestamp, roomObject) => {
* @usage users/editNicknameHandler
*/
const completeNicknameChangingQuest = async (userId, timestamp) => {
return await completeQuest(
userId,
timestamp,
eventPeriod,
quests.nicknameChanging
);
return await completeQuest(userId, timestamp, quests.nicknameChanging);
};

/**
@@ -201,12 +185,7 @@ const completeNicknameChangingQuest = async (userId, timestamp) => {
const completeAccountChangingQuest = async (userId, timestamp, newAccount) => {
if (newAccount === "") return null;

return await completeQuest(
userId,
timestamp,
eventPeriod,
quests.accountChanging
);
return await completeQuest(userId, timestamp, quests.accountChanging);
};

/**
@@ -225,12 +204,7 @@ const completeAdPushAgreementQuest = async (
) => {
if (!advertisement) return null;

return await completeQuest(
userId,
timestamp,
eventPeriod,
quests.adPushAgreement
);
return await completeQuest(userId, timestamp, quests.adPushAgreement);
};

const completeEventSharingOnInstagramQuest = async () => {
@@ -243,7 +217,6 @@ const completePurchaseSharingOnInstagramQuest = async () => {

module.exports = {
quests,
eventPeriod,
completeFirstLoginQuest,
completePayingAndSendingQuest,
completeFirstRoomCreationQuest,
13 changes: 8 additions & 5 deletions src/lottery/modules/quests.js
Original file line number Diff line number Diff line change
@@ -7,6 +7,12 @@ const {
const logger = require("../../modules/logger");
const mongoose = require("mongoose");

const { eventConfig } = require("../../../loadenv");
const eventPeriod = {
startAt: new Date(eventConfig.startAt),
endAt: new Date(eventConfig.endAt),
};

const requiredQuestFields = ["name", "description", "imageUrl", "reward"];
const buildQuests = (quests) => {
for (const [id, quest] of Object.entries(quests)) {
@@ -45,9 +51,6 @@ const buildQuests = (quests) => {
* 퀘스트 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @param {Object} eventPeriod - 이벤트의 기간입니다.
* @param {Date} eventPeriod.start - 이벤트의 시작 시각(Inclusive)입니다.
* @param {Date} eventPeriod.end - 이벤트의 종료 시각(Exclusive)입니다.
* @param {Object} quest - 퀘스트의 정보입니다.
* @param {string} quest.id - 퀘스트의 Id입니다.
* @param {string} quest.name - 퀘스트의 이름입니다.
@@ -57,14 +60,14 @@ const buildQuests = (quests) => {
* @param {number} quest.maxCount - 퀘스트의 최대 완료 가능 횟수입니다.
* @returns {Object|null} 성공한 경우 Object를, 실패한 경우 null을 반환합니다. 이미 최대 완료 횟수에 도달했거나, 퀘스트가 원격으로 비활성화 된 경우에도 실패로 처리됩니다.
*/
const completeQuest = async (userId, timestamp, eventPeriod, quest) => {
const completeQuest = async (userId, timestamp, quest) => {
try {
// 1단계: 유저의 EventStatus를 가져옵니다.
const eventStatus = await eventStatusModel.findOne({ userId }).lean();
if (!eventStatus) return null;

// 2단계: 이벤트 기간인지 확인합니다.
if (timestamp >= eventPeriod.end || timestamp < eventPeriod.start) {
if (timestamp >= eventPeriod.endAt || timestamp < eventPeriod.startAt) {
logger.info(
`User ${userId} failed to complete auto-disabled ${quest.id}Quest`
);
4 changes: 2 additions & 2 deletions src/lottery/routes/docs/globalState.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { eventMode } = require("../../../../loadenv");
const apiPrefix = `/events/${eventMode}/global-state`;
const { eventConfig } = require("../../../../loadenv");
const apiPrefix = `/events/${eventConfig.mode}/global-state`;

const globalStateDocs = {};
globalStateDocs[`${apiPrefix}/`] = {
4 changes: 2 additions & 2 deletions src/lottery/routes/docs/items.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { eventMode } = require("../../../../loadenv");
const apiPrefix = `/events/${eventMode}/items`;
const { eventConfig } = require("../../../../loadenv");
const apiPrefix = `/events/${eventConfig.mode}/items`;

const itemsDocs = {};
itemsDocs[`${apiPrefix}/list`] = {
4 changes: 2 additions & 2 deletions src/lottery/routes/docs/publicNotice.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { eventMode } = require("../../../../loadenv");
const apiPrefix = `/events/${eventMode}/public-notice`;
const { eventConfig } = require("../../../../loadenv");
const apiPrefix = `/events/${eventConfig.mode}/public-notice`;

const publicNoticeDocs = {};
publicNoticeDocs[`${apiPrefix}/leaderboard`] = {
4 changes: 2 additions & 2 deletions src/lottery/routes/docs/swaggerDocs.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
const { eventMode } = require("../../../../loadenv");
const { eventConfig } = require("../../../../loadenv");
const globalStateDocs = require("./globalState");
const itemsDocs = require("./items");
const transactionsDocs = require("./transactions");
const itemsSchema = require("./itemsSchema");
const publicNoticeDocs = require("./publicNotice");

const apiPrefix = `/events/${eventMode}`;
const apiPrefix = `/events/${eventConfig.mode}`;

const eventSwaggerDocs = {
tags: [
4 changes: 2 additions & 2 deletions src/lottery/routes/docs/transactions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { eventMode } = require("../../../../loadenv");
const apiPrefix = `/events/${eventMode}/transactions`;
const { eventConfig } = require("../../../../loadenv");
const apiPrefix = `/events/${eventConfig.mode}/transactions`;

const transactionsDocs = {};
transactionsDocs[`${apiPrefix}/`] = {
11 changes: 5 additions & 6 deletions src/lottery/services/globalState.js
Original file line number Diff line number Diff line change
@@ -2,11 +2,10 @@ const { eventStatusModel } = require("../modules/stores/mongo");
const logger = require("../../modules/logger");
const { isLogin, getLoginInfo } = require("../../modules/auths/login");

const { eventMode } = require("../../../loadenv");
const contract = eventMode
? require(`../modules/contracts/${eventMode}`)
: undefined;
const quests = contract ? Object.values(contract.quests) : undefined;
const { eventConfig } = require("../../../loadenv");
const contracts =
eventConfig && require(`../modules/contracts/${eventConfig.mode}`);
const quests = contracts ? Object.values(contracts.quests) : undefined;

const getUserGlobalStateHandler = async (req, res) => {
try {
@@ -50,7 +49,7 @@ const createUserGlobalStateHandler = async (req, res) => {
});
await eventStatus.save();

await contract.completeFirstLoginQuest(req.userOid, req.timestamp);
await contracts.completeFirstLoginQuest(req.userOid, req.timestamp);

res.json({ result: true });
} catch (err) {
14 changes: 9 additions & 5 deletions src/lottery/services/items.js
Original file line number Diff line number Diff line change
@@ -20,10 +20,11 @@ const updateEventStatus = async (
}
);

const { eventMode } = require("../../../loadenv");
const eventPeriod = eventMode
? require(`../modules/contracts/${eventMode}`).eventPeriod
: undefined;
const { eventConfig } = require("../../../loadenv");
const eventPeriod = eventConfig && {
startAt: new Date(eventConfig.startAt),
endAt: new Date(eventConfig.endAt),
};

const getRandomItem = async (req, depth) => {
if (depth >= 10) {
@@ -126,7 +127,10 @@ const purchaseHandler = async (req, res) => {
.status(400)
.json({ error: "Items/Purchase : nonexistent eventStatus" });

if (req.timestamp >= eventPeriod.end || req.timestamp < eventPeriod.start)
if (
req.timestamp >= eventPeriod.endAt ||
req.timestamp < eventPeriod.startAt
)
return res.status(400).json({ error: "Items/Purchase : out of date" });

const { itemId } = req.params;
4 changes: 2 additions & 2 deletions src/routes/admin.js
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ const {
deviceTokenModel,
notificationOptionModel,
} = require("../modules/stores/mongo");
const { eventMode } = require("../../loadenv");
const { eventConfig } = require("../../loadenv");
const { buildResource } = require("../modules/adminResource");

const router = express.Router();
@@ -37,7 +37,7 @@ const baseResources = [
notificationOptionModel,
].map(buildResource());
const resources = baseResources.concat(
eventMode === "2023fall" ? require("../lottery").resources : []
eventConfig?.mode === "2023fall" ? require("../lottery").resources : []
);

// Create router for admin page