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

Commit

Permalink
misc: Finalized SchemaEntry's tests, documented more, moved resolve m…
Browse files Browse the repository at this point in the history
…ethods

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>
  • Loading branch information
kyranet and vladfrangu committed Nov 30, 2019
1 parent 7eb7def commit 4668d66
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 72 deletions.
32 changes: 17 additions & 15 deletions src/lib/gateway/GatewayStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,7 @@ export class GatewayStorage {
if (provider === null) throw new Error(`The gateway ${this.name} could not find the provider ${this._provider}.`);
this.ready = true;

const errors = [];
for (const entry of this.schema.values(true)) {
// Assign Client to all Pieces for Serializers && Type Checking
entry.client = this.client;

Object.freeze(entry);

// Check if the entry is valid
try {
entry.check();
} catch (error) {
errors.push(error.message);
}
}

const errors = [...this._checkSchemaFolder(this.schema)];
if (errors.length) throw new Error(`[SCHEMA] There is an error with your schema.\n${errors.join('\n')}`);

// Initialize the defaults
Expand Down Expand Up @@ -112,6 +98,22 @@ export class GatewayStorage {
};
}

private *_checkSchemaFolder(schema: Schema): IterableIterator<string> {
for (const value of schema.values()) {
if (value instanceof Schema) {

This comment was marked as resolved.

Copy link
@PyroTechniac

PyroTechniac Nov 30, 2019

Couldn't this just pass true to the values method to get a recursive iterator instead of checking if it's a schema?

This comment has been minimized.

Copy link
@kyranet

kyranet Nov 30, 2019

Author Member

No, because for each Schema/SchemaFolder, I do this assignment:

https://github.com/dirigeants/settings-gateway/blob/4668d667c8042d3d0690f027779af915d82f6074/src/lib/gateway/GatewayStorage.ts#L114

That way, when GatewayDriver initializes, all Schema/SchemaFolders become "ready", and they won't allow further calls to add/delete.

yield* this._checkSchemaFolder(value);
} else {
value.client = this.client;
try {
value.check();
} catch (error) {
yield error.message;
}
}
}
schema.ready = true;
}

}

