Skip to content

Commit

Permalink
refactor: new OO patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
didinele committed Jun 20, 2024
1 parent 62cd6ed commit 3cb7b3b
Show file tree
Hide file tree
Showing 55 changed files with 261 additions and 1,947 deletions.
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"inversify": "^6.0.1",
"ioredis": "^5.3.2",
"kysely": "^0.26.3",
"murmurhash": "^2.0.1",
"pg": "^8.11.3",
"pino": "^8.15.1",
"pino-pretty": "^10.2.0",
Expand Down
49 changes: 0 additions & 49 deletions packages/core/src/actions/IModAction.ts

This file was deleted.

8 changes: 0 additions & 8 deletions packages/core/src/actions/README.md

This file was deleted.

105 changes: 0 additions & 105 deletions packages/core/src/actions/RestrictModAction.ts

This file was deleted.

11 changes: 11 additions & 0 deletions packages/core/src/applicationData/IDataManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { Selectable } from 'kysely';
import type { Experiment, ExperimentOverride } from '../db.js';

export type ExperimentWithOverrides = Selectable<Experiment> & { overrides: Selectable<ExperimentOverride>[] };

/**
* Abstraction over all database interactions (things like Redis included)
*/
export abstract class IDataManager {
public abstract getExperiments(): Promise<ExperimentWithOverrides[]>;
}
34 changes: 34 additions & 0 deletions packages/core/src/applicationData/KyselyDataManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { Kysely } from 'kysely';
import { jsonArrayFrom } from 'kysely/helpers/postgres';
import type { DB } from '../db.js';
import { IDataManager, type ExperimentWithOverrides } from './IDataManager.js';

/**
* Our current databse implementation, using kysely with types generated by prisma-kysely
*/
export class KyselyDataManager extends IDataManager {
readonly #database: Kysely<DB>;

// Note that we avoid using an actual dependency. This is because we don't really want to expose database
// into our container. An implementation over IDataHandler should always be used.
public constructor(database: Kysely<DB>) {
super();

this.#database = database;
}

public override async getExperiments(): Promise<ExperimentWithOverrides[]> {
return this.#database
.selectFrom('Experiment')
.selectAll()
.select((query) => [
jsonArrayFrom(
query
.selectFrom('ExperimentOverride')
.selectAll('ExperimentOverride')
.whereRef('Experiment.name', '=', 'ExperimentOverride.experimentName'),
).as('overrides'),
])
.execute();
}
}
2 changes: 1 addition & 1 deletion packages/core/src/binary-encoding/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# binary-encoding

Custom encoding we use for storing cache.
Custom encoding we use for storing Discord data cache.
20 changes: 0 additions & 20 deletions packages/core/src/binary-encoding/RWFactory.ts

This file was deleted.

28 changes: 0 additions & 28 deletions packages/core/src/broker-types/logging.ts

This file was deleted.

7 changes: 3 additions & 4 deletions packages/core/src/cache/CacheFactory.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { inject, injectable } from 'inversify';
import { inject } from 'inversify';
import { Redis } from 'ioredis';
import { INJECTION_TOKENS } from '../container.js';
import { Cache } from './Cache.js';
import type { ICache } from './ICache.js';
import { RedisCache } from './RedisCache.js';
import type { ICacheEntity } from './entities/ICacheEntity';

@injectable()
/**
* @remarks
* Since this is a singleton factroy, we "cache our caches" in a WeakMap to avoid additional computation on subsequent calls.
Expand All @@ -20,7 +19,7 @@ export class CacheFactory {
return this.caches.get(entity)! as ICache<T>;
}

const cache = new Cache<T>(this.redis, entity);
const cache = new RedisCache<T>(this.redis, entity);
this.caches.set(entity, cache);

return cache;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/cache/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# cache

This is where we deal with caching. A couple of things to note:
This is where we deal with Discord data caching. A couple of things to note:

- we use our own encoding format for cache to make encoding/decoding and space usage as efficient as possible.
Refer to the [`binary-encoding` directory](../binary-encoding)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { ICacheEntity } from './entities/ICacheEntity.js';
* This class is deliberately not an `@injectable()`, refer to the README for more information on the pattern
* being used.
*/
export class Cache<T> implements ICache<T> {
export class RedisCache<T> implements ICache<T> {
public constructor(
private readonly redis: Redis,
private readonly entity: ICacheEntity<T>,
Expand Down
15 changes: 4 additions & 11 deletions packages/core/src/cache/entities/GuildCacheEntity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Buffer } from 'node:buffer';
import { injectable } from 'inversify';
import { RWFactory } from '../../binary-encoding/RWFactory.js';
import { Reader } from '../../binary-encoding/Reader.js';
import { Writer } from '../../binary-encoding/Writer.js';
import type { ICacheEntity } from './ICacheEntity';

export interface CachedGuild {
Expand All @@ -12,26 +13,18 @@ export interface CachedGuild {

@injectable()
export class GuildCacheEntity implements ICacheEntity<CachedGuild> {
public constructor(private readonly rwFactory: RWFactory) {}

public readonly TTL = 60_000;

public makeKey(id: string): string {
return `guild:${id}`;
}

public toBuffer(guild: CachedGuild): Buffer {
return this.rwFactory
.buildWriter(200)
.u64(guild.id)
.string(guild.icon)
.string(guild.name)
.u64(guild.owner_id)
.dumpTrimmed();
return new Writer(200).u64(guild.id).string(guild.icon).string(guild.name).u64(guild.owner_id).dumpTrimmed();
}

public toJSON(data: Buffer): CachedGuild {
const reader = this.rwFactory.buildReader(data);
const reader = new Reader(data);

const decoded: CachedGuild = {
id: reader.u64()!.toString(),
Expand Down
9 changes: 5 additions & 4 deletions packages/core/src/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ export const globalContainer = new Container({
});

export const INJECTION_TOKENS = {
redis: Symbol('redis instance'),
logger: Symbol('logger instance'),
/**
* @remarks
* Not to be used explicitly ever. There should always be abstraction classes for interactions with redis
*/
redis: Symbol('redis instance'),
cacheEntities: {
guild: Symbol('guild cache entity'),
},
actions: {
restrict: Symbol('restrict action'),
},
} as const;
Loading

0 comments on commit 3cb7b3b

Please sign in to comment.