Skip to content

Commit

Permalink
fix: build errors
Browse files Browse the repository at this point in the history
  • Loading branch information
didinele committed Sep 9, 2023
1 parent ed211a9 commit e7eae5d
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 22 deletions.
15 changes: 8 additions & 7 deletions packages/core/src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ Please do not modify the [`db.ts` file](./db.ts) in this directory. It is automa
Use `yarn prisma generate` in the root directory to reflect schema changes.

A couple of things to note about this codebase:

- There are areas where we leverage sort of out-of-pattern factory singletons. This is preferred over just binding
a proper factory in the container because it:
1) Allows us to rely on implicit resolution of the factory without the need to `@inject()` a symbol.
2) Is less boilerplate overall.
a proper factory in the container because it:
1. Allows us to rely on implicit resolution of the factory without the need to `@inject()` a symbol.
2. Is less boilerplate overall.
- One thing to note about this approach is that we do theoretically risk a footgun here, but because
those factory classes are so incredibly simple and should always return `IX` (interfaces), it should never
ever be an issue.
those factory classes are so incredibly simple and should always return `IX` (interfaces), it should never
ever be an issue.
- When we use `snake_case` in our own types, it's generally because those properties are directly mapped from Discord's
API.
API.
- Generally, we only use `constructor(private readonly foo: Foo)` to signal a dependency being injected,
other sorts of parameters should be taken in as usual and assigned in the constructor.
other sorts of parameters should be taken in as usual and assigned in the constructor.
4 changes: 4 additions & 0 deletions packages/core/src/binary-encoding/RWFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { Reader } from './Reader.js';
import { Writer } from './Writer.js';

@injectable()
/**
* @remarks
* Obviously, those are not singletons.
*/
export class RWFactory {
public buildReader(data: Buffer): IReader {
return new Reader(data);
Expand Down
15 changes: 14 additions & 1 deletion packages/core/src/cache/CacheFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,23 @@ import { Cache } from './Cache.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.
*/
export class CacheFactory {
private readonly caches = new WeakMap<ICacheEntity<unknown>, Cache<unknown>>();

public constructor(@inject(INJECTION_TOKENS.redis) private readonly redis: Redis) {}

public build<T>(entity: ICacheEntity<T>): Cache<T> {
return new Cache<T>(this.redis, entity);
if (this.caches.has(entity)) {
return this.caches.get(entity)! as Cache<T>;
}

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

return cache;
}
}
9 changes: 5 additions & 4 deletions packages/core/src/cache/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# cache

This is where we deal with 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)
for more information.
Refer to the [`binary-encoding` directory](../binary-encoding)
for more information.
- `Cache` is deliberately not decorated with `@injectable()`, because we have a rather dynamic dependency
of a `CacheEntity`, responsible for specific encoding/decoding ops, defining the cache key and TTL of the
data and so on. As such, we use a factory singleton to create `Cache` instances.
of a `CacheEntity`, responsible for specific encoding/decoding ops, defining the cache key and TTL of the
data and so on. As such, we use a factory singleton to create `Cache` instances.
2 changes: 2 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ export * from './util/factoryFrom.js';
export * from './util/PermissionsBitField.js';
export * from './util/promiseAllObject.js';
export * from './util/sqlJson.js';

export * from './db.js';
26 changes: 16 additions & 10 deletions services/discord-proxy/src/cache.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import type { Cache } from '@automoderator/core';
import { GuildCache, globalContainer } from '@automoderator/core';
import type { ICacheEntity } from '@automoderator/core';
import { globalContainer, CacheFactory, type Cache, INJECTION_TOKENS } from '@automoderator/core';
import { injectable } from 'inversify';

type CacheConstructor = new () => Cache<unknown>;

@injectable()
export class ProxyCache {
private readonly cacheConstructorsMap: Record<string, CacheConstructor> = {
'/guilds/:id': GuildCache,
public constructor(private readonly cacheFactory: CacheFactory) {}

private readonly cacheEntityTokensMap: Record<string, symbol> = {
'/guilds/:id': INJECTION_TOKENS.cacheEntities.guild,
};

private readonly idResolversMap: Record<string, (parameters: string[]) => string> = {
Expand All @@ -17,11 +19,13 @@ export class ProxyCache {
public async fetch(route: string): Promise<unknown> {
const [normalized, parameters] = this.normalizeRoute(route);

const cacheConstructor = this.cacheConstructorsMap[normalized];
const cacheEntityToken = this.cacheEntityTokensMap[normalized];
const idResolver = this.idResolversMap[normalized];

if (cacheConstructor && idResolver) {
const cache = globalContainer.get<Cache<unknown>>(cacheConstructor);
if (cacheEntityToken && idResolver) {
const cacheEntity = globalContainer.get<ICacheEntity<unknown>>(cacheEntityToken);
const cache = this.cacheFactory.build(cacheEntity);

return cache.get(idResolver(parameters));
}

Expand All @@ -31,11 +35,13 @@ export class ProxyCache {
public async update(route: string, data: unknown): Promise<void> {
const [normalized, parameters] = this.normalizeRoute(route);

const cacheConstructor = this.cacheConstructorsMap[normalized];
const cacheEntityToken = this.cacheEntityTokensMap[normalized];
const idResolver = this.idResolversMap[normalized];

if (cacheConstructor && idResolver) {
const cache = globalContainer.get<Cache<unknown>>(cacheConstructor);
if (cacheEntityToken && idResolver) {
const cacheEntity = globalContainer.get<ICacheEntity<unknown>>(cacheEntityToken);
const cache = this.cacheFactory.build(cacheEntity);

await cache.set(idResolver(parameters), data);
}
}
Expand Down

0 comments on commit e7eae5d

Please sign in to comment.