export interface GatewayStorageOptions {
Expand Down
30 changes: 16 additions & 14 deletions src/lib/schema/Schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { isFunction } from '@klasa/utils';
import { Language } from 'klasa';
import { Guild } from 'discord.js';
import { SettingsFolder } from '../settings/SettingsFolder';
import { fromEntries } from '../polyfills';

Expand All @@ -26,7 +24,7 @@ export class Schema extends Map<string, SchemaFolder | SchemaEntry> {
/**
* Whether or not this instance is ready.
*/
private ready: boolean;
public ready: boolean;

/**
* Constructs the schema
Expand Down Expand Up @@ -164,15 +162,6 @@ export class Schema extends Map<string, SchemaFolder | SchemaEntry> {
return value;
}

public resolve(settings: SettingsFolder, language: Language, guild: Guild | null): Promise<unknown[]> {
const promises = [];
for (const entry of this.values(true)) {
promises.push(entry.resolve(settings, language, guild));
}

return Promise.all(promises);
}

/**
* Returns a new Iterator object that contains the keys for each element contained in this folder.
* Identical to [Map.keys()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys)
Expand All @@ -190,11 +179,16 @@ export class Schema extends Map<string, SchemaFolder | SchemaEntry> {
}

/**
* Returns a new Iterator object that contains the values for each element contained in this folder.
* Returns a new Iterator object that contains the values for each element contained in this folder and children folders.
* Identical to [Map.values()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values)
* @param recursive Whether the iteration should be recursive
*/
public values(recursive: true): IterableIterator<SchemaEntry>;
/**
* Returns a new Iterator object that contains the values for each element contained in this folder.
* Identical to [Map.values()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values)
* @param recursive Whether the iteration should be recursive
*/
public values(recursive?: false): IterableIterator<SchemaFolder | SchemaEntry>;
public *values(recursive = false): IterableIterator<SchemaFolder | SchemaEntry> {
if (recursive) {
Expand All @@ -208,11 +202,16 @@ export class Schema extends Map<string, SchemaFolder | SchemaEntry> {
}

/**
* Returns a new Iterator object that contains the `[key, value]` pairs for each element contained in this folder.
* Returns a new Iterator object that contains the `[key, value]` pairs for each element contained in this folder and children folders.
* Identical to [Map.entries()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries)
* @param recursive Whether the iteration should be recursive
*/
public entries(recursive: true): IterableIterator<[string, SchemaEntry]>;
/**
* Returns a new Iterator object that contains the `[key, value]` pairs for each element contained in this folder.
* Identical to [Map.entries()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries)
* @param recursive Whether the iteration should be recursive
*/
public entries(recursive?: false): IterableIterator<[string, SchemaFolder | SchemaEntry]>;
public *entries(recursive = false): IterableIterator<[string, SchemaFolder | SchemaEntry]> {
if (recursive) {
Expand All @@ -225,6 +224,9 @@ export class Schema extends Map<string, SchemaFolder | SchemaEntry> {
}
}

/**
* Returns an object literal composed of all children serialized recursively.
*/
public toJSON(): SchemaJson {
return fromEntries([...this.entries()].map(([key, value]) => [key, value.toJSON()]));
}
Expand Down
36 changes: 10 additions & 26 deletions src/lib/schema/SchemaEntry.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Language } from 'klasa';
import { Schema } from './Schema';
import { isNumber, isFunction } from '@klasa/utils';
import { SettingsFolder } from '../settings/SettingsFolder';
import { Guild } from 'discord.js';
import { SchemaFolder } from './SchemaFolder';
import { Client, SerializableValue } from '../types';
import { Serializer } from '../structures/Serializer';
Expand Down Expand Up @@ -117,22 +115,24 @@ export class SchemaEntry {
if (this.client === null) throw new Error('Cannot retrieve serializers from non-initialized SchemaEntry.');

// Check type
if (typeof this.type !== 'string') throw new TypeError(`[KEY] ${this.path} - Parameter type must be a string.`);
if (!this.client.serializers.has(this.type)) throw new TypeError(`[KEY] ${this.path} - ${this.type} is not a valid type.`);
if (typeof this.type !== 'string') throw new TypeError(`[KEY] ${this.path} - Parameter 'type' must be a string.`);
if (!this.client.serializers.has(this.type)) throw new TypeError(`[KEY] ${this.path} - '${this.type}' is not a valid type.`);

// Check array
if (typeof this.array !== 'boolean') throw new TypeError(`[KEY] ${this.path} - Parameter array must be a boolean.`);
if (typeof this.array !== 'boolean') throw new TypeError(`[KEY] ${this.path} - Parameter 'array' must be a boolean.`);

// Check configurable
if (typeof this.configurable !== 'boolean') throw new TypeError(`[KEY] ${this.path} - Parameter configurable must be a boolean.`);
if (typeof this.configurable !== 'boolean') throw new TypeError(`[KEY] ${this.path} - Parameter 'configurable' must be a boolean.`);

// Check limits
if (this.minimum !== null && !isNumber(this.minimum)) throw new TypeError(`[KEY] ${this.path} - Parameter min must be a number or null.`);
if (this.maximum !== null && !isNumber(this.maximum)) throw new TypeError(`[KEY] ${this.path} - Parameter max must be a number or null.`);
if (this.minimum !== null && this.maximum !== null && this.minimum > this.maximum) throw new TypeError(`[KEY] ${this.path} - Parameter min must contain a value lower than the parameter max.`);
if (this.minimum !== null && !isNumber(this.minimum)) throw new TypeError(`[KEY] ${this.path} - Parameter 'minimum' must be a number or null.`);
if (this.maximum !== null && !isNumber(this.maximum)) throw new TypeError(`[KEY] ${this.path} - Parameter 'maximum' must be a number or null.`);
if (this.minimum !== null && this.maximum !== null && this.minimum > this.maximum) {
throw new TypeError(`[KEY] ${this.path} - Parameter 'minimum' must contain a value lower than the parameter 'maximum'.`);
}

// Check filter
if (this.filter !== null && !isFunction(this.filter)) throw new TypeError(`[KEY] ${this.path} - Parameter filter must be a function`);
if (this.filter !== null && !isFunction(this.filter)) throw new TypeError(`[KEY] ${this.path} - Parameter 'filter' must be a function`);

// Check default
if (this.array) {
Expand All @@ -142,22 +142,6 @@ export class SchemaEntry {
}
}

public async resolve(settings: SettingsFolder, language: Language, guild: Guild | null): Promise<unknown> {
const values = settings.get(this.path);
if (typeof values === 'undefined') return undefined;

if (!this.shouldResolve) return values;

const { serializer } = this;
if (serializer === null) throw new Error('The serializer was not available during the resolve.');
if (this.array) {
return (await Promise.all((values as unknown as readonly SerializableValue[]).map(value => serializer.deserialize(value, this, language, guild))))
.filter(value => value !== null);
}

return serializer.deserialize(values, this, language, guild);
}

public toJSON(): SchemaEntryJson {
return {
type: this.type,
Expand Down
5 changes: 5 additions & 0 deletions src/lib/schema/SchemaFolder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ export class SchemaFolder extends Schema {
*/
public readonly key: string;

/**
* Constructs a SchemaFolder instance.
* @param parent The schema that manages this instance
* @param key This folder's key name
*/
public constructor(parent: Schema, key: string) {
super(parent.path.length === 0 ? key : `${parent.path}.${key}`);
this.parent = parent;
Expand Down
31 changes: 30 additions & 1 deletion src/lib/settings/SettingsFolder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ export class SettingsFolder extends Map<string, SerializableValue> {
const language = guild === null ? this.base.gateway.client.languages.default : guild.language;
return Promise.all(paths.map(path => {
const entry = this.schema.get(path);
return typeof entry === 'undefined' ? undefined : entry.resolve(this, language, guild);
if (typeof entry === 'undefined') return undefined;
return entry.type === 'Folder' ?
this._resolveFolder(entry as SchemaFolder, language, guild) :
this._resolveEntry(entry as SchemaEntry, language, guild);
}));
}

Expand Down Expand Up @@ -299,6 +302,32 @@ export class SettingsFolder extends Map<string, SerializableValue> {
}
}

private async _resolveFolder(schemaFolder: SchemaFolder, language: Language, guild: Guild | null): Promise<unknown[]> {
const promises = [];
for (const entry of schemaFolder.values(true)) {
promises.push(this._resolveEntry(entry, language, guild));
}

return Promise.all(promises);
}

private async _resolveEntry(schemaEntry: SchemaEntry, language: Language, guild: Guild | null): Promise<unknown> {
const values = this.get(schemaEntry.path);
if (typeof values === 'undefined') return undefined;

if (!schemaEntry.shouldResolve) return values;

const { serializer } = schemaEntry;
if (serializer === null) throw new Error('The serializer was not available during the resolve.');
if (schemaEntry.array) {
return (await Promise.all((values as unknown as readonly SerializableValue[])
.map(value => serializer.deserialize(value, schemaEntry, language, guild))))
.filter(value => value !== null);
}

return serializer.deserialize(values, schemaEntry, language, guild);
}

private _resetSchemaFolder(changes: SettingsUpdateResults, schemaFolder: SchemaFolder, key: string, language: Language, onlyConfigurable: boolean): void {
let nonConfigurable = 0;
let skipped = 0;
Expand Down
15 changes: 4 additions & 11 deletions src/lib/structures/SQLProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,35 +95,28 @@ export abstract class SQLProvider extends Provider {
* @param table The table to check against
* @param entry The SchemaFolder or SchemaEntry added to the schema
*/
public abstract addColumn(_table: string, _entry: SchemaFolder | SchemaEntry): Promise<void>;
public abstract addColumn(table: string, entry: SchemaFolder | SchemaEntry): Promise<void>;

/**
* The removeColumn method which inserts/creates a new table to the database.
* @since 0.5.0
* @param table The table to check against
* @param columns The column names to remove
*/
public abstract removeColumn(_table: string, _columns: readonly string[]): Promise<void>;
public abstract removeColumn(table: string, columns: readonly string[]): Promise<void>;

/**
* The updateColumn method which alters the datatype from a column.
* @param table The table to check against
* @param entry The modified SchemaEntry
*/
public abstract updateColumn(_table: string, _entry: SchemaEntry): Promise<void>;
public abstract updateColumn(table: string, entry: SchemaEntry): Promise<void>;

/**
* The getColumns method which gets the name of all columns.
* @param table The table to check against
*/
public abstract getColumns(_table: string): Promise<string[]>;

/**
* Shutdown method, this is called before the piece is unloaded.
*/
public async shutdown(): Promise<void> {
// Optionally defined in extension Classes
}
public abstract getColumns(table: string): Promise<string[]>;

/**
* The query builder debug check for errors in the QueryBuilder, if one exists in the extended SQLProvider instance
Expand Down

0 comments on commit 4668d66

Please sign in to comment.