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:
parent
c3b92ca837
commit
dd955c6ede
@ -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) =>
|
||||||
|
@ -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();
|
||||||
|
@ -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];
|
||||||
}
|
}
|
||||||
|
@ -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>;
|
||||||
|
@ -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);
|
||||||
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user