Skip to content
This repository has been archived by the owner on Aug 28, 2020. It is now read-only.

Commit

Permalink
SettingGateway v2.1 (#179)
Browse files Browse the repository at this point in the history
* SGv2.1 refactor

* Fixed deploy

* Now for real

* Make unused gateways lightning fast, updated changelog, fixed cache

* SchemaPiece#modify -> edit, GatewayDriver#types type from string[] to Set<string>, massive documentation overhaul

* MDLint

* Fixed deploy

* Docs fixes

* Removed ConfigUpdateEntryMany, tweaked docs, added toJSON to Gateway and GatewayDriver

* only decent way to document Symbol.iterator

* Requested changes

* IterableIterator -> Iterator

* Added iterator for Schedule

* Updated changelog

* Removed `SchemaFolder#getList` and replaced it to `Configuration#list`.

* First functional version

* Removed the abstract method `resolveString()` from SchemaFolder and SchemaPiece.

* Fixed path.resolveString

* whoops

* Fixed disabledCommands' validator

* Updated typings

* Fixed Configuration#resolveString not parsing the keys correctly

* removeKey/Folder -> remove, hasKey -> has, improvements, code reduction

* More memory improvements, bugfixes

* perf(iterators), removed Configuration#resetConfiguration

* SchemaFolder#add

* Forgot to call the function

* Configuration#reset

* Bugfixes

* Update Client.js

* Updated resolvers to not include Promise<> in returns when async

* Removed Promise<> in all JSDocs for methods with async

* Fixed SchemaFolder#add

* Fixed old cache methods

* Fixed Configuration#reset() not creating the right object

* Added `Configuration#reset(string[]);`

* Fixed an edge case in super-nested keys

* Fixed deep merger

* Fix Configuration._patch

* Fixed critical bug in SGv2 shard support

* Generate better SchemaPiece defaults

* Fixed a bug

* Better defaults

* Fixed example running off the site

* Fixed example for GatewayDriver#add

* Give the example some sense

* (typedef) emoji -> Emoji

* Fixed Emoji in RM and RD, RichMenuEmojisObject extend RichDisplayEmojisObject

* Way much safer and faster Configuration#_merge

* Configuration refactor

* Fix Configuration merge

* Fix a little issue

* Return a better error if getPath returns null in silent mode

* Fix Gateway#getPath

* Actually fix it

* Whoops

* Fixed save not accessing to the right tuple

* Refactored the updateMany pattern

* Revert accidental .eslintrc.json change

* Fixed getPath, again

* Fix getPath, finally

* Fixed another typo in Configuration#_setValueByPath

* Fix Configuration#_setValueByPath not updating correctly

* Fixed Util.deepClone and SchemaFolder#_shardSyncSchema

* Added overload for Configuration#reset, added COMMAND_CONF_NOCHANGE to language

* Changelog

* Ao's request

* Add KlasaClient#options docs, updated events, fixed typo in KlasaUser docs

* Typo

* Removed all `@memberof` from jsdocs, removed duplicated/useless typedefs

* Don't show folders if they don't have any configurable keys

* Fixed Configuration#resolveString not getting the right values

* Fixed ESLint

* Fixed Configuration#get calling itself recursively instead of Configuration#_get

* Fixed resetAll pattern not resetting nested keys

* Typings fixes
  • Loading branch information
kyranet authored and bdistin committed Feb 25, 2018
1 parent 0e7f8d5 commit 0bd3d4f
Show file tree
Hide file tree
Showing 48 changed files with 1,377 additions and 1,435 deletions.
32 changes: 32 additions & 0 deletions CHANGELOG.md
Expand Up @@ -13,6 +13,14 @@ NOTE: For the contributors, you add new entries to this document following this

### Added

- [[#179](https://github.com/dirigeants/klasa/pull/179)] Added the key `COMMAND_CONF_NOCHANGE` to the **en-US** language file. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Added support for `Configuration#reset(string[]);` to reset multiple keys. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Added `util.arraysEqual`. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Added property `Symbol.iterator` to Schedule. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Added `Gateway#toJSON()` and `GatewayDriver#toJSON()`. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Added `GatewayDriver#register` to be able to register new gateways without events (directly in your `app.js`). (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Added `util.getIdentifier` as a replacement for the function validator. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Added `SchemaFolder#keys()`, `SchemaFolder#values()`, `SchemaFolder#entries()` and `SchemaFolder#[@@iterator]()`. Identical to Map's respective methods. (kyranet)
- [[#201](https://github.com/dirigeants/klasa/pull/201)] Improve `util.toTitleCase`. (KingDGrizzle)
- [[#186](https://github.com/dirigeants/klasa/pull/186)] Added a **load** command. (kyranet)
- [[#176](https://github.com/dirigeants/klasa/pull/176)] Added `categorychannel` type to `ArgResolver`. (kyranet)
Expand Down Expand Up @@ -83,6 +91,10 @@ NOTE: For the contributors, you add new entries to this document following this

### Changed

- [[#179](https://github.com/dirigeants/klasa/pull/179)] Refactored Configuration's internals for maximum consistency and reduced code duplication. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Changed the type for `GatewayDriver#types` from `string[]` to `Set<string>`. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Renamed `SchemaPiece#modify()` to `SchemaPiece#edit()`. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Renamed `Gateway#getKeys()` and `Gateway#getValues()` to `Gateway#keys(true)` and `Gateway#values(true)` respectively, which return iterators. (kyranet)
- [[#184](https://github.com/dirigeants/klasa/pull/184)] `Piece#file` is now consistent between all pieces and it's type of `string[]`. (bdistin)
- [[#184](https://github.com/dirigeants/klasa/pull/184)] **[MEM-PERF]** Tweaked `Command` to have category and subCategory properties as getters. (bdistin)
- [[#184](https://github.com/dirigeants/klasa/pull/184)] Tweaked all stores to be able to load files in deep folders and abstracted it in `Store` for code reduction. (bdistin)
Expand Down Expand Up @@ -139,6 +151,17 @@ NOTE: For the contributors, you add new entries to this document following this

### Removed

- [[#179](https://github.com/dirigeants/klasa/pull/179)] **[BREAKING]** Removed `SchemaFolder#addKey` and `SchemaFolder#addFolder` in favor to a more consistent `Schema#add`. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] **[BREAKING]** Removed `Configuration#resetConfiguration()`. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] **[PERF-MEM]** Removed `Configuration#type`. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] **[BREAKING]** Removed `SchemaFolder#removeKey` and `SchemaFolder#removeFolder` in favor to a more consistent `Schema#remove`. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Removed the abstract method `resolveString()` from SchemaFolder and SchemaPiece. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Removed `SchemaFolder#getList` and replaced it to `Configuration#list`. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Removed the `ConfigUpdateEntryMany` typedef in favor of a more constant type. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Removed the resolver functions from constants. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Removed `SchemaFolder#keys` (`Map<string>`) to reduce RAM usage and key caching duplication. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Removed SettingGateway function validators. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Removed Collection cache provider (will be moved to klasa-pieces). (kyranet)
- [[#159](https://github.com/dirigeants/klasa/pull/159)] Removed deprecated property `GatewayOptions.cache` to be locked to `'collection'`. (kyranet)
- [[#158](https://github.com/dirigeants/klasa/pull/158)] `Configuration#updateMany` is now under `Configuration#update`, in favor of a much less confusing naming. (kyranet)
- [[`5b0c468362`](https://github.com/dirigeants/klasa/commit/5b0c46836200a57577bbd4aaa5cd463089a3bff0)] Removed `KlasaClient.sharded` as `Client.shard` is now fixed. (bdistin)
Expand All @@ -160,6 +183,15 @@ NOTE: For the contributors, you add new entries to this document following this

### Fixed

- [[#179](https://github.com/dirigeants/klasa/pull/179)] Fixed `Util.deepClone` not cloning full objects. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Fixed `SchemaFolder#_shardSyncSchema` not passing the action as string. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Fixed `null` values in *updateMany*'s pattern not updating nested keys plus individual queries. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Fixed update/reset methods in Configuration not emitting `configEntryCreate` when the entry does not exist. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Fixed the updateMany pattern in Configuration not accepting a guild. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Fixed the **configUpdateEntry** event (used to sync configuration instances across shards) running in non-sharded bots, now it will be disabled if the bot is not sharded. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Fixed `Configuration._patch` not patching after the second nested folder. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Fixed SettingResolver's return types. (kyranet)
- [[#179](https://github.com/dirigeants/klasa/pull/179)] Fixed Gateway syncing keys even when it's unused. (kyranet)
- [[#184](https://github.com/dirigeants/klasa/pull/184)] Fixed classes and options missing methods and properties in typings. (kyranet)
- [[#184](https://github.com/dirigeants/klasa/pull/184)] Fixed `Provider` not having abstract methods in typings. (kyranet)
- [[#184](https://github.com/dirigeants/klasa/pull/184)] Fixed typings using `Store` without generic parameters, causing the TypeScript compiler to fail. (kyranet)
Expand Down
19 changes: 19 additions & 0 deletions guides/.docconfig.json
Expand Up @@ -79,6 +79,25 @@
"path": "CommandsCustomResolvers.md"
}]
},
{
"name": "Advanced SettingGateway",
"files": [{
"name": "Understanding SchemaFolders",
"path": "UnderstandingSchemaFolders.md"
},
{
"name": "Understanding SchemaPieces",
"path": "UnderstandingSchemaPieces.md"
},
{
"name": "SettingGateway's Types",
"path": "SettingGatewayKeyTypes.md"
},
{
"name": "Updating your Configuration",
"path": "SettingGatewayConfigurationUpdate.md"
}]
},
{
"name": "Other Subjects",
"files": [{
Expand Down
@@ -0,0 +1,38 @@
# Updating your configuration

Once we have our schema done with all the keys, folders and types needed, we may want to update our configuration via SettingGateway, all of this is done via {@link Configuration#update}. However, how can I update it? Use any of the following code snippets:

```javascript
// Updating the value of a key
// This key is contained in the roles folder, and the second value is a role id, we also need
// to pass a GuildResolvable.
msg.guild.configs.update('roles.administrator', '339943234405007361', msg.guild);

// Updating an array
// userBlacklist, as mentioned in another tutorial, it's a piece with an array of users. Using
// the following code will add or remove it, depending on the existence of the key in the configuration.
msg.guild.configs.update('userBlacklist', '272689325521502208');

// Ensuring the function call adds (error if it exists)
msg.guild.configs.update('userBlacklist', '272689325521502208', { action: 'add' });

// Ensuring the function call removes (error if it doesn't exist)
msg.guild.configs.update('userBlacklist', '272689325521502208', { action: 'remove' });

// Updating it with a json object
// It's the same as the first code snippet. However, this pattern is slower.
msg.guild.configs.update({ roles: { administrator: '339943234405007361' } }, msg.guild);

// Updating multiple keys (only possible with json object)
msg.guild.configs.update({ prefix: 'k!', language: 'es-ES' });
```

> **Note**: Some types require a Guild instance to work, for example, *channels*, *roles* and *members*.
> Additionally, if no 'action' option is passed to {@link ConfigurationUpdateOptions}, it'll assume the `auto` mode, which will add or remove depending on the existence of the key.
## Further Reading:

- {@tutorial UnderstandingSchemaPieces}
- {@tutorial UnderstandingSchemaFolders}
- {@tutorial SettingGatewayKeyTypes}
66 changes: 66 additions & 0 deletions guides/Advanced SettingGateway/SettingGatewayKeyTypes.md
@@ -0,0 +1,66 @@
# SettingGateway's Types

By default, there are several built-in types that the developer can use, and with the possibility to add custom types via {@link Extendable}s as explained below. The built-in types are:

| Name | Type | Description |
| ------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| **any** | Anything, no type restriction | Resolves anything, even objects. The usage of this type will make a key unconfigurable |
| **boolean** | A {@link Boolean} resolvable | Resolves a boolean primitive value |
| **categorychannel** | A {@link external:CategoryChannel} instance or id | Resolves a CategoryChannel |
| **channel** | A {@link external:Channel} instance or id | Resolves a channel. Be careful with using this, as it accepts any type of channel |
| **command** | A {@link Command} instance or name | Resolves a Command |
| **emoji** | An {@link external:Emoji} instance or name | Resolves a custom emoji |
| **float** | A floating point number | Resolves a [float](https://en.wikipedia.org/wiki/Double-precision_floating-point_format) |
| **guild** | A {@link KlasaGuild} instance or id | Resolves a KlasaGuild (which extends Guild) |
| **integer** | An integer number | Resolves an [integer](https://en.wikipedia.org/wiki/Integer) number |
| **language** | A {@link Language} instance or name | Resolves a language |
| **member** | A {@link external:GuildMember} instance or id | Resolves a GuildMember |
| **msg** | A {@link KlasaMessage} instance or id | Resolves a KlasaMessage (which extends Message) |
| **role** | A {@link external:Role} instance or id | Resolves a Role |
| **string** | A {@link external:StringResolvable} | Resolves a string |
| **textchannel** | A {@link external:TextChannel} instance or id | Resolves a TextChannel |
| **url** | An URL resolvable | Resolves a URL with Node.js' URL parser |
| **user** | A {@link KlasaUser} instance or id | Resolves a KlasaUser (which extends User) |
| **voicechannel** | A {@link external:VoiceChannel} instance or id | Resolves a VoiceChannel |

## Adding new types

To add new types, you use an {@link Extendable} extending {@link SettingResolver}. If you don't know how to create an extendable, check the following tutorial: {@tutorial CreatingExtendables}. The following extendable is a template for this:

```javascript
const { Extendable } = require('klasa');

module.exports = class extends Extendable {

constructor(...args) {
super(...args, ['SettingResolver'], {
name: 'typeName',
klasa: true
});
}

/**
* Resolves my custom type!
* @param {*} data The data to resolve
* @param {KlasaGuild} guild The guild to resolve for
* @param {string} name The name of the key being resolved
* @param {Object} [minMax={}] The minimum and maximum
* @param {?number} minMax.min The minimum value
* @param {?number} minMax.max The maximum value
* @returns {Promise<*>}
*/
async extend(data, guild, name, { min, max } = {}) {
// The content
return data;
}

};
```

> **Note**: If a type does not load, you can add the type name to {@link GatewayDriver#types}, but it must be before the {@link SchemaPiece}s init as they check if the type is included in that Set.
## Further Reading:

- {@tutorial UnderstandingSchemaPieces}
- {@tutorial UnderstandingSchemaFolders}
- {@tutorial SettingGatewayConfigurationUpdate}
142 changes: 142 additions & 0 deletions guides/Advanced SettingGateway/UnderstandingSchemaFolders.md
@@ -0,0 +1,142 @@
# Understanding Schema

A schema works like a diagram or a blueprint, in SettingGateway, the schema defines the keys present in the configuration for a specific gateway. This feature serves multiple purposes:

1. Define what keys the {@link Gateway} manages and their properties.
1. Define what type the keys must hold.
1. Define the SQL schema when using a SQL database.
1. Speed up performance when iterating over keys.

## Adding keys

Adding keys with the schema is like adding a piece into a box, but you can also have boxes inside other boxes. That being said, you get the box you want to modify and insert the new pieces or boxes into it. The methods to achieve that are {@link SchemaFolder#add} to add pieces (keys) and boxes (folders).

You would normally use these two methods using the following snippet:

```javascript
// Add a new key or folder
this.client.gateways.gatewayName.schema.add(name, options, force);
```

The parameters are:

- **name**: The name of the new key. If it conflicts with a pre-existent key, this will error.
- **options**: The options for the new key or folder. Check {@link SchemaFolderAddOptions}.
- **force**: Whether this change should affect all entries. It requires a lot of processing but ensures the changes are correctly applied in both the cache and database.

You can also extend any of the three built-in {@link Gateway}s from Klasa. For example, if you want to add a new key called **modlogs** that accepts only text channels, for your guild configs, you would use the following code:

```javascript
this.client.gateways.guilds.schema.add('modlogs', { type: 'TextChannel' });
```

Where you're doing the following steps:

1. Access to {@link KlasaClient#gateways}, type of {@link GatewayDriver}, which holds all gateways.
1. Access to the guilds' {@link Gateway}, which manages the per-guild configuration.
1. Access to the guilds' schema via {@link Gateway#schema}, which manages the gateway's schema.
1. Add a new key called **modlogs** in the root of the schema, with a type of **TextChannel**.

And you would have a perfectly configured modlogs key in your configs. However, you can also have an array of the same type. For example, you want to have a configurable array of users blacklisted in a guild, in a key named **userBlacklist**:

```javascript
this.client.gateways.guilds.schema.add('userBlacklist', { type: 'User', array: true });
```

And now you can have access to any of them in your guild configs like in the following snippet!

```javascript
msg.guild.configs.modlogs;
// null
msg.guild.configs.userBlacklist;
// []
```

## Removing keys

Removing keys with the schema is quite easy, as you would have access to the {@link SchemaFolder} that holds it and remove it by its name (remember that `force` is optional and defaults to `true`) using {@link SchemaFolder#remove} as in the following example:

```javascript
this.client.gateways.gatewayName.schema.remove(name, force);
```

In case you have a key you do not longer use and you want to get rid of it, for example, the recently created **userBlacklist** key for guild configs, you would run the following code:

```javascript
this.client.gateways.guilds.schema.remove('userBlacklist');
```

And the property `userBlacklist` for all guild configs will be deleted.

## Adding folders

Folder creation is very similar to key creation, but with one key difference: it has no options for itself, but instead, it can create its children keys (just like you can add a box with other boxes and pieces, into another). You can add a new key inside a new folder in two different ways:

### Slower

You can create a folder, then create the keys, however, this will iterate over all entries twice:

```javascript
async function init() {
const { schema } = this.client.gateways.guilds;

await schema.add('channels', {});
await schema.channels.add('modlogs', { type: 'TextChannel' });
console.log(schema.channels.modlogs.toJSON());
// {
// type: 'textchannel',
// array: false,
// default: null,
// min: null,
// max: null,
// configurable: true
// }
}
```

### Faster

However, it's possible to create a folder with all the sub-keys (and even more nested folders) with the folder creation.

```javascript
async function init() {
const { schema } = this.client.gateways.guilds;

await schema.add('channels', { modlogs: { type: 'TextChannel' } });
console.log(schema.channels.modlogs.toJSON());
// {
// type: 'textchannel',
// array: false,
// default: null,
// min: null,
// max: null,
// configurable: true
// }
}
```

> **Reminder**: To access a key inside a folder in your configuration command, you use the access operator (`.`). For example: *k!conf set channels.modlogs #modlogs*
## Removing folders

Removing folders is the same as removing keys, check {@link SchemaFolder#remove}, the difference is that, while removing a key will remove one value from the schema, removing a folder will remove it with all its nested keys and folders, even very nested ones.

## Ensuring the existence of a key.

In [klasa pieces](https://github.com/dirigeants/klasa-pieces/) specially, some pieces require a key from the configuration to work, however, the creator of the pieces does not know if the user who downloads the piece has it, so this function becomes useful in this case.

```javascript
async function init() {
const { schema } = this.client.gateways.guilds;

if (!schema.has('modlog')) {
await schema.add('modlog', { type: 'TextChannel' });
}
}
```

## Further Reading:

- {@tutorial UnderstandingSchemaPieces}
- {@tutorial SettingGatewayKeyTypes}
- {@tutorial SettingGatewayConfigurationUpdate}

0 comments on commit 0bd3d4f

Please sign in to comment.