lib/src/orm/support/ORMCache.ts
garrettmills 074a3187eb
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is failing
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
2021-08-23 23:51:53 -05:00

127 lines
4.2 KiB
TypeScript

import {Container} from '../../di'
import {Awaitable, Cache, ErrorWithContext, Maybe} from '../../util'
import {CacheModel} from './CacheModel'
/**
* A cache driver whose records are stored in a database table using the CacheModel.
*/
export class ORMCache extends Cache {
public async fetch(key: string): Promise<string | undefined> {
return (await CacheModel.getCacheKey(key))?.cacheValue
}
public async put(key: string, value: string, expires?: Date): Promise<void> {
let model = await CacheModel.findByKey<CacheModel>(key)
if ( !model ) {
model = <CacheModel> Container.getContainer().make(CacheModel)
}
model.cacheKey = key
model.cacheValue = value
model.cacheExpires = expires
await model.save()
}
public async has(key: string): Promise<boolean> {
return CacheModel.withCacheKey(key)
.exists()
}
public async drop(key: string): Promise<void> {
await CacheModel.query()
.whereKey(key)
.delete()
}
public async pop(key: string): Promise<string> {
return CacheModel.getConnection()
.asTransaction<string>(async () => {
const model = await CacheModel.getCacheKey(key)
if ( !model ) {
throw new ErrorWithContext('Cannot pop cache value: key does not exist.', {
key,
})
}
await model.delete()
return model.cacheValue
})
}
public increment(key: string, amount = 1): Awaitable<number> {
return CacheModel.getConnection()
.asTransaction<number>(async () => {
const model = await CacheModel.getCacheKey(key)
if ( !model ) {
await this.put(key, String(amount))
return amount
}
model.cacheValue = String(parseInt(model.cacheValue, 10) + amount)
await model.save()
return parseInt(model.cacheValue, 10)
})
}
public decrement(key: string, amount = 1): Awaitable<number> {
return CacheModel.getConnection()
.asTransaction<number>(async () => {
const model = await CacheModel.getCacheKey(key)
if ( !model ) {
await this.put(key, String(-amount))
return amount
}
model.cacheValue = String(parseInt(model.cacheValue, 10) - amount)
await model.save()
return parseInt(model.cacheValue, 10)
})
}
public async arrayPush(key: string, value: string): Promise<void> {
await CacheModel.getConnection()
.asTransaction<void>(async () => {
const model = await CacheModel.getCacheKey(key)
if ( !model ) {
await this.put(key, JSON.stringify([value]))
return
}
const cacheValue = JSON.parse(model.cacheValue)
if ( !Array.isArray(cacheValue) ) {
throw new ErrorWithContext('Cannot push value to non-array.', {
key,
})
}
cacheValue.push(value)
model.cacheValue = JSON.stringify(cacheValue)
})
throw new Error('Method not implemented.')
}
public async arrayPop(key: string): Promise<Maybe<string>> {
return CacheModel.getConnection()
.asTransaction<Maybe<string>>(async () => {
const model = await CacheModel.getCacheKey(key)
if ( !model ) {
return
}
const cacheValue = JSON.parse(model.cacheValue)
if ( !Array.isArray(cacheValue) ) {
throw new ErrorWithContext('Cannot pop value from non-array.', {
key,
})
}
const value = cacheValue.pop()
model.cacheValue = JSON.stringify(cacheValue)
await model.save()
return value
})
}
}