1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-02-09 00:18:00 +01:00

feat: dependent features use new transaction mechanism (#5073)

This commit is contained in:
Mateusz Kwasniewski 2023-10-18 08:59:26 +02:00 committed by GitHub
parent 4dd01c1765
commit 75b131162e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 75 additions and 96 deletions

View File

@ -17,35 +17,33 @@ import {
import { FeaturesReadModel } from '../feature-toggle/features-read-model'; import { FeaturesReadModel } from '../feature-toggle/features-read-model';
import { FakeFeaturesReadModel } from '../feature-toggle/fakes/fake-features-read-model'; import { FakeFeaturesReadModel } from '../feature-toggle/fakes/fake-features-read-model';
export const createDependentFeaturesService = ( export const createDependentFeaturesService =
db: Db, (config: IUnleashConfig) => (db: Db): DependentFeaturesService => {
config: IUnleashConfig, const { getLogger, eventBus } = config;
): DependentFeaturesService => { const eventStore = new EventStore(db, getLogger);
const { getLogger, eventBus } = config; const featureTagStore = new FeatureTagStore(db, eventBus, getLogger);
const eventStore = new EventStore(db, getLogger); const eventService = new EventService(
const featureTagStore = new FeatureTagStore(db, eventBus, getLogger); {
const eventService = new EventService( eventStore,
{ featureTagStore,
eventStore, },
featureTagStore, config,
}, );
config, const dependentFeaturesStore = new DependentFeaturesStore(db);
); const dependentFeaturesReadModel = new DependentFeaturesReadModel(db);
const dependentFeaturesStore = new DependentFeaturesStore(db); const changeRequestAccessReadModel = createChangeRequestAccessReadModel(
const dependentFeaturesReadModel = new DependentFeaturesReadModel(db); db,
const changeRequestAccessReadModel = createChangeRequestAccessReadModel( config,
db, );
config, const featuresReadModel = new FeaturesReadModel(db);
); return new DependentFeaturesService({
const featuresReadModel = new FeaturesReadModel(db); dependentFeaturesStore,
return new DependentFeaturesService({ dependentFeaturesReadModel,
dependentFeaturesStore, changeRequestAccessReadModel,
dependentFeaturesReadModel, featuresReadModel,
changeRequestAccessReadModel, eventService,
featuresReadModel, });
eventService, };
});
};
export const createFakeDependentFeaturesService = ( export const createFakeDependentFeaturesService = (
config: IUnleashConfig, config: IUnleashConfig,

View File

@ -20,7 +20,11 @@ import {
import { IAuthRequest } from '../../routes/unleash-types'; import { IAuthRequest } from '../../routes/unleash-types';
import { InvalidOperationError } from '../../error'; import { InvalidOperationError } from '../../error';
import { DependentFeaturesService } from './dependent-features-service'; import { DependentFeaturesService } from './dependent-features-service';
import { TransactionCreator, UnleashTransaction } from '../../db/transaction'; import {
TransactionCreator,
UnleashTransaction,
WithTransactional,
} from '../../db/transaction';
interface ProjectParams { interface ProjectParams {
projectId: string; projectId: string;
@ -44,19 +48,11 @@ const PATH_DEPENDENCY = `${PATH_FEATURE}/dependencies/:parent`;
type DependentFeaturesServices = Pick< type DependentFeaturesServices = Pick<
IUnleashServices, IUnleashServices,
| 'transactionalDependentFeaturesService' 'transactionalDependentFeaturesService' | 'openApiService'
| 'dependentFeaturesService'
| 'openApiService'
>; >;
export default class DependentFeaturesController extends Controller { export default class DependentFeaturesController extends Controller {
private transactionalDependentFeaturesService: ( private dependentFeaturesService: WithTransactional<DependentFeaturesService>;
db: UnleashTransaction,
) => DependentFeaturesService;
private dependentFeaturesService: DependentFeaturesService;
private readonly startTransaction: TransactionCreator<UnleashTransaction>;
private openApiService: OpenApiService; private openApiService: OpenApiService;
@ -68,18 +64,13 @@ export default class DependentFeaturesController extends Controller {
config: IUnleashConfig, config: IUnleashConfig,
{ {
transactionalDependentFeaturesService, transactionalDependentFeaturesService,
dependentFeaturesService,
openApiService, openApiService,
}: DependentFeaturesServices, }: DependentFeaturesServices,
startTransaction: TransactionCreator<UnleashTransaction>,
) { ) {
super(config); super(config);
this.transactionalDependentFeaturesService = this.dependentFeaturesService = transactionalDependentFeaturesService;
transactionalDependentFeaturesService;
this.dependentFeaturesService = dependentFeaturesService;
this.openApiService = openApiService; this.openApiService = openApiService;
this.flagResolver = config.flagResolver; this.flagResolver = config.flagResolver;
this.startTransaction = startTransaction;
this.logger = config.getLogger( this.logger = config.getLogger(
'/dependent-features/dependent-feature-service.ts', '/dependent-features/dependent-feature-service.ts',
); );
@ -196,10 +187,8 @@ export default class DependentFeaturesController extends Controller {
const { variants, enabled, feature } = req.body; const { variants, enabled, feature } = req.body;
if (this.config.flagResolver.isEnabled('dependentFeatures')) { if (this.config.flagResolver.isEnabled('dependentFeatures')) {
await this.startTransaction(async (tx) => await this.dependentFeaturesService.transactional((service) =>
this.transactionalDependentFeaturesService( service.upsertFeatureDependency(
tx,
).upsertFeatureDependency(
{ child, projectId }, { child, projectId },
{ {
variants, variants,
@ -209,6 +198,7 @@ export default class DependentFeaturesController extends Controller {
req.user, req.user,
), ),
); );
res.status(200).end(); res.status(200).end();
} else { } else {
throw new InvalidOperationError( throw new InvalidOperationError(
@ -224,13 +214,15 @@ export default class DependentFeaturesController extends Controller {
const { child, parent, projectId } = req.params; const { child, parent, projectId } = req.params;
if (this.config.flagResolver.isEnabled('dependentFeatures')) { if (this.config.flagResolver.isEnabled('dependentFeatures')) {
await this.dependentFeaturesService.deleteFeatureDependency( await this.dependentFeaturesService.transactional((service) =>
{ service.deleteFeatureDependency(
parent, {
child, parent,
}, child,
projectId, },
req.user, projectId,
req.user,
),
); );
res.status(200).end(); res.status(200).end();
} else { } else {
@ -247,10 +239,12 @@ export default class DependentFeaturesController extends Controller {
const { child, projectId } = req.params; const { child, projectId } = req.params;
if (this.config.flagResolver.isEnabled('dependentFeatures')) { if (this.config.flagResolver.isEnabled('dependentFeatures')) {
await this.dependentFeaturesService.deleteFeaturesDependencies( await this.dependentFeaturesService.transactional((service) =>
[child], service.deleteFeaturesDependencies(
projectId, [child],
req.user, projectId,
req.user,
),
); );
res.status(200).end(); res.status(200).end();
} else { } else {

View File

@ -236,10 +236,8 @@ export const deferredExportImportTogglesService = (
const segmentService = createSegmentService(db, config); const segmentService = createSegmentService(db, config);
const dependentFeaturesService = createDependentFeaturesService( const dependentFeaturesService =
db, createDependentFeaturesService(config)(db);
config,
);
const exportImportService = new ExportImportService( const exportImportService = new ExportImportService(
{ {

View File

@ -2,30 +2,29 @@ import { Logger } from '../../logger';
import { IStrategy } from '../../types/stores/strategy-store'; import { IStrategy } from '../../types/stores/strategy-store';
import { IFeatureToggleStore } from '../feature-toggle/types/feature-toggle-store-type'; import { IFeatureToggleStore } from '../feature-toggle/types/feature-toggle-store-type';
import { IFeatureStrategiesStore } from '../feature-toggle/types/feature-toggle-strategies-store-type'; import { IFeatureStrategiesStore } from '../feature-toggle/types/feature-toggle-strategies-store-type';
import {
IUnleashConfig,
IContextFieldStore,
IUnleashStores,
ISegmentStore,
IFeatureEnvironmentStore,
ITagTypeStore,
IFeatureTagStore,
FeatureToggleDTO,
IFeatureStrategy,
IFeatureStrategySegment,
IVariant,
} from '../../types';
import { ExportQuerySchema, ImportTogglesSchema } from '../../openapi';
import { import {
FEATURES_EXPORTED, FEATURES_EXPORTED,
FEATURES_IMPORTED, FEATURES_IMPORTED,
FeatureToggleDTO,
IContextFieldStore,
IFeatureEnvironmentStore,
IFeatureStrategy,
IFeatureStrategySegment,
IFeatureTagStore,
IFlagResolver, IFlagResolver,
ISegmentStore,
ITagTypeStore,
IUnleashConfig,
IUnleashServices, IUnleashServices,
IUnleashStores,
IVariant,
WithRequired, WithRequired,
} from '../../types'; } from '../../types';
import { import {
ExportQuerySchema,
ExportResultSchema, ExportResultSchema,
FeatureStrategySchema, FeatureStrategySchema,
ImportTogglesSchema,
ImportTogglesValidateSchema, ImportTogglesValidateSchema,
} from '../../openapi'; } from '../../openapi';
import User from '../../types/user'; import User from '../../types/user';

View File

@ -119,7 +119,7 @@ export const createFeatureToggleService = (
const dependentFeaturesReadModel = new DependentFeaturesReadModel(db); const dependentFeaturesReadModel = new DependentFeaturesReadModel(db);
const dependentFeaturesService = createDependentFeaturesService(db, config); const dependentFeaturesService = createDependentFeaturesService(config)(db);
const featureToggleService = new FeatureToggleService( const featureToggleService = new FeatureToggleService(
{ {

View File

@ -113,14 +113,7 @@ export default class ProjectApi extends Controller {
createKnexTransactionStarter(db), createKnexTransactionStarter(db),
).router, ).router,
); );
this.use( this.use('/', new DependentFeaturesController(config, services).router);
'/',
new DependentFeaturesController(
config,
services,
createKnexTransactionStarter(db),
).router,
);
this.use('/', new EnvironmentsController(config, services).router); this.use('/', new EnvironmentsController(config, services).router);
this.use('/', new ProjectHealthReport(config, services).router); this.use('/', new ProjectHealthReport(config, services).router);
this.use('/', new VariantsController(config, services).router); this.use('/', new VariantsController(config, services).router);

View File

@ -267,11 +267,10 @@ export const createServices = (
privateProjectChecker, privateProjectChecker,
); );
const dependentFeaturesService = db const transactionalDependentFeaturesService = db
? createDependentFeaturesService(db, config) ? withTransactional(createDependentFeaturesService(config), db)
: createFakeDependentFeaturesService(config); : withFakeTransactional(createFakeDependentFeaturesService(config));
const transactionalDependentFeaturesService = (txDb: Knex.Transaction) => const dependentFeaturesService = transactionalDependentFeaturesService;
createDependentFeaturesService(txDb, config);
const featureToggleServiceV2 = new FeatureToggleService( const featureToggleServiceV2 = new FeatureToggleService(
stores, stores,

View File

@ -105,8 +105,6 @@ export interface IUnleashServices {
transactionalGroupService: (db: Knex.Transaction) => GroupService; transactionalGroupService: (db: Knex.Transaction) => GroupService;
privateProjectChecker: IPrivateProjectChecker; privateProjectChecker: IPrivateProjectChecker;
dependentFeaturesService: DependentFeaturesService; dependentFeaturesService: DependentFeaturesService;
transactionalDependentFeaturesService: ( transactionalDependentFeaturesService: WithTransactional<DependentFeaturesService>;
db: Knex.Transaction,
) => DependentFeaturesService;
clientFeatureToggleService: ClientFeatureToggleService; clientFeatureToggleService: ClientFeatureToggleService;
} }