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

small spike for service level transactions, implementing this at the store level for groups

This commit is contained in:
sighphyre 2022-10-20 08:35:40 +02:00
parent c4d68110fc
commit 8c82e4d0a0
9 changed files with 102 additions and 2 deletions

View File

@ -10,6 +10,7 @@ import Group, {
IGroupUserModel, IGroupUserModel,
} from '../types/group'; } from '../types/group';
import Transaction = Knex.Transaction; import Transaction = Knex.Transaction;
import { AbstractTransactional } from './transactional';
const T = { const T = {
GROUPS: 'groups', GROUPS: 'groups',
@ -60,10 +61,14 @@ const groupToRow = (group: IStoreGroup) => ({
mappings_sso: JSON.stringify(group.mappingsSSO), mappings_sso: JSON.stringify(group.mappingsSSO),
}); });
export default class GroupStore implements IGroupStore { export default class GroupStore
extends AbstractTransactional<GroupStore>
implements IGroupStore
{
private db: Knex; private db: Knex;
constructor(db: Knex) { constructor(db: Knex) {
super();
this.db = db; this.db = db;
} }

View File

@ -0,0 +1,13 @@
import { Knex } from 'knex';
import { Transactional } from 'lib/types/stores/transactional';
export abstract class AbstractTransactional<T> implements Transactional<T> {
transactional(transaction: Knex.Transaction): T {
let clone = new (this.constructor as { new (): any })();
for (const attribute in this) {
clone[attribute] = this[attribute];
}
clone.db = transaction;
return clone as T;
}
}

View File

@ -76,6 +76,7 @@ async function createApp(
app, app,
config, config,
version: serverVersion, version: serverVersion,
db,
}; };
if (config.import.file) { if (config.import.file) {

View File

@ -6,6 +6,7 @@ import User from './user';
import { IUnleashConfig } from './option'; import { IUnleashConfig } from './option';
import { IUnleashStores } from './stores'; import { IUnleashStores } from './stores';
import { IUnleashServices } from './services'; import { IUnleashServices } from './services';
import { Knex } from 'knex';
export interface AuthedRequest extends Request { export interface AuthedRequest extends Request {
user: User; user: User;
@ -20,4 +21,5 @@ export interface IUnleash {
services: IUnleashServices; services: IUnleashServices;
stop: () => Promise<void>; stop: () => Promise<void>;
version: string; version: string;
db: Knex;
} }

View File

@ -7,6 +7,7 @@ import Group, {
IGroupUser, IGroupUser,
IGroupUserModel, IGroupUserModel,
} from '../group'; } from '../group';
import { Transactional } from './transactional';
export interface IStoreGroup { export interface IStoreGroup {
name: string; name: string;
@ -14,7 +15,9 @@ export interface IStoreGroup {
mappingsSSO?: string[]; mappingsSSO?: string[];
} }
export interface IGroupStore extends Store<IGroup, number> { export interface IGroupStore
extends Store<IGroup, number>,
Transactional<IGroupStore> {
getGroupsForUser(userId: number): Promise<Group[]>; getGroupsForUser(userId: number): Promise<Group[]>;
getOldGroupsForExternalUser( getOldGroupsForExternalUser(
userId: number, userId: number,

View File

@ -0,0 +1,5 @@
import { Knex } from 'knex';
export interface Transactional<T> {
transactional(transaction: Knex.Transaction): T;
}

View File

@ -11,6 +11,7 @@ import { IUnleashStores } from '../../../lib/types';
import { IFeatureEnvironmentStore } from '../../../lib/types/stores/feature-environment-store'; import { IFeatureEnvironmentStore } from '../../../lib/types/stores/feature-environment-store';
import { DEFAULT_ENV } from '../../../lib/util/constants'; import { DEFAULT_ENV } from '../../../lib/util/constants';
import { IUnleashOptions } from 'lib/server-impl'; import { IUnleashOptions } from 'lib/server-impl';
import { Knex } from 'knex';
// require('db-migrate-shared').log.silence(false); // require('db-migrate-shared').log.silence(false);
@ -73,6 +74,7 @@ async function setupDatabase(stores) {
export interface ITestDb { export interface ITestDb {
stores: IUnleashStores; stores: IUnleashStores;
db: Knex;
reset: () => Promise<void>; reset: () => Promise<void>;
destroy: () => Promise<void>; destroy: () => Promise<void>;
} }
@ -108,6 +110,7 @@ export default async function init(
return { return {
stores, stores,
db: testDb,
reset: async () => { reset: async () => {
await resetDatabase(testDb); await resetDatabase(testDb);
await setupDatabase(stores); await setupDatabase(stores);

View File

@ -7,6 +7,7 @@ import Group, {
IGroupUser, IGroupUser,
IGroupUserModel, IGroupUserModel,
} from '../../lib/types/group'; } from '../../lib/types/group';
import { Knex } from 'knex';
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
export default class FakeGroupStore implements IGroupStore { export default class FakeGroupStore implements IGroupStore {
data: IGroup[]; data: IGroup[];
@ -109,4 +110,8 @@ export default class FakeGroupStore implements IGroupStore {
getGroupsForUser(userId: number): Promise<Group[]> { getGroupsForUser(userId: number): Promise<Group[]> {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
transactional(transaction: Knex.Transaction<any, any[]>): IGroupStore {
throw new Error('Method not implemented.');
}
} }

View File

@ -0,0 +1,63 @@
import dbInit, { ITestDb } from './/e2e/helpers/database-init';
import getLogger from './fixtures/no-logger';
let stores;
let db: ITestDb;
beforeAll(async () => {
db = await dbInit('group_service_serial', getLogger);
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 () => {
await db.destroy();
});
test('should actually do something transactional mode', async () => {
await db.db.transaction(async (trx) => {
await stores.groupStore.transactional(trx).create({
name: 'some_other_group',
description: 'admin_group',
mappingsSSO: ['admin'],
});
});
const groups = await stores.groupStore.getAll();
const createdGroup = groups.find((group) => {
return group.name === 'some_other_group';
});
expect(createdGroup).toBeDefined();
});
test('should actually do something transactional mode', async () => {
await db.db.transaction(async (trx) => {
await stores.groupStore.transactional(trx).create({
name: 'some_other_group',
description: 'admin_group',
mappingsSSO: ['admin'],
});
});
const groups = await stores.groupStore.getAll();
const createdGroup = groups.find((group) => {
return group.name === 'some_other_group';
});
expect(createdGroup).toBeDefined();
});