Update Configuration to handle arrays/ Add new db client (#2395)

* Update Configuration to handle arrays

* Add new db client

* Add comment
This commit is contained in:
Bruce Berrios 2022-02-02 13:26:43 -05:00 committed by GitHub
parent 086f949e38
commit 5beb1bf619
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 145 additions and 79 deletions

View file

@ -3,7 +3,7 @@
* To add a new configuration. Simply add it to this object.
* When changing this template, please follow the principle of "Secure by default" (https://en.wikipedia.org/wiki/Secure_by_default).
*/
const BASE_CONFIGURATION = {
const BASE_CONFIGURATION = Object.freeze({
maintenance: false,
quoteReport: {
enabled: false,
@ -16,6 +16,6 @@ const BASE_CONFIGURATION = {
resultObjectHashCheck: {
enabled: false,
},
};
});
module.exports = BASE_CONFIGURATION;

View file

@ -1,12 +1,10 @@
const _ = require("lodash");
const { mongoDB } = require("../init/mongodb");
const db = require("../init/db");
const BASE_CONFIGURATION = require("../constants/base-configuration");
const Logger = require("../handlers/logger.js");
const CONFIG_UPDATE_INTERVAL = 10 * 60 * 1000; // 10 Minutes
let databaseConfigurationUpdated = false;
function mergeConfigurations(baseConfiguration, liveConfiguration) {
if (
!_.isPlainObject(baseConfiguration) ||
@ -22,9 +20,21 @@ function mergeConfigurations(baseConfiguration, liveConfiguration) {
const baseValue = base[key];
const sourceValue = source[key];
if (_.isPlainObject(baseValue) && _.isPlainObject(sourceValue)) {
const isBaseValueObject = _.isPlainObject(baseValue);
const isSourceValueObject = _.isPlainObject(sourceValue);
const isBaseValueArray = _.isArray(baseValue);
const isSourceValueArray = _.isArray(sourceValue);
const arrayObjectMismatch =
(isBaseValueObject && isSourceValueArray) ||
(isBaseValueArray && isSourceValueObject);
if (isBaseValueObject && isSourceValueObject) {
merge(baseValue, sourceValue);
} else if (typeof baseValue === typeof sourceValue) {
} else if (
typeof baseValue === typeof sourceValue &&
!arrayObjectMismatch // typeof {} = "object", typeof [] = "object"
) {
base[key] = sourceValue;
}
});
@ -34,8 +44,9 @@ function mergeConfigurations(baseConfiguration, liveConfiguration) {
}
class ConfigurationDAO {
static configuration = Object.freeze(BASE_CONFIGURATION);
static configuration = BASE_CONFIGURATION;
static lastFetchTime = 0;
static databaseConfigurationUpdated = false;
static async getCachedConfiguration(attemptCacheUpdate = false) {
if (
@ -51,28 +62,23 @@ class ConfigurationDAO {
static async getLiveConfiguration() {
this.lastFetchTime = Date.now();
const configurationCollection = db.collection("configuration");
try {
const liveConfiguration = await mongoDB()
.collection("configuration")
.findOne();
const liveConfiguration = await configurationCollection.findOne();
if (liveConfiguration) {
const baseConfiguration = _.cloneDeep(BASE_CONFIGURATION);
mergeConfigurations(baseConfiguration, liveConfiguration);
this.configuration = baseConfiguration;
if (!databaseConfigurationUpdated) {
await mongoDB()
.collection("configuration")
.updateOne({}, { $set: Object.assign({}, this.configuration) });
databaseConfigurationUpdated = true;
}
this.pushConfiguration(baseConfiguration);
this.configuration = Object.freeze(baseConfiguration);
} else {
await mongoDB()
.collection("configuration")
.insertOne(Object.assign({}, BASE_CONFIGURATION)); // Seed the base configuration.
await configurationCollection.insertOne(
Object.assign({}, BASE_CONFIGURATION)
); // Seed the base configuration.
}
Logger.log(
"fetch_configuration_success",
"Successfully fetched live configuration."
@ -84,10 +90,27 @@ class ConfigurationDAO {
);
}
this.configuration = Object.freeze(this.configuration);
return this.configuration;
}
static async pushConfiguration(configuration) {
if (this.databaseConfigurationUpdated) {
return;
}
const configurationCollection = db.collection("configuration");
try {
await configurationCollection.replaceOne({}, configuration);
this.databaseConfigurationUpdated = true;
} catch (error) {
Logger.log(
"push_configuration_failure",
`Could not push configuration: ${error.message}`
);
}
}
}
module.exports = ConfigurationDAO;

View file

@ -1,8 +1,10 @@
const { mongoDB } = require("../init/mongodb");
const db = require("../init/db");
async function log(event, message, uid) {
const logsCollection = db.collection("logs");
console.log(new Date(), "\t", event, "\t", uid, "\t", message);
await mongoDB().collection("logs").insertOne({
await logsCollection.insertOne({
timestamp: Date.now(),
uid,
event,

75
backend/init/db.js Normal file
View file

@ -0,0 +1,75 @@
const { MongoClient } = require("mongodb");
class DatabaseClient {
static mongoClient = null;
static db = null;
static collections = {};
static connected = false;
static async connect() {
const {
DB_USERNAME,
DB_PASSWORD,
DB_AUTH_MECHANISM,
DB_AUTH_SOURCE,
DB_URI,
DB_NAME,
} = process.env;
const connectionOptions = {
useNewUrlParser: true,
useUnifiedTopology: true,
connectTimeoutMS: 2000,
serverSelectionTimeoutMS: 2000,
};
if (DB_USERNAME && DB_PASSWORD) {
connectionOptions.auth = {
username: DB_USERNAME,
password: DB_PASSWORD,
};
}
if (DB_AUTH_MECHANISM) {
connectionOptions.authMechanism = DB_AUTH_MECHANISM;
}
if (DB_AUTH_SOURCE) {
connectionOptions.authSource = DB_AUTH_SOURCE;
}
this.mongoClient = new MongoClient(DB_URI, connectionOptions);
try {
await this.mongoClient.connect();
this.db = this.mongoClient.db(DB_NAME);
this.connected = true;
} catch (error) {
console.error(e.message);
console.error(
"Failed to connect to database. Exiting with exit status code 1."
);
process.exit(1);
}
}
static async close() {
if (this.connected) {
await this.mongoClient.close();
}
}
static collection(collectionName) {
if (!this.connected) {
return null;
}
if (!(collectionName in this.collections)) {
this.collections[collectionName] = this.db.collection(collectionName);
}
return this.collections[collectionName];
}
}
module.exports = DatabaseClient;

View file

@ -1,42 +1,7 @@
const { MongoClient } = require("mongodb");
let mongoClient;
const db = require("./db");
module.exports = {
async connectDB() {
let options = {
useNewUrlParser: true,
useUnifiedTopology: true,
connectTimeoutMS: 2000,
serverSelectionTimeoutMS: 2000,
};
if (process.env.DB_USERNAME && process.env.DB_PASSWORD) {
options.auth = {
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
};
}
if (process.env.DB_AUTH_MECHANISM) {
options.authMechanism = process.env.DB_AUTH_MECHANISM;
}
if (process.env.DB_AUTH_SOURCE) {
options.authSource = process.env.DB_AUTH_SOURCE;
}
return MongoClient.connect(process.env.DB_URI, options)
.then((client) => {
mongoClient = client;
})
.catch((e) => {
console.error(e.message);
console.error("FAILED TO CONNECT TO DATABASE. EXITING...");
process.exit(1);
});
},
mongoDB() {
return mongoClient.db(process.env.DB_NAME);
return db;
},
};

View file

@ -7,7 +7,7 @@ const cors = require("cors");
const admin = require("firebase-admin");
const Logger = require("./handlers/logger.js");
const serviceAccount = require("./credentials/serviceAccountKey.json");
const { connectDB, mongoDB } = require("./init/mongodb");
const db = require("./init/db");
const jobs = require("./jobs");
const addApiRoutes = require("./api/routes");
const contextMiddleware = require("./middlewares/context");
@ -15,7 +15,7 @@ const ConfigurationDAO = require("./dao/configuration");
const PORT = process.env.PORT || 5005;
// MIDDLEWARE & SETUP
// MIDDLEWARE & SETUP
const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
@ -62,7 +62,7 @@ app.use(function (e, req, res, _next) {
`${monkeyError.status} ${monkeyError.message}`,
monkeyError.uid
);
mongoDB().collection("errors").insertOne({
db.collection("errors").insertOne({
_id: monkeyError.errorID,
timestamp: Date.now(),
status: monkeyError.status,
@ -80,12 +80,15 @@ app.use(function (e, req, res, _next) {
console.log("Starting server...");
app.listen(PORT, async () => {
console.log(`Listening on port ${PORT}`);
console.log("Connecting to database...");
await connectDB();
await db.connect();
console.log("Database connected");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
await ConfigurationDAO.getLiveConfiguration();
console.log("Starting cron jobs...");

View file

@ -1,19 +1,13 @@
const express = require("express");
const { config } = require("dotenv");
const path = require("path");
const MonkeyError = require("./handlers/error");
config({ path: path.join(__dirname, ".env") });
const cors = require("cors");
const db = require("./init/db");
const admin = require("firebase-admin");
const serviceAccount = require("./credentials/serviceAccountKey.json");
const { connectDB, mongoDB } = require("./init/mongodb");
const PORT = process.env.PORT || 5005;
async function main() {
await connectDB();
await db.connect();
await admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
@ -25,8 +19,11 @@ main();
async function refactor() {
console.log("getting all users");
let users = await mongoDB().collection("users").find({}).toArray();
const usersCollection = db.collection("users");
let users = await usersCollection.find({}).toArray();
console.log(users.length);
for (let user of users) {
let obj = user.personalBests;
@ -67,9 +64,10 @@ async function refactor() {
});
}
await mongoDB()
.collection("users")
.updateOne({ _id: user._id }, { $set: { lbPersonalBests: lbPb } });
await usersCollection.updateOne(
{ _id: user._id },
{ $set: { lbPersonalBests: lbPb } }
);
console.log(`updated ${user.name}`);
}
console.log("done");