1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-09-10 17:53:36 +02:00

implement transactional for feature toggle store and write a test to prove it

This commit is contained in:
sighphyre 2022-10-20 10:27:53 +02:00
parent c3b92ca837
commit dd955c6ede
5 changed files with 43 additions and 26 deletions

View File

@ -6,6 +6,7 @@ import NotFoundError from '../error/notfound-error';
import { Logger, LogProvider } from '../logger'; import { Logger, LogProvider } from '../logger';
import { FeatureToggle, FeatureToggleDTO, IVariant } from '../types/model'; import { FeatureToggle, FeatureToggleDTO, IVariant } from '../types/model';
import { IFeatureToggleStore } from '../types/stores/feature-toggle-store'; import { IFeatureToggleStore } from '../types/stores/feature-toggle-store';
import { Transactor } from './transactional';
const FEATURE_COLUMNS = [ const FEATURE_COLUMNS = [
'name', 'name',
@ -36,14 +37,18 @@ export interface FeaturesTable {
const TABLE = 'features'; const TABLE = 'features';
export default class FeatureToggleStore implements IFeatureToggleStore { export default class FeatureToggleStore
private db: Knex; extends Transactor<FeatureToggleStore>
implements IFeatureToggleStore
{
private db: Knex | Knex.Transaction;
private logger: Logger; private logger: Logger;
private timer: Function; private timer: Function;
constructor(db: Knex, eventBus: EventEmitter, getLogger: LogProvider) { constructor(db: Knex, eventBus: EventEmitter, getLogger: LogProvider) {
super(db, eventBus, getLogger);
this.db = db; this.db = db;
this.logger = getLogger('feature-toggle-store.ts'); this.logger = getLogger('feature-toggle-store.ts');
this.timer = (action) => this.timer = (action) =>

View File

@ -10,7 +10,7 @@ import Group, {
IGroupUserModel, IGroupUserModel,
} from '../types/group'; } from '../types/group';
import Transaction = Knex.Transaction; import Transaction = Knex.Transaction;
import { AbstractTransactional } from './transactional'; import { Transactor } from './transactional';
const T = { const T = {
GROUPS: 'groups', GROUPS: 'groups',
@ -62,10 +62,10 @@ const groupToRow = (group: IStoreGroup) => ({
}); });
export default class GroupStore export default class GroupStore
extends AbstractTransactional<GroupStore> extends Transactor<GroupStore>
implements IGroupStore implements IGroupStore
{ {
private db: Knex; private db: Knex | Knex.Transaction;
constructor(db: Knex) { constructor(db: Knex) {
super(); super();

View File

@ -1,9 +1,18 @@
import { Knex } from 'knex'; import { Knex } from 'knex';
import { Transactional } from 'lib/types/stores/transactional'; import { Transactional } from 'lib/types/stores/transactional';
export abstract class AbstractTransactional<T> implements Transactional<T> { export abstract class Transactor<T> implements Transactional<T> {
args: any[];
// eslint-disable-next-line
constructor(...args: any) {
this.args = args;
}
transactional(transaction: Knex.Transaction): T { transactional(transaction: Knex.Transaction): T {
let clone = new (this.constructor as { new (): any })(); let clone = new (this.constructor as { new (...args: any[]): any })(
...this.args,
);
for (const attribute in this) { for (const attribute in this) {
clone[attribute] = this[attribute]; clone[attribute] = this[attribute];
} }

View File

@ -1,4 +1,5 @@
import { FeatureToggle, FeatureToggleDTO, IVariant } from '../model'; import { FeatureToggle, FeatureToggleDTO, IVariant } from '../model';
import { Transactional } from './transactional';
import { Store } from './store'; import { Store } from './store';
export interface IFeatureToggleQuery { export interface IFeatureToggleQuery {
@ -7,7 +8,9 @@ export interface IFeatureToggleQuery {
stale: boolean; stale: boolean;
} }
export interface IFeatureToggleStore extends Store<FeatureToggle, string> { export interface IFeatureToggleStore
extends Store<FeatureToggle, string>,
Transactional<IFeatureToggleStore> {
count(query?: Partial<IFeatureToggleQuery>): Promise<number>; count(query?: Partial<IFeatureToggleQuery>): Promise<number>;
setLastSeen(toggleNames: string[]): Promise<void>; setLastSeen(toggleNames: string[]): Promise<void>;
getProjectId(name: string): Promise<string>; getProjectId(name: string): Promise<string>;

View File

@ -5,25 +5,8 @@ let stores;
let db: ITestDb; let db: ITestDb;
beforeAll(async () => { beforeAll(async () => {
db = await dbInit('group_service_serial', getLogger); db = await dbInit('transactional_serial', getLogger);
stores = db.stores; stores = db.stores;
await stores.groupStore.create({
name: 'dev_group',
description: 'dev_group',
mappingsSSO: ['dev'],
});
await stores.groupStore.create({
name: 'maintainer_group',
description: 'maintainer_group',
mappingsSSO: ['maintainer'],
});
await stores.groupStore.create({
name: 'admin_group',
description: 'admin_group',
mappingsSSO: ['admin'],
});
}); });
afterAll(async () => { afterAll(async () => {
@ -45,3 +28,20 @@ test('should actually do something transactional mode', async () => {
}); });
expect(createdGroup).toBeDefined(); expect(createdGroup).toBeDefined();
}); });
test('should fail entire transaction if encountering an error', async () => {
await db.db.transaction(async (trx) => {
const featureDTO = {
name: 'SomeUniqueNameThatSoonWontBeUnique',
};
await stores.featureToggleStore
.transactional(trx)
.create('default', featureDTO);
await stores.featureToggleStore
.transactional(trx)
.create('default', featureDTO);
});
const toggles = await stores.featureToggleStore.getAll();
expect(toggles.length).toBe(0);
});