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

Commit

Permalink
feat: First step at the rewrite (#1)
Browse files Browse the repository at this point in the history
* feat: First step at the rewrite

* feat: Added more progress

* feat: Added more things

* git: Why do we not ignore yarn-error.log?

* refactor: Move code around and finished Settings and GatewayDriver

* misc: Deleted yarn-error.log

* feat: Added all structures

* lint: Fixed several ESLint warnings

* build: Resolved all build errors

* fix: Resovled circular references

ESM for when?

* tests: Added first mock classes and fixed a few things

* tests: Add a bunch of tests and fixed a few things

* misc: More tests, finalized SG and fixed bugs

* polyfill: Added ObjectFromEntries for Node.js v10 compatibility

* misc: Fixed SchemaEntry#edit, added examples and tests

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>

* docs: I heard you liked examples

* docs: Added more examples

* misc: Finalized SchemaEntry's tests, documented more, moved resolve methods

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>

* fix: Better `_checkSchemaFolder` and freeze SchemaEntry instances

Invalid entries should be removed, they should never stay as it corrupts the data.

* misc: Refactor code and added more tests

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>

* tests: Fixed Settings tests

* misc: Resolved bug in Settings upsert code, added more tests

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>

* chore: Update all dependencies

* build: Enable declarationMap

* ci: Use Crawl's action and Node.js 12

* fix: Resolved several bugs and added more tests

* tests: Removed snapshot testing

Favna has explained them to me 6 times and I still don't get it.
Plus it errors on Linux, so...

* tests: Added a lot of SettingsFolder#reset tests, fixed several things

* tests: Finalize SettingsFolder#reset's tests

* fix: Resolved several bugs and added some SF#update tests

* fix: Resolved type issues and another bug

* test: Added more SF#update tests and more hooks for Serializer

* tests: Added array action tests

* tests: Added ArrayIndex tests

With almost 1000 lines, SettingsFolder's tests are now finished.

* docs: Added LOTS OF DOCS

* docs: Added CHANGELOG

* fix: Generate TS compiler error to stop unexpected behaviour.

The `arrayAction: 'overwrite'` and `arrayIndex` options are mutually exclusive, this patch increases verbosity but fixes this behaviour on compile time.

For JavaScript users... either use `// @ts-check`, or you're on your own. Either way this is more likely to affect advanced users, since they're the ones who will mess with those options, definitely not the ones who are new to SettingsGateway.

* misc: Resolved code review by @vladfrangu

* misc: Resolved code review by @favna

* git: Ignore all .xml and .log files

* chore: Update all dependencies

* package: Update the name, description, and repository.url

* npm: Add .github and remove unused/inexistent files

* Update src/lib/schema/Schema.ts

Co-Authored-By: Gryffon Bellish <owenbellish@gmail.com>

* misc: Resolved all requested changes from @vladfrangu and @favna

* style: Re-add `_` prefix to methods users are not supposed to use
  • Loading branch information
kyranet committed Dec 9, 2019
1 parent 4c6b0e0 commit bf60f41
Show file tree
Hide file tree
Showing 38 changed files with 4,480 additions and 293 deletions.
14 changes: 7 additions & 7 deletions .github/workflows/codequality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ jobs:
steps:
- name: Checkout Project
uses: actions/checkout@v1
- name: Use Node.js 10
- name: Use Node.js 12
uses: actions/setup-node@v1
with:
node-version: 10
node-version: 12
- name: Restore CI Cache
uses: actions/cache@v1
with:
path: node_modules
key: ${{ runner.os }}-10-${{ hashFiles('**/yarn.lock') }}
key: ${{ runner.os }}-12-${{ hashFiles('**/yarn.lock') }}
- name: Install Dependencies
run: yarn
- name: Run ESLint
uses: discordjs/action-eslint@v1
uses: icrawl/action-eslint@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
Expand All @@ -38,15 +38,15 @@ jobs:
steps:
- name: Checkout Project
uses: actions/checkout@v1
- name: Use Node.js 10
- name: Use Node.js 12
uses: actions/setup-node@v1
with:
node-version: 10
node-version: 12
- name: Restore CI Cache
uses: actions/cache@v1
with:
path: node_modules
key: ${{ runner.os }}-10-${{ hashFiles('**/yarn.lock') }}
key: ${{ runner.os }}-12-${{ hashFiles('**/yarn.lock') }}
- name: Install Dependencies
run: yarn
- name: Run TSC
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ coverage/
.nyc_output/
dist/
docs/
test-results.xml
**/*.xml
**/*.log
18 changes: 4 additions & 14 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
# .npmignore
.get/
.github/
coverage/
node_modules/
test/
docs/
scripts/
src/
.docstrap.json
test/
.eslintrc.json
tslint.json
.travis.yml
appveyor.yml
tsconfig.json
package-lock.json
README.md
tsconfig.json
yarn.lock
coverage/
.nyc_output/
azure-pipelines.yml
test-results.xml
54 changes: 54 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Change Log

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

<!--
NOTE: For the contributors, you add new entries to this document following this format:
- [[#PRNUMBER](https://github.com/dirigeants/settings-gateway/pull/PRNUMBER)] The change that has been made. (Author's Github name)
-->

## 0.0.1

### Added

- [[#1][]] Added Unit Testing. (kyranet)
- [[#1][]] Added examples for a large set of methods. (kyranet)
- [[#1][]] Added bare `null` as an option to reset a key in `SettingsFolder`#{`update`}. (kyranet)
- [[#1][]] Added `Serializer`#{`validate`,`resolve`} to allow further control on how SettingsGateway handles the data. (kyranet)
- [[#1][]] Added `context` to the `settingsUpdate` and `settingsCreate` events, they contain the raw changes, guild, language, etc. (kyranet)
- [[#1][]] Added `extraContext` to the `SettingsFolderResetOptions` type, this value is pased in all places (`Serializer`#`validate`, `SchemaEntry`#`filter`, `settingsUpdate` and `settingsCreate` events, and more). (kyranet)

### Changed

- [[#1][]] Tweaked `Serializer`#`deserialize`'s arguments to (`SerializableValue`, `SerializerUpdateContext`). (kyranet)
- [[#1][]] Renamed `SchemaEntry`#{`min`,`max`} to `SchemaEntry`#{`minimum`,`maximum`}. (kyranet)
- [[#1][]] Tweaked `SettingsFolder`'s value type to be more accurate. (kyranet)
- [[#1][]] Tweaked `SettingsFolderUpdateOptions`'s option to produce a TypeScript compiler error when `arrayAction` is set to `'overwrite'` and `arrayIndex` is defined. (kyranet)
- [[#1][]] Tweaked `SettingsFolder`#`client` to throw an error when it's uninitialized. (kyranet)
- [[#1][]] Tweaked `SettingsFolder`#{`reset`,`update`} to return a much more useful struct. (kyranet)
- [[#1][]] When specifying `arrayIndex` in `SettingsFolder`#`update` and `arrayAction` is defined as `add`, all entries will be inserted at given index. (kyranet)
- [[#1][]] When specifying `arrayIndex` in `SettingsFolder`#`update` and `arrayAction` is defined as `remove`, as many entries as given will be removed from given index. (kyranet)
- [[#1][]] When specifying `arrayIndex` in `SettingsFolder`#`update` and `arrayAction` is not defined or defined as `auto`, all entries will replace the existing ones. (kyranet)

### Removed

- [[#1][]] Removed `throwOnError` option in `SettingsFolder`#{`reset`,`update`}, they will now always throw when they encounter an error. (kyranet)

### Fixed

- [[#1][]] Resolved bug where `Schema`#`get` would throw an error if a path did not exist. (kyranet)
- [[#1][]] Resolved bug where `Schema`#{`add`,`remove`} was still callable even after being initialized. (kyranet)
- [[#1][]] Resolved bug where `SettingsFolder`#`get` would throw an error if a path did not exist. (kyranet)
- [[#1][]] Resolved type bug in `SettingsFolder`#`pluck`. (kyranet)
- [[#1][]] Resolved bug in `SettingsFolder`#`resolve` not resolving into objects when specifying a folder path. (kyranet)
- [[#1][]] Resolved bug in `SettingsFolder`#`reset` where database conditions were not handled correctly. (kyranet)
- [[#1][]] Resolved bug in `SettingsFolder`#`update` where options would sometimes type error. (kyranet)
- [[#1][]] Resolved bug in `SettingsFolder`'s patch function not allowing non-literal objects to be used. (kyranet)
- [[#1][]] Resolved bug in `SettingsFolder`#{`reset`,`update`} patching after emit. (kyranet)
- [[#1][]] Fixed the types from the `Provider` and `SQLProvider` classes. (kyranet)

<!-- References, they're to shorten lines -->
[#1]: https://github.com/dirigeants/settings-gateway/pull/1
30 changes: 21 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@klasa/util",
"name": "@klasa/settings-gateway",
"version": "0.0.1",
"description": "some util thing.",
"description": "The centralized settings system designed for the Klasa framework.",
"main": "dist/index.js",
"scripts": {
"prepublishOnly": "yarn build",
Expand All @@ -17,20 +17,26 @@
"engines": {
"node": ">=10.1.0"
},
"peerDependencies": {
"klasa": "dirigeants/klasa"
},
"devDependencies": {
"@types/node": "^12.7.3",
"@typescript-eslint/eslint-plugin": "^2.0.0",
"@typescript-eslint/parser": "^2.0.0",
"@types/node": "^12.12.15",
"@types/ws": "^6.0.4",
"@typescript-eslint/eslint-plugin": "^2.10.0",
"@typescript-eslint/parser": "^2.10.0",
"ava": "^2.3.0",
"eslint": "^6.3.0",
"discord.js": "discordjs/discord.js",
"eslint": "^6.7.2",
"eslint-config-klasa": "dirigeants/klasa-lint",
"klasa": "dirigeants/klasa#875/head",
"source-map-support": "^0.5.13",
"ts-node": "^8.3.0",
"typescript": "^3.6.2"
"ts-node": "^8.5.4",
"typescript": "^3.7.3"
},
"repository": {
"type": "git",
"url": "https://github.com/dirigeants/util"
"url": "https://github.com/dirigeants/settings-gateway"
},
"ava": {
"compileEnhancements": false,
Expand All @@ -45,5 +51,11 @@
"ts-node/register",
"source-map-support/register"
]
},
"dependencies": {
"@discordjs/collection": "^0.1.3",
"@klasa/querybuilder": "^0.0.1",
"@klasa/request-handler": "^0.0.3",
"@klasa/utils": "^0.0.4"
}
}
17 changes: 14 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
export default function foo(): string {
return 'bar';
}
export * from './lib/gateway/Gateway';
export * from './lib/gateway/GatewayDriver';
export * from './lib/gateway/GatewayStorage';
export * from './lib/schema/Schema';
export * from './lib/schema/SchemaEntry';
export * from './lib/schema/SchemaFolder';
export * from './lib/settings/Settings';
export * from './lib/settings/SettingsFolder';
export * from './lib/structures/Provider';
export * from './lib/structures/SQLProvider';
export * from './lib/structures/ProviderStore';
export * from './lib/structures/Serializer';
export * from './lib/structures/SerializerStore';
export * from './lib/types';
97 changes: 97 additions & 0 deletions src/lib/gateway/Gateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { RequestHandler, IdKeyed } from '@klasa/request-handler';
import Collection from '@discordjs/collection';
import { GatewayStorage } from './GatewayStorage';
import { Settings } from '../settings/Settings';
import { Client } from '../types';

export class Gateway extends GatewayStorage {

/* eslint-disable no-invalid-this */
/**
* The cached entries for this Gateway or the external datastore to get the settings from.
*/
public cache: ProxyMap = (this.name in this.client) && (this.client[this.name as keyof Client] instanceof Map) ?
this.client[this.name as keyof Client] as ProxyMap :
new Collection<string, ProxyMapEntry>();

/**
* The request handler that manages the synchronization queue.
*/
public requestHandler = new RequestHandler(
(id: string): Promise<IdKeyed<string>> => {
const { provider } = this;
return provider === null ?
Promise.reject(new Error('Cannot run requests without a provider available.')) :
provider.get(this.name, id) as Promise<IdKeyed<string>>;
}, (ids: string[]): Promise<IdKeyed<string>[]> => {
const { provider } = this;
return provider === null ?
Promise.reject(new Error('Cannot run requests without a provider available.')) :
provider.getAll(this.name, ids) as Promise<IdKeyed<string>[]>;
}
);
/* eslint-enable no-invalid-this */

/**
* Gets an entry from the cache or creates one if it does not exist
* @param target The target that holds a Settings instance of the holder for the new one
* @param id The settings' identificator
* @example
* // Retrieve a members gateway
* const gateway = this.client.gateways.get('members');
*
* // Acquire a settings instance belonging to a member
* gateway.acquire(message.member);
*/
public acquire(target: IdKeyed<string>, id = target.id): Settings {
return this.get(id) || this.create(target, id);
}

/**
* Get an entry from the cache.
* @param id The key to get from the cache
* @example
* // Retrieve a members gateway
* const gateway = this.client.gateways.get('members');
*
* // Retrieve a settings instance belonging to a member's id
* const settings = gateway.get(someMemberID);
*
* // Do something with it, be careful as it can return null
* if (settings === null) {
* // settings is null
* } else {
* // console.log(settings);
* }
*/
public get(id: string): Settings | null {
const entry = this.cache.get(id);
return (entry && entry.settings) || null;
}

/**
* Create a new Settings instance for this gateway.
* @param target The target that will hold this instance alive
* @param id The settings' identificator
*/
public create(target: IdKeyed<string>, id = target.id): Settings {
const settings = new Settings(this, target, id);
if (this.schema.size !== 0) settings.sync(true).catch(err => this.client.emit('error', err));
return settings;
}

/**
* Runs a synchronization task for the gateway.
*/
public async sync(): Promise<this> {
await this.requestHandler.wait();
return this;
}

}

export interface ProxyMapEntry {
settings: Settings;
}

export type ProxyMap = Map<string, ProxyMapEntry>;
66 changes: 66 additions & 0 deletions src/lib/gateway/GatewayDriver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { GatewayStorage, GatewayStorageJson } from './GatewayStorage';
import { Client } from '../types';
import { fromEntries } from '../polyfills';
import Collection from '@discordjs/collection';

export class GatewayDriver extends Collection<string, GatewayStorage> {

/**
* The client this GatewayDriver was created with.
*/
public readonly client: Client;

/**
* Constructs a new instance of GatewayDriver.
* @param client The client that manages this instance
*/
public constructor(client: Client) {
super();
this.client = client;
}

/**
* Registers a new gateway.
* @param gateway The gateway to register
* @example
* // Import Client and Gateway from klasa
* const { Client, Gateway } = require('klasa');
*
* // Construct the client and register a gateway named channels
* const client = new Client();
* client.register(new Gateway(client, 'channels'));
*
* @example
* // Import Client and Gateway from klasa
* const { Client, Gateway } = require('klasa');
* const client = new Client();
*
* // register calls can be chained
* client
* .register(new Gateway(client, 'channels'))
* .register(new GatewayStorage(client, 'moderations', { provider: 'postgres' }));
*/
public register(gateway: GatewayStorage): this {
if (typeof this.client.options.settings.gateways === 'undefined') this.client.options.settings.gateways = { [gateway.name]: {} };
if (!(gateway.name in this.client.options.settings.gateways)) this.client.options.settings.gateways[gateway.name] = {};
this.set(gateway.name, gateway);
return this;
}

/**
* Initializes all gateways.
*/
public async init(): Promise<void> {
await Promise.all([...this.values()].map(gateway => gateway.init()));
}

/**
* The gateway driver with all serialized gateways.
*/
public toJSON(): GatewayDriverJson {
return fromEntries([...this.entries()].map(([key, value]) => [key, value.toJSON()]));
}

}

export type GatewayDriverJson = Record<string, GatewayStorageJson>;

0 comments on commit bf60f41

Please sign in to comment.