/* eslint-disable @typescript-eslint/explicit-module-boundary-types,@typescript-eslint/no-unused-vars,@typescript-eslint/no-explicit-any,prefer-destructuring */ import { RepositoryBase, recordInfo, UUID, Flavor } from "common"; export type SettingId = Flavor; export interface UnsavedSetting { name: string; value: T; } export interface SavedSetting extends UnsavedSetting { id: SettingId; createdAt: Date; updatedAt: Date; } export const SettingRecord = recordInfo, SavedSetting>( "app_public", "settings" ); export class SettingRecordRepository extends RepositoryBase(SettingRecord) { async findByName(name: string): Promise | null> { return this.db.oneOrNone("SELECT * FROM $1 $2:raw LIMIT 1", [ this.schemaTable, this.where({ name }), ]); } async upsert(name: string, value: T): Promise> { return this.db.one( `INSERT INTO $1 ($2:name) VALUES ($2:csv) ON CONFLICT (name) DO UPDATE SET value = EXCLUDED.value RETURNING *`, [this.schemaTable, this.columnize({ name, value })] ); } } // these helpers let us create type safe setting constants export interface SettingType { _type: T; } export interface SettingInfo extends SettingType { name: string; } export function castToSettingInfo( runtimeData: Omit ): SettingInfo { return runtimeData as SettingInfo; } export function settingInfo(name: string): SettingInfo; // don't use this signature, use the explicit typed signature export function settingInfo(name: string) { return castToSettingInfo({ name, }); } export interface ISettingsService { name: string; lookup(settingInfo: SettingInfo): Promise; save(settingInfo: SettingInfo, value: T): Promise; } export const SettingsService = ( repo: SettingRecordRepository ): ISettingsService => ({ name: "settingService", lookup: async (settingInfo: SettingInfo): Promise => { const s = await repo.findByName(settingInfo.name); return s.value; }, save: async (settingInfo: SettingInfo, value: T): Promise => { const s = await repo.upsert(settingInfo.name, value); return s.value; }, }); const _test = async () => { // here is an example of how to use this module // it also serves as a compile-time test case const repo = new SettingRecordRepository({} as any); // create your own custom setting types! // the value is serialized as json in the database type Custom = { foo: string; bar: string }; type CustomUnsavedSetting = UnsavedSetting; type CustomSetting = SavedSetting; const s3: CustomSetting = await repo.findByName("test"); const customValue = { foo: "monkeys", bar: "eggplants" }; let customSetting = { name: "custom", value: customValue }; customSetting = await repo.insert(customSetting); const value: Custom = customSetting.value; const MySetting = settingInfo("my-setting"); };