const Setting = require('../models/setting'); const { ErrSettingsNotInit } = require('../errors'); const { dotize } = require('./utils'); const { isEmpty, zipObject, uniq } = require('lodash'); const DataLoader = require('dataloader'); const selector = { id: '1' }; async function loadFn(fields = []) { const model = await Setting.findOne(selector).select(uniq(fields)); if (!model) { throw new ErrSettingsNotInit(); } return model; } // batchLoadFn will load a settings object with all the requested fields. async function batchLoadFn(fields) { // Load a settings object with all the requested fields. const model = await loadFn(fields); // Convert the model into an object for easier manipulation. const obj = model.toObject(); // Return the specific fields for each of the fields that were loaded. return fields.map(field => obj[field]); } // batchedSettingsLoader will load setting fields for each request. This isn't a // cached loader, so this is really just for optimizing the requests made to the // database by batching. const batchedSettingsLoader = new DataLoader(batchLoadFn, { cache: false }); class Settings { static async retrieve(...fields) { if (!isEmpty(fields)) { // Included for backwards compatibility. return Settings.select(...fields); } // Call the loadFn directly if we need to load all the fields. return loadFn(fields); } static async select(...fields) { // Load all the values for the specific fields. const values = await batchedSettingsLoader.loadMany(fields); // Zip up the fields and values to create an object to return. const obj = zipObject(fields, values); // Return the assembled Settings object. return new Setting(obj); } static async update(settings) { const updatedSettings = await Setting.findOneAndUpdate( selector, { $set: dotize(settings), }, { upsert: true, new: true, setDefaultsOnInsert: true, } ); return updatedSettings; } static init(defaults = {}) { return Settings.retrieve().catch(() => { const settings = new Setting(defaults); return settings.save(); }); } } module.exports = Settings;