Skip to content

Commit

Permalink
Local Cache Preps
Browse files Browse the repository at this point in the history
- [x] Modify `SQLiteConfigStore` so that the table is dynamic
- [x] Modify `ORMConfigStore` to except `tableName` in params
- [x] Modify `SQLiteConfigStore` to except `tableName` in params
- [x] Modify `EvalCommand` to throw `ConfigStoreError` where:
  - [x] `store.init()` throws
  - [x] any error inside `evalFromStoreSet`
  - [x] `validateResult` throws and the origin is `EvaluatedConfigOrigin.StoreSet`
  • Loading branch information
ShriekinNinja committed Mar 7, 2024
1 parent 0bef4f5 commit 3ff9128
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 63 deletions.
53 changes: 23 additions & 30 deletions ts/packages/node/src/stores/ORM.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,40 @@
import 'reflect-metadata';
import { ConfigStore, type ConfigStoreQuery, type Config as IConfig } from '@configu/ts';
import { DataSource, type DataSourceOptions, EntitySchema } from 'typeorm';
import { DataSource, type DataSourceOptions, Entity, Index, PrimaryGeneratedColumn, Column } from 'typeorm';
import _ from 'lodash';

const createTable = (tableName: string) =>
new EntitySchema<IConfig & { id: string }>({
name: tableName,
columns: {
id: {
type: 'uuid',
primary: true,
generated: 'uuid',
},
set: {
type: 'text',
},
key: {
type: 'text',
},
value: {
type: 'text',
},
},
indices: [
{
name: `${tableName.toUpperCase()}_IDX_SET_KEY`,
unique: true,
columns: ['set', 'key'],
},
],
});
const createEntity = (tableName: string) => {
@Entity({ name: tableName })
@Index(['set', 'key'], { unique: true })
class Config {
@PrimaryGeneratedColumn('uuid')
id: string;

@Index(`${tableName}_set`)
@Column('text')
set: string;

@Column('text')
key: string;

@Column('text')
value: string;
}

return Config;
};

type ORMConfigStoreOptions = DataSourceOptions & {
tableName?: string;
};

export abstract class ORMConfigStore extends ConfigStore {
readonly dataSource: DataSource;
private readonly table: EntitySchema;
private readonly table: ReturnType<typeof createEntity>;

protected constructor(type: string, { tableName = 'config', ...dataSourceOptions }: ORMConfigStoreOptions) {
super(type);
this.table = createTable(tableName);
this.table = createEntity(tableName);
this.dataSource = new DataSource({
// TODO: synchronize is not production safe - create a migration script to initialize tables
synchronize: true,
Expand Down
79 changes: 46 additions & 33 deletions ts/packages/ts/src/commands/EvalCommand.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import _ from 'lodash';
import { Command } from '../Command';
import { type Cfgu } from '../types';
import { ConfigError, TMPL } from '../utils';
import { ConfigError, TMPL, ConfigStoreError } from '../utils';
import { type ConfigStore } from '../ConfigStore';
import { ConfigSet } from '../ConfigSet';
import { ConfigSchema } from '../ConfigSchema';
Expand Down Expand Up @@ -60,37 +60,42 @@ export class EvalCommand extends Command<EvalCommandReturn> {
private async evalFromStoreSet(result: EvalCommandReturn): Promise<EvalCommandReturn> {
const { store, set } = this.parameters;

const storeQueries = _(result)
.values()
.flatMap((current) => {
const { key } = current.context;
return set.hierarchy.map((node) => ({ set: node, key }));
})
.value();
const storeConfigsArray = await store.get(storeQueries);
const storeConfigsDict = _(storeConfigsArray)
.orderBy([(config) => set.hierarchy.indexOf(config.set)], ['desc'])
.uniqBy((config) => config.key)
.keyBy((config) => config.key)
.value();

return _.mapValues(result, (current) => {
const { context } = current;
const storeConfig = storeConfigsDict[context.key];

if (!storeConfig) {
return current;
}
try {
const storeQueries = _(result)
.values()
.flatMap((current) => {
const { key } = current.context;
return set.hierarchy.map((node) => ({ set: node, key }));
})
.value();

const storeConfigsArray = await store.get(storeQueries);
const storeConfigsDict = _(storeConfigsArray)
.orderBy([(config) => set.hierarchy.indexOf(config.set)], ['desc'])
.uniqBy((config) => config.key)
.keyBy((config) => config.key)
.value();

return _.mapValues(result, (current) => {
const { context } = current;
const storeConfig = storeConfigsDict[context.key];

if (!storeConfig) {
return current;
}

return {
...current,
result: {
origin: EvaluatedConfigOrigin.StoreSet,
source: `parameters.store=${context.store},parameters.set=${context.set}`,
value: storeConfig.value,
},
};
});
return {
...current,
result: {
origin: EvaluatedConfigOrigin.StoreSet,
source: `parameters.store=${context.store},parameters.set=${context.set}`,
value: storeConfig.value,
},
};
});
} catch (error) {
throw new ConfigStoreError('failed to get store configs', error.message, [['EvalCommand', 'evalFromStoreSet']]);
}
}

private evalFromSchema(result: EvalCommandReturn): EvalCommandReturn {
Expand Down Expand Up @@ -245,7 +250,11 @@ export class EvalCommand extends Command<EvalCommandReturn> {
ConfigSchema.CFGU.VALIDATORS.valueOptions(cfgu, evaluatedValue);
ConfigSchema.CFGU.VALIDATORS.valueType(cfgu, evaluatedValue);
} catch (error) {
throw error?.appendScope?.(errorScope) ?? error;
let err = error?.appendScope?.(errorScope) ?? error;
if (current.result.origin === EvaluatedConfigOrigin.StoreSet) {
err = new ConfigStoreError('invalid config value', err.message, err?.scope ?? errorScope);
}
throw err;
}

if (cfgu.depends && cfgu.depends.some((depend) => !evaluatedConfigsDict[depend])) {
Expand All @@ -264,7 +273,11 @@ export class EvalCommand extends Command<EvalCommandReturn> {
async run() {
const { store, set, schema } = this.parameters;

await store.init();
try {
await store.init();
} catch (error) {
throw new ConfigStoreError('failed to init store', error.message, [['EvalCommand', 'store']]);
}

let result = _.mapValues<typeof schema.contents, EvalCommandReturn['string']>(schema.contents, (cfgu, key) => {
return {
Expand Down
2 changes: 2 additions & 0 deletions ts/packages/ts/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export class ConfigError extends Error {
}
}

export class ConfigStoreError extends ConfigError {}

export const REGEX = (pattern: string | RegExp, string: string) => RegExp(pattern).test(string);

const NAMING_PATTERN = /^[A-Za-z0-9_-]+$/;
Expand Down

0 comments on commit 3ff9128

Please sign in to comment.