Add support for jobs & queueables, migrations
- Create migration directives & migrators - Modify Cache classes to support array manipulation - Create Redis unit and RedisCache implementation - Create Queueable base class and Queue class that uses Cache backend
This commit is contained in:
37
src/util/cache/Cache.ts
vendored
37
src/util/cache/Cache.ts
vendored
@@ -15,8 +15,9 @@ export abstract class Cache {
|
||||
* Store the given value in the cache by key.
|
||||
* @param {string} key
|
||||
* @param {string} value
|
||||
* @param expires
|
||||
*/
|
||||
public abstract put(key: string, value: string): Awaitable<void>;
|
||||
public abstract put(key: string, value: string, expires?: Date): Awaitable<void>;
|
||||
|
||||
/**
|
||||
* Check if the cache has the given key.
|
||||
@@ -30,4 +31,38 @@ export abstract class Cache {
|
||||
* @param {string} key
|
||||
*/
|
||||
public abstract drop(key: string): Awaitable<void>;
|
||||
|
||||
/**
|
||||
* Fetch an item from the cache by key, and then remove it.
|
||||
* @param key
|
||||
*/
|
||||
public abstract pop(key: string): Awaitable<string|undefined>;
|
||||
|
||||
/**
|
||||
* Increment a key in the cache by a given amount.
|
||||
* @param key
|
||||
* @param amount
|
||||
*/
|
||||
public abstract increment(key: string, amount?: number): Awaitable<number|undefined>;
|
||||
|
||||
/**
|
||||
* Decrement a key in the cache by a given amount.
|
||||
* @param key
|
||||
* @param amount
|
||||
*/
|
||||
public abstract decrement(key: string, amount?: number): Awaitable<number|undefined>;
|
||||
|
||||
/**
|
||||
* Push an item onto the end an array-like key.
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public abstract arrayPush(key: string, value: string): Awaitable<void>;
|
||||
|
||||
/**
|
||||
* Remove and return an item from the beginning of an array-like key.
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public abstract arrayPop(key: string): Awaitable<string|undefined>;
|
||||
}
|
||||
|
||||
59
src/util/cache/InMemCache.ts
vendored
59
src/util/cache/InMemCache.ts
vendored
@@ -1,5 +1,7 @@
|
||||
import { Cache } from './Cache'
|
||||
import { Collection } from '../collection/Collection'
|
||||
import {Awaitable, Maybe} from '../support/types'
|
||||
import {ErrorWithContext} from '../error/ErrorWithContext'
|
||||
|
||||
/**
|
||||
* Base interface for an item stored in a memory cache.
|
||||
@@ -44,4 +46,61 @@ export class InMemCache extends Cache {
|
||||
public async drop(key: string): Promise<void> {
|
||||
this.items = this.items.whereNot('key', '=', key)
|
||||
}
|
||||
|
||||
public pop(key: string): Awaitable<Maybe<string>> {
|
||||
const existing = this.items.firstWhere('key', '=', key)
|
||||
this.items = this.items.where('key', '!=', key)
|
||||
return existing?.item
|
||||
}
|
||||
|
||||
public async increment(key: string, amount?: number): Promise<number> {
|
||||
const next = parseInt((await this.fetch(key)) ?? '0', 10) + (amount ?? 1)
|
||||
await this.put(key, String(next))
|
||||
return next
|
||||
}
|
||||
|
||||
public async decrement(key: string, amount?: number): Promise<number> {
|
||||
const next = parseInt((await this.fetch(key)) ?? '0', 10) - (amount ?? 1)
|
||||
await this.put(key, String(next))
|
||||
return next
|
||||
}
|
||||
|
||||
public arrayPush(key: string, value: string): Awaitable<void> {
|
||||
const existing = this.items.where('key', '=', key).first()
|
||||
const arr = JSON.parse(existing?.item ?? '[]')
|
||||
|
||||
if ( !Array.isArray(arr) ) {
|
||||
throw new ErrorWithContext('Unable to arrayPush: key is not an array', {
|
||||
key,
|
||||
value,
|
||||
})
|
||||
}
|
||||
|
||||
arr.push(value)
|
||||
if ( existing ) {
|
||||
existing.item = JSON.stringify(arr)
|
||||
} else {
|
||||
this.items.push({
|
||||
key,
|
||||
item: JSON.stringify(arr),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public arrayPop(key: string): Awaitable<Maybe<string>> {
|
||||
const existing = this.items.where('key', '=', key).first()
|
||||
const arr = JSON.parse(existing?.item ?? '[]')
|
||||
|
||||
const value = arr.pop()
|
||||
if ( existing ) {
|
||||
existing.item = JSON.stringify(arr)
|
||||
} else {
|
||||
this.items.push({
|
||||
key,
|
||||
item: JSON.stringify(arr),
|
||||
})
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +158,8 @@ export type AsyncPipeResolver<T> = () => Awaitable<T>
|
||||
*/
|
||||
export type AsyncPipeOperator<T, T2> = (subject: T) => Awaitable<T2>
|
||||
|
||||
export type PromisePipeOperator<T, T2> = (subject: T, resolve: (val: T2) => unknown, reject: (err: Error) => unknown) => Awaitable<unknown>
|
||||
|
||||
/**
|
||||
* A closure that maps a given pipe item to an item of the same type.
|
||||
*/
|
||||
@@ -193,6 +195,23 @@ export class AsyncPipe<T> {
|
||||
return new AsyncPipe<T2>(async () => op(await this.subject()))
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a transformative operator to the pipe, wrapping it
|
||||
* in a Promise and passing the resolve/reject callbacks to the
|
||||
* closure.
|
||||
* @param op
|
||||
*/
|
||||
promise<T2>(op: PromisePipeOperator<T, T2>): AsyncPipe<T2> {
|
||||
return new AsyncPipe<T2>(() => {
|
||||
return new Promise<T2>((res, rej) => {
|
||||
(async () => this.subject())()
|
||||
.then(subject => {
|
||||
op(subject, res, rej)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an operator to the pipe, but return the reference
|
||||
* to the current pipe. The operator is resolved when the
|
||||
|
||||
Reference in New Issue
Block a user