- 1 :
const SQL = require("../sql");
- 2 :
- 3 :
/**
- 4 :
* The gateway for this settings instance. The gateway handles all the creation and setting of non-default entries, along with saving.
- 5 :
*/
- 6 :
- 7 :
class Gateway {
- 8 :
- 9 :
/**
- 10 :
* Constructs our instance of Gateway
- 11 :
* @param {any} settings The settings that created this gateway.
- 12 :
* @param {any} validateFunction The validation function used to validate user input.
- 13 :
*/
- 14 :
constructor(settings, validateFunction) {
- 15 :
/**
- 16 :
* The Settings class that this gateway is a part of.
- 17 :
* @name Gateway.settings
- 18 :
* @type {Settings}
- 19 :
* @readonly
- 20 :
*/
- 21 :
Object.defineProperty(this, "settings", { value: settings });
- 22 :
- 23 :
/**
- 24 :
* The provider engine that will handle saving and getting all data for this instance.
- 25 :
* @type {string}
- 26 :
*/
- 27 :
this.engine = this.client.config.provider.engine;
- 28 :
- 29 :
if (!this.provider) throw `This provider(${this.engine}) does not exist in your system.`;
- 30 :
- 31 :
/**
- 32 :
* If the provider is SQL, this property will ensure data is serialized and deserialized.
- 33 :
* @type {string}
- 34 :
*/
- 35 :
this.sql = this.provider.conf.sql ? new SQL(this.client, this) : null;
- 36 :
- 37 :
/**
- 38 :
* The function validator for this gateway.
- 39 :
* @type {function}
- 40 :
*/
- 41 :
this.validate = validateFunction;
- 42 :
}
- 43 :
- 44 :
/**
- 45 :
* Initializes the gateway, creating tables, ensuring the schema exists, and caching values.
- 46 :
* @param {Schema} schema The Schema object, validated from settings.
- 47 :
* @returns {void}
- 48 :
*/
- 49 :
async init(schema) {
- 50 :
if (!(await this.provider.hasTable(this.type))) await this.provider.createTable(this.type, this.sql ? this.sql.buildSQLSchema(schema) : undefined);
- 51 :
const data = await this.provider.getAll(this.type);
- 52 :
if (this.sql) {
- 53 :
this.sql.initDeserialize();
- 54 :
for (let i = 0; i < data.length; i++) this.sql.deserializer(data[i]);
- 55 :
}
- 56 :
for (const key of data) this.cache.set(this.type, key.id, key); // eslint-disable-line
- 57 :
}
- 58 :
- 59 :
/**
- 60 :
* Creates a new entry in the cache.
- 61 :
* @param {Object|string} input An object containing a id property, like discord.js objects, or a string.
- 62 :
*/
- 63 :
async create(input) {
- 64 :
const target = await this.validate(input).then(output => (output.id || output));
- 65 :
await this.provider.create(this.type, target, this.schema.defaults);
- 66 :
this.cache.set(this.type, target, this.schema.defaults);
- 67 :
}
- 68 :
- 69 :
/**
- 70 :
* Removes an entry from the cache.
- 71 :
* @param {Object|string} input An object containing a id property, like discord.js objects, or a string.
- 72 :
*/
- 73 :
async destroy(input) {
- 74 :
const target = await this.validate(input).then(output => (output.id || output));
- 75 :
await this.provider.delete(this.type, target);
- 76 :
this.cache.delete(this.type, target);
- 77 :
}
- 78 :
- 79 :
/**
- 80 :
* Gets an entry from the cache
- 81 :
* @param {string} input The key you are you looking for.
- 82 :
* @returns {Schema}
- 83 :
*/
- 84 :
get(input) {
- 85 :
return input !== "default" ? this.cache.get(this.type, input) || this.schema.defaults : this.schema.defaults;
- 86 :
}
- 87 :
- 88 :
/**
- 89 :
* Sync either all entries from the provider, or a single one.
- 90 :
* @param {Object|string} [input=null] An object containing a id property, like discord.js objects, or a string.
- 91 :
* @returns {void}
- 92 :
*/
- 93 :
async sync(input = null) {
- 94 :
if (!input) {
- 95 :
const data = await this.provider.getAll(this.type);
- 96 :
if (this.sql) for (let i = 0; i < data.length; i++) this.sql.deserializer(data[i]);
- 97 :
for (const key of data) this.cache.set(this.type, key.id, key); // eslint-disable-line
- 98 :
return;
- 99 :
}
- 100 :
const target = await this.validate(input).then(output => (output.id || output));
- 101 :
const data = await this.provider.get(this.type, target);
- 102 :
if (this.sql) this.sql.deserializer(data);
- 103 :
await this.cache.set(this.type, target, data);
- 104 :
}
- 105 :
- 106 :
/**
- 107 :
* Reset a key's value to default from a entry.
- 108 :
* @param {Object|string} input An object containing a id property, like Discord.js objects, or a string.
- 109 :
* @param {string} key The key to reset.
- 110 :
* @returns {any}
- 111 :
*/
- 112 :
async reset(input, key) {
- 113 :
const target = await this.validate(input).then(output => (output.id || output));
- 114 :
if (!(key in this.schema)) throw `The key ${key} does not exist in the current data schema.`;
- 115 :
const defaultKey = this.schema[key].default;
- 116 :
await this.provider.update(this.type, target, { [key]: defaultKey });
- 117 :
this.sync(target);
- 118 :
return defaultKey;
- 119 :
}
- 120 :
- 121 :
/**
- 122 :
* Updates an entry.
- 123 :
* @param {Object|string} input An object or string that can be parsed by this instance's resolver.
- 124 :
* @param {Object} object An object with pairs of key/value to update.
- 125 :
* @param {Object|string} [guild=null] A Guild resolvable, useful for when the instance of SG doesn't aim for Guild settings.
- 126 :
* @returns {Object}
- 127 :
*/
- 128 :
async update(input, object, guild = null) {
- 129 :
const target = await this.validate(input).then(output => output.id || output);
- 130 :
guild = await this.resolver.guild(guild || target);
- 131 :
- 132 :
const resolved = await Promise.all(Object.entries(object).map(async ([key, value]) => {
- 133 :
if (!(key in this.schema)) throw `The key ${key} does not exist in the current data schema.`;
- 134 :
return this.resolver[this.schema[key].type.toLowerCase()](value, guild, this.schema[key])
- 135 :
.then(res => ({ [key]: res.id || res }));
- 136 :
}));
- 137 :
- 138 :
const result = Object.assign({}, ...resolved);
- 139 :
- 140 :
await this.ensureCreate(target);
- 141 :
await this.provider.update(this.type, target, result);
- 142 :
await this.sync(target);
- 143 :
return result;
- 144 :
}
- 145 :
- 146 :
/**
- 147 :
* Creates the settings if it did not exist previously.
- 148 :
* @param {Object|string} target An object or string that can be parsed by this instance's resolver.
- 149 :
* @returns {true}
- 150 :
*/
- 151 :
async ensureCreate(target) {
- 152 :
if (typeof target !== "string") throw `Expected input type string, got ${typeof target}`;
- 153 :
let exists = this.cache.has(this.type, target);
- 154 :
if (exists instanceof Promise) exists = await exists;
- 155 :
if (exists === false) return this.create(target);
- 156 :
return true;
- 157 :
}
- 158 :
- 159 :
/**
- 160 :
* Update an array from the a Guild's configuration.
- 161 :
* @param {Object|string} input An object containing a id property, like discord.js objects, or a string.
- 162 :
* @param {string} type Either 'add' or 'remove'.
- 163 :
* @param {string} key The key from the Schema.
- 164 :
* @param {any} data The value to be added or removed.
- 165 :
* @param {Object|string} [guild=null] The guild for this setting, useful for when the settings aren't aimed for guilds
- 166 :
* @returns {boolean}
- 167 :
*/
- 168 :
async updateArray(input, type, key, data, guild = null) {
- 169 :
if (!["add", "remove"].includes(type)) throw "The type parameter must be either add or remove.";
- 170 :
if (!(key in this.schema)) throw `The key ${key} does not exist in the current data schema.`;
- 171 :
if (!this.schema[key].array) throw `The key ${key} is not an Array.`;
- 172 :
if (data === undefined) throw "You must specify the value to add or filter.";
- 173 :
const target = await this.validate(input).then(output => (output.id || output));
- 174 :
guild = await this.resolver.guild(guild || target);
- 175 :
let result = await this.resolver[this.schema[key].type.toLowerCase()](data, guild, this.schema[key]);
- 176 :
if (result.id) result = result.id;
- 177 :
let cache = this.cache.get(this.type, target);
- 178 :
if (cache instanceof Promise) cache = await cache;
- 179 :
if (type === "add") {
- 180 :
if (cache[key].includes(result)) throw `The value ${data} for the key ${key} already exists.`;
- 181 :
cache[key].push(result);
- 182 :
await this.provider.update(this.type, target, { [key]: cache[key] });
- 183 :
await this.sync(target);
- 184 :
return result;
- 185 :
}
- 186 :
if (!cache[key].includes(result)) throw `The value ${data} for the key ${key} does not exist.`;
- 187 :
cache[key] = cache[key].filter(v => v !== result);
- 188 :
- 189 :
await this.ensureCreate(target);
- 190 :
await this.provider.update(this.type, target, { [key]: cache[key] });
- 191 :
await this.sync(target);
- 192 :
return true;
- 193 :
}
- 194 :
- 195 :
/**
- 196 :
* The client this SettingGateway was created with.
- 197 :
* @type {KomadaClient}
- 198 :
* @readonly
- 199 :
*/
- 200 :
get client() {
- 201 :
return this.settings.client;
- 202 :
}
- 203 :
- 204 :
/**
- 205 :
* The resolver instance this SettingGateway uses to parse the data.
- 206 :
* @type {Resolver}
- 207 :
* @readonly
- 208 :
*/
- 209 :
get resolver() {
- 210 :
return this.settings.resolver;
- 211 :
}
- 212 :
- 213 :
/**
- 214 :
* The provider this SettingGateway instance uses for the persistent data operations.
- 215 :
* @type {Provider}
- 216 :
* @readonly
- 217 :
*/
- 218 :
get provider() {
- 219 :
return this.client.providers.get(this.engine);
- 220 :
}
- 221 :
- 222 :
/**
- 223 :
* The schema this gateway instance is handling.
- 224 :
* @type {Schema}
- 225 :
* @readonly
- 226 :
*/
- 227 :
get schema() {
- 228 :
return this.settings.schema;
- 229 :
}
- 230 :
- 231 :
/**
- 232 :
* The cache created with this instance
- 233 :
* @type {Cache}
- 234 :
* @readonly
- 235 :
*/
- 236 :
- 237 :
get cache() {
- 238 :
return this.settings.cache;
- 239 :
}
- 240 :
- 241 :
/**
- 242 :
* The type of settings (or name).
- 243 :
* @type {string}
- 244 :
* @readonly
- 245 :
*/
- 246 :
get type() {
- 247 :
return this.settings.type;
- 248 :
}
- 249 :
- 250 :
}
- 251 :
- 252 :
module.exports = Gateway;