1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-08-04 13:48:56 +02:00

feat: make all feature toggle service write methods transactional (#9973)

This commit is contained in:
Mateusz Kwasniewski 2025-05-13 12:16:20 +02:00 committed by GitHub
parent 2a083edc14
commit 410142cb42
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 164 additions and 228 deletions

View File

@ -16,10 +16,7 @@ import {
emptyResponse, emptyResponse,
getStandardResponses, getStandardResponses,
} from '../../openapi/util/standard-responses'; } from '../../openapi/util/standard-responses';
import type { import type { WithTransactional } from '../../db/transaction';
TransactionCreator,
UnleashTransaction,
} from '../../db/transaction';
import { import {
archivedFeaturesSchema, archivedFeaturesSchema,
type ArchivedFeaturesSchema, type ArchivedFeaturesSchema,
@ -27,10 +24,7 @@ import {
export default class ArchiveController extends Controller { export default class ArchiveController extends Controller {
private featureService: FeatureToggleService; private featureService: FeatureToggleService;
private transactionalFeatureToggleService: ( private transactionalFeatureToggleService: WithTransactional<FeatureToggleService>;
db: UnleashTransaction,
) => FeatureToggleService;
private readonly startTransaction: TransactionCreator<UnleashTransaction>;
private openApiService: OpenApiService; private openApiService: OpenApiService;
constructor( constructor(
@ -45,14 +39,12 @@ export default class ArchiveController extends Controller {
| 'featureToggleService' | 'featureToggleService'
| 'openApiService' | 'openApiService'
>, >,
startTransaction: TransactionCreator<UnleashTransaction>,
) { ) {
super(config); super(config);
this.featureService = featureToggleService; this.featureService = featureToggleService;
this.openApiService = openApiService; this.openApiService = openApiService;
this.transactionalFeatureToggleService = this.transactionalFeatureToggleService =
transactionalFeatureToggleService; transactionalFeatureToggleService;
this.startTransaction = startTransaction;
this.route({ this.route({
method: 'get', method: 'get',
@ -204,11 +196,8 @@ export default class ArchiveController extends Controller {
): Promise<void> { ): Promise<void> {
const { featureName } = req.params; const { featureName } = req.params;
await this.startTransaction(async (tx) => await this.transactionalFeatureToggleService.transactional((service) =>
this.transactionalFeatureToggleService(tx).reviveFeature( service.reviveFeature(featureName, req.audit),
featureName,
req.audit,
),
); );
res.status(200).end(); res.status(200).end();
} }

View File

@ -48,10 +48,7 @@ import type {
} from '../../services'; } from '../../services';
import { querySchema } from '../../schema/feature-schema'; import { querySchema } from '../../schema/feature-schema';
import type { BatchStaleSchema } from '../../openapi/spec/batch-stale-schema'; import type { BatchStaleSchema } from '../../openapi/spec/batch-stale-schema';
import type { import type { WithTransactional } from '../../db/transaction';
TransactionCreator,
UnleashTransaction,
} from '../../db/transaction';
import { BadDataError } from '../../error'; import { BadDataError } from '../../error';
import { anonymise } from '../../util'; import { anonymise } from '../../util';
import { throwOnInvalidSchema } from '../../openapi/validate'; import { throwOnInvalidSchema } from '../../openapi/validate';
@ -116,9 +113,7 @@ export default class ProjectFeaturesController extends Controller {
private featureTagService: FeatureTagService; private featureTagService: FeatureTagService;
private transactionalFeatureToggleService: ( private transactionalFeatureToggleService: WithTransactional<FeatureToggleService>;
db: UnleashTransaction,
) => FeatureToggleService;
private openApiService: OpenApiService; private openApiService: OpenApiService;
@ -126,8 +121,6 @@ export default class ProjectFeaturesController extends Controller {
private readonly logger: Logger; private readonly logger: Logger;
private readonly startTransaction: TransactionCreator<UnleashTransaction>;
constructor( constructor(
config: IUnleashConfig, config: IUnleashConfig,
{ {
@ -136,13 +129,11 @@ export default class ProjectFeaturesController extends Controller {
transactionalFeatureToggleService, transactionalFeatureToggleService,
featureTagService, featureTagService,
}: ProjectFeaturesServices, }: ProjectFeaturesServices,
startTransaction: TransactionCreator<UnleashTransaction>,
) { ) {
super(config); super(config);
this.featureService = featureToggleService; this.featureService = featureToggleService;
this.transactionalFeatureToggleService = this.transactionalFeatureToggleService =
transactionalFeatureToggleService; transactionalFeatureToggleService;
this.startTransaction = startTransaction;
this.openApiService = openApiService; this.openApiService = openApiService;
this.featureTagService = featureTagService; this.featureTagService = featureTagService;
this.flagResolver = config.flagResolver; this.flagResolver = config.flagResolver;
@ -658,13 +649,17 @@ export default class ProjectFeaturesController extends Controller {
): Promise<void> { ): Promise<void> {
const { projectId, featureName } = req.params; const { projectId, featureName } = req.params;
const { name, replaceGroupId } = req.body; const { name, replaceGroupId } = req.body;
const created = await this.featureService.cloneFeatureToggle( const created =
featureName, await this.transactionalFeatureToggleService.transactional(
projectId, (service) =>
name, service.cloneFeatureToggle(
req.audit, featureName,
replaceGroupId, projectId,
); name,
req.audit,
replaceGroupId,
),
);
this.openApiService.respondWithValidation( this.openApiService.respondWithValidation(
201, 201,
@ -680,14 +675,18 @@ export default class ProjectFeaturesController extends Controller {
): Promise<void> { ): Promise<void> {
const { projectId } = req.params; const { projectId } = req.params;
const created = await this.featureService.createFeatureToggle( const created =
projectId, await this.transactionalFeatureToggleService.transactional(
{ (service) =>
...req.body, service.createFeatureToggle(
description: req.body.description || undefined, projectId,
}, {
req.audit, ...req.body,
); description: req.body.description || undefined,
},
req.audit,
),
);
this.openApiService.respondWithValidation( this.openApiService.respondWithValidation(
201, 201,
@ -762,15 +761,19 @@ export default class ProjectFeaturesController extends Controller {
if (data.name && data.name !== featureName) { if (data.name && data.name !== featureName) {
throw new BadDataError('Cannot change name of feature flag'); throw new BadDataError('Cannot change name of feature flag');
} }
const created = await this.featureService.updateFeatureToggle( const created =
projectId, await this.transactionalFeatureToggleService.transactional(
{ (service) =>
...data, service.updateFeatureToggle(
name: featureName, projectId,
}, {
featureName, ...data,
req.audit, name: featureName,
); },
featureName,
req.audit,
),
);
this.openApiService.respondWithValidation( this.openApiService.respondWithValidation(
200, 200,
@ -790,12 +793,16 @@ export default class ProjectFeaturesController extends Controller {
res: Response<FeatureSchema>, res: Response<FeatureSchema>,
): Promise<void> { ): Promise<void> {
const { projectId, featureName } = req.params; const { projectId, featureName } = req.params;
const updated = await this.featureService.patchFeature( const updated =
projectId, await this.transactionalFeatureToggleService.transactional(
featureName, (service) =>
req.body, service.patchFeature(
req.audit, projectId,
); featureName,
req.body,
req.audit,
),
);
this.openApiService.respondWithValidation( this.openApiService.respondWithValidation(
200, 200,
res, res,
@ -814,13 +821,8 @@ export default class ProjectFeaturesController extends Controller {
res: Response<void>, res: Response<void>,
): Promise<void> { ): Promise<void> {
const { featureName, projectId } = req.params; const { featureName, projectId } = req.params;
await this.startTransaction(async (tx) => await this.transactionalFeatureToggleService.transactional((service) =>
this.transactionalFeatureToggleService(tx).archiveToggle( service.archiveToggle(featureName, req.user, req.audit, projectId),
featureName,
req.user,
req.audit,
projectId,
),
); );
res.status(202).send(); res.status(202).send();
} }
@ -887,14 +889,16 @@ export default class ProjectFeaturesController extends Controller {
): Promise<void> { ): Promise<void> {
const { featureName, environment, projectId } = req.params; const { featureName, environment, projectId } = req.params;
const { shouldActivateDisabledStrategies } = req.query; const { shouldActivateDisabledStrategies } = req.query;
await this.featureService.updateEnabled( await this.transactionalFeatureToggleService.transactional((service) =>
projectId, service.updateEnabled(
featureName, projectId,
environment, featureName,
true, environment,
req.audit, true,
req.user, req.audit,
shouldActivateDisabledStrategies === 'true', req.user,
shouldActivateDisabledStrategies === 'true',
),
); );
res.status(200).end(); res.status(200).end();
} }
@ -917,8 +921,8 @@ export default class ProjectFeaturesController extends Controller {
return; return;
} }
await this.startTransaction(async (tx) => await this.transactionalFeatureToggleService.transactional((service) =>
this.transactionalFeatureToggleService(tx).bulkUpdateEnabled( service.bulkUpdateEnabled(
projectId, projectId,
features, features,
environment, environment,
@ -949,8 +953,8 @@ export default class ProjectFeaturesController extends Controller {
return; return;
} }
await this.startTransaction(async (tx) => await this.transactionalFeatureToggleService.transactional((service) =>
this.transactionalFeatureToggleService(tx).bulkUpdateEnabled( service.bulkUpdateEnabled(
projectId, projectId,
features, features,
environment, environment,
@ -968,13 +972,15 @@ export default class ProjectFeaturesController extends Controller {
res: Response<void>, res: Response<void>,
): Promise<void> { ): Promise<void> {
const { featureName, environment, projectId } = req.params; const { featureName, environment, projectId } = req.params;
await this.featureService.updateEnabled( await this.transactionalFeatureToggleService.transactional((service) =>
projectId, service.updateEnabled(
featureName, projectId,
environment, featureName,
false, environment,
req.audit, false,
req.user, req.audit,
req.user,
),
); );
res.status(200).end(); res.status(200).end();
} }
@ -994,12 +1000,16 @@ export default class ProjectFeaturesController extends Controller {
strategyConfig.segmentIds = []; strategyConfig.segmentIds = [];
} }
const strategy = await this.featureService.createStrategy( const strategy =
strategyConfig, await this.transactionalFeatureToggleService.transactional(
{ environment, projectId, featureName }, (service) =>
req.audit, service.createStrategy(
req.user, strategyConfig,
); { environment, projectId, featureName },
req.audit,
req.user,
),
);
const updatedStrategy = await this.featureService.getStrategy( const updatedStrategy = await this.featureService.getStrategy(
strategy.id, strategy.id,
@ -1031,10 +1041,8 @@ export default class ProjectFeaturesController extends Controller {
res: Response, res: Response,
): Promise<void> { ): Promise<void> {
const { featureName, projectId, environment } = req.params; const { featureName, projectId, environment } = req.params;
await this.startTransaction(async (tx) => await this.transactionalFeatureToggleService.transactional((service) =>
this.transactionalFeatureToggleService( service.updateStrategiesSortOrder(
tx,
).updateStrategiesSortOrder(
{ {
featureName, featureName,
environment, environment,
@ -1058,15 +1066,17 @@ export default class ProjectFeaturesController extends Controller {
req.body.segmentIds = []; req.body.segmentIds = [];
} }
const updatedStrategy = await this.startTransaction(async (tx) => const updatedStrategy =
this.transactionalFeatureToggleService(tx).updateStrategy( await this.transactionalFeatureToggleService.transactional(
strategyId, (service) =>
req.body, service.updateStrategy(
{ environment, projectId, featureName }, strategyId,
req.audit, req.body,
req.user, { environment, projectId, featureName },
), req.audit,
); req.user,
),
);
res.status(200).json(updatedStrategy); res.status(200).json(updatedStrategy);
} }
@ -1083,15 +1093,17 @@ export default class ProjectFeaturesController extends Controller {
throwOnInvalidSchema(featureStrategySchema.$id, newDocument); throwOnInvalidSchema(featureStrategySchema.$id, newDocument);
const updatedStrategy = await this.startTransaction(async (tx) => const updatedStrategy =
this.transactionalFeatureToggleService(tx).updateStrategy( await this.transactionalFeatureToggleService.transactional(
strategyId, (service) =>
newDocument, service.updateStrategy(
{ environment, projectId, featureName }, strategyId,
req.audit, newDocument,
req.user, { environment, projectId, featureName },
), req.audit,
); req.user,
),
);
res.status(200).json(updatedStrategy); res.status(200).json(updatedStrategy);
} }
@ -1115,36 +1127,17 @@ export default class ProjectFeaturesController extends Controller {
const { environment, projectId, featureName } = req.params; const { environment, projectId, featureName } = req.params;
const { strategyId } = req.params; const { strategyId } = req.params;
this.logger.info(strategyId); this.logger.info(strategyId);
const strategy = await this.featureService.deleteStrategy( const strategy =
strategyId, await this.transactionalFeatureToggleService.transactional(
{ environment, projectId, featureName }, (service) =>
req.audit, service.deleteStrategy(
req.user, strategyId,
); { environment, projectId, featureName },
res.status(200).json(strategy); req.audit,
} req.user,
),
async updateStrategyParameter(
req: IAuthRequest<
StrategyIdParams,
any,
{ name: string; value: string | number },
any
>,
res: Response<FeatureStrategySchema>,
): Promise<void> {
const { strategyId, environment, projectId, featureName } = req.params;
const { name, value } = req.body;
const updatedStrategy =
await this.featureService.updateStrategyParameter(
strategyId,
name,
value,
{ environment, projectId, featureName },
req.audit,
); );
res.status(200).json(updatedStrategy); res.status(200).json(strategy);
} }
async updateFeaturesTags( async updateFeaturesTags(

View File

@ -31,7 +31,6 @@ import type { OpenApiService } from '../../services';
import type { IAuthRequest } from '../../routes/unleash-types'; import type { IAuthRequest } from '../../routes/unleash-types';
import { ProjectApiTokenController } from '../../routes/admin-api/project/api-token'; import { ProjectApiTokenController } from '../../routes/admin-api/project/api-token';
import ProjectArchiveController from '../../routes/admin-api/project/project-archive'; import ProjectArchiveController from '../../routes/admin-api/project/project-archive';
import { createKnexTransactionStarter } from '../../db/transaction';
import type { Db } from '../../db/db'; import type { Db } from '../../db/db';
import DependentFeaturesController from '../dependent-features/dependent-features-controller'; import DependentFeaturesController from '../dependent-features/dependent-features-controller';
import type { ProjectOverviewSchema } from '../../openapi/spec/project-overview-schema'; import type { ProjectOverviewSchema } from '../../openapi/spec/project-overview-schema';
@ -222,14 +221,7 @@ export default class ProjectController extends Controller {
], ],
}); });
this.use( this.use('/', new ProjectFeaturesController(config, services).router);
'/',
new ProjectFeaturesController(
config,
services,
createKnexTransactionStarter(db),
).router,
);
this.use('/', new DependentFeaturesController(config, services).router); this.use('/', new DependentFeaturesController(config, services).router);
this.use( this.use(
'/', '/',
@ -238,14 +230,7 @@ export default class ProjectController extends Controller {
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);
this.use('/', new ProjectApiTokenController(config, services).router); this.use('/', new ProjectApiTokenController(config, services).router);
this.use( this.use('/', new ProjectArchiveController(config, services).router);
'/',
new ProjectArchiveController(
config,
services,
createKnexTransactionStarter(db),
).router,
);
this.use('/', new ProjectInsightsController(config, services).router); this.use('/', new ProjectInsightsController(config, services).router);
this.use('/', new ProjectStatusController(config, services).router); this.use('/', new ProjectStatusController(config, services).router);
this.use('/', new FeatureLifecycleController(config, services).router); this.use('/', new FeatureLifecycleController(config, services).router);

View File

@ -28,7 +28,6 @@ import InstanceAdminController from './instance-admin';
import TelemetryController from './telemetry'; import TelemetryController from './telemetry';
import FavoritesController from './favorites'; import FavoritesController from './favorites';
import MaintenanceController from '../../features/maintenance/maintenance-controller'; import MaintenanceController from '../../features/maintenance/maintenance-controller';
import { createKnexTransactionStarter } from '../../db/transaction';
import type { Db } from '../../db/db'; import type { Db } from '../../db/db';
import ExportImportController from '../../features/export-import-toggles/export-import-controller'; import ExportImportController from '../../features/export-import-toggles/export-import-controller';
import { SegmentsController } from '../../features/segment/segment-controller'; import { SegmentsController } from '../../features/segment/segment-controller';
@ -53,11 +52,7 @@ export class AdminApi extends Controller {
); );
this.app.use( this.app.use(
'/archive', '/archive',
new ArchiveController( new ArchiveController(config, services).router,
config,
services,
createKnexTransactionStarter(db),
).router,
); );
this.app.use( this.app.use(
'/strategies', '/strategies',

View File

@ -21,10 +21,7 @@ import {
createResponseSchema, createResponseSchema,
} from '../../../openapi'; } from '../../../openapi';
import Controller from '../../controller'; import Controller from '../../controller';
import type { import type { WithTransactional } from '../../../db/transaction';
TransactionCreator,
UnleashTransaction,
} from '../../../db/transaction';
const PATH = '/:projectId'; const PATH = '/:projectId';
const PATH_ARCHIVE = `${PATH}/archive`; const PATH_ARCHIVE = `${PATH}/archive`;
@ -37,10 +34,7 @@ export default class ProjectArchiveController extends Controller {
private featureService: FeatureToggleService; private featureService: FeatureToggleService;
private transactionalFeatureToggleService: ( private transactionalFeatureToggleService: WithTransactional<FeatureToggleService>;
db: UnleashTransaction,
) => FeatureToggleService;
private readonly startTransaction: TransactionCreator<UnleashTransaction>;
private openApiService: OpenApiService; private openApiService: OpenApiService;
@ -58,7 +52,6 @@ export default class ProjectArchiveController extends Controller {
| 'featureToggleService' | 'featureToggleService'
| 'openApiService' | 'openApiService'
>, >,
startTransaction: TransactionCreator<UnleashTransaction>,
) { ) {
super(config); super(config);
this.logger = config.getLogger('/admin-api/archive.js'); this.logger = config.getLogger('/admin-api/archive.js');
@ -67,7 +60,6 @@ export default class ProjectArchiveController extends Controller {
this.flagResolver = config.flagResolver; this.flagResolver = config.flagResolver;
this.transactionalFeatureToggleService = this.transactionalFeatureToggleService =
transactionalFeatureToggleService; transactionalFeatureToggleService;
this.startTransaction = startTransaction;
this.route({ this.route({
method: 'post', method: 'post',
@ -178,12 +170,8 @@ export default class ProjectArchiveController extends Controller {
): Promise<void> { ): Promise<void> {
const { projectId } = req.params; const { projectId } = req.params;
const { features } = req.body; const { features } = req.body;
await this.startTransaction(async (tx) => await this.transactionalFeatureToggleService.transactional((service) =>
this.transactionalFeatureToggleService(tx).reviveFeatures( service.reviveFeatures(features, projectId, req.audit),
features,
projectId,
req.audit,
),
); );
res.status(200).end(); res.status(200).end();
} }
@ -195,13 +183,8 @@ export default class ProjectArchiveController extends Controller {
const { features } = req.body; const { features } = req.body;
const { projectId } = req.params; const { projectId } = req.params;
await this.startTransaction(async (tx) => await this.transactionalFeatureToggleService.transactional((service) =>
this.transactionalFeatureToggleService(tx).archiveToggles( service.archiveToggles(features, req.user, req.audit, projectId),
features,
req.user,
req.audit,
projectId,
),
); );
res.status(202).end(); res.status(202).end();

View File

@ -69,6 +69,7 @@ import {
createFakeAccessService, createFakeAccessService,
createFakeEnvironmentService, createFakeEnvironmentService,
createFakeEventsService, createFakeEventsService,
createFakeFeatureToggleService,
createFakeProjectService, createFakeProjectService,
createFakeUserSubscriptionsService, createFakeUserSubscriptionsService,
createFeatureLifecycleService, createFeatureLifecycleService,
@ -133,8 +134,6 @@ import {
createFakeApiTokenService, createFakeApiTokenService,
} from '../features/api-tokens/createApiTokenService'; } from '../features/api-tokens/createApiTokenService';
import { IntegrationEventsService } from '../features/integration-events/integration-events-service'; import { IntegrationEventsService } from '../features/integration-events/integration-events-service';
import { FeatureCollaboratorsReadModel } from '../features/feature-toggle/feature-collaborators-read-model';
import { FakeFeatureCollaboratorsReadModel } from '../features/feature-toggle/fake-feature-collaborators-read-model';
import { import {
createFakePlaygroundService, createFakePlaygroundService,
createPlaygroundService, createPlaygroundService,
@ -160,8 +159,6 @@ import {
} from '../features/context/createContextService'; } from '../features/context/createContextService';
import { UniqueConnectionService } from '../features/unique-connection/unique-connection-service'; import { UniqueConnectionService } from '../features/unique-connection/unique-connection-service';
import { createFakeFeatureLinkService } from '../features/feature-links/createFeatureLinkService'; import { createFakeFeatureLinkService } from '../features/feature-links/createFeatureLinkService';
import { FeatureLinksReadModel } from '../features/feature-links/feature-links-read-model';
import { FakeFeatureLinksReadModel } from '../features/feature-links/fake-feature-links-read-model';
import { UnknownFlagsService } from '../features/metrics/unknown-flags/unknown-flags-service'; import { UnknownFlagsService } from '../features/metrics/unknown-flags/unknown-flags-service';
export const createServices = ( export const createServices = (
@ -287,26 +284,6 @@ export const createServices = (
? createFeatureSearchService(config)(db) ? createFeatureSearchService(config)(db)
: createFakeFeatureSearchService(config); : createFakeFeatureSearchService(config);
const featureCollaboratorsReadModel = db
? new FeatureCollaboratorsReadModel(db)
: new FakeFeatureCollaboratorsReadModel();
const featureLinksReadModel = db
? new FeatureLinksReadModel(db, config.eventBus)
: new FakeFeatureLinksReadModel();
const featureToggleService = new FeatureToggleService(stores, config, {
segmentService,
accessService,
eventService,
changeRequestAccessReadModel,
privateProjectChecker,
dependentFeaturesReadModel,
dependentFeaturesService,
featureLifecycleReadModel,
featureCollaboratorsReadModel,
featureLinksReadModel,
});
const transactionalEnvironmentService = db const transactionalEnvironmentService = db
? withTransactional(createEnvironmentService(config), db) ? withTransactional(createEnvironmentService(config), db)
: withFakeTransactional(createFakeEnvironmentService(config)); : withFakeTransactional(createFakeEnvironmentService(config));
@ -346,8 +323,12 @@ export const createServices = (
const importService = db const importService = db
? withTransactional(deferredExportImportTogglesService(config), db) ? withTransactional(deferredExportImportTogglesService(config), db)
: withFakeTransactional(createFakeExportImportTogglesService(config)); : withFakeTransactional(createFakeExportImportTogglesService(config));
const transactionalFeatureToggleService = (txDb: Knex.Transaction) => const featureToggleService = db
createFeatureToggleService(txDb, config); ? withTransactional((db) => createFeatureToggleService(db, config), db)
: withFakeTransactional(
createFakeFeatureToggleService(config).featureToggleService,
);
const transactionalFeatureToggleService = featureToggleService;
const transactionalGroupService = (txDb: Knex.Transaction) => const transactionalGroupService = (txDb: Knex.Transaction) =>
createGroupService(txDb, config); createGroupService(txDb, config);
const userSplashService = new UserSplashService(stores, config); const userSplashService = new UserSplashService(stores, config);

View File

@ -114,9 +114,7 @@ export interface IUnleashServices {
configurationRevisionService: ConfigurationRevisionService; configurationRevisionService: ConfigurationRevisionService;
schedulerService: SchedulerService; schedulerService: SchedulerService;
eventAnnouncerService: EventAnnouncerService; eventAnnouncerService: EventAnnouncerService;
transactionalFeatureToggleService: ( transactionalFeatureToggleService: WithTransactional<FeatureToggleService>;
db: Knex.Transaction,
) => FeatureToggleService;
transactionalGroupService: (db: Knex.Transaction) => GroupService; transactionalGroupService: (db: Knex.Transaction) => GroupService;
privateProjectChecker: IPrivateProjectChecker; privateProjectChecker: IPrivateProjectChecker;
dependentFeaturesService: DependentFeaturesService; dependentFeaturesService: DependentFeaturesService;

View File

@ -35,7 +35,11 @@ test('should not allow to create feature flags in maintenance mode', async () =>
}); });
test('maintenance mode is off by default', async () => { test('maintenance mode is off by default', async () => {
const appWithMaintenanceMode = await setupApp(db.stores); const appWithMaintenanceMode = await setupAppWithCustomConfig(
db.stores,
{},
db.rawDatabase,
);
return appWithMaintenanceMode.request return appWithMaintenanceMode.request
.post('/api/admin/projects/default/features') .post('/api/admin/projects/default/features')

View File

@ -11,14 +11,18 @@ let db: ITestDb;
beforeAll(async () => { beforeAll(async () => {
db = await dbInit('project_feature_variants_api_sunset', getLogger); db = await dbInit('project_feature_variants_api_sunset', getLogger);
app = await setupAppWithCustomConfig(db.stores, { app = await setupAppWithCustomConfig(
experimental: { db.stores,
flags: { {
strictSchemaValidation: true, experimental: {
enableLegacyVariants: false, flags: {
strictSchemaValidation: true,
enableLegacyVariants: false,
},
}, },
}, },
}); db.rawDatabase,
);
}); });
afterAll(async () => { afterAll(async () => {

View File

@ -12,14 +12,18 @@ let db: ITestDb;
beforeAll(async () => { beforeAll(async () => {
db = await dbInit('project_feature_variants_api_serial', getLogger); db = await dbInit('project_feature_variants_api_serial', getLogger);
app = await setupAppWithCustomConfig(db.stores, { app = await setupAppWithCustomConfig(
experimental: { db.stores,
flags: { {
strictSchemaValidation: true, experimental: {
enableLegacyVariants: true, flags: {
strictSchemaValidation: true,
enableLegacyVariants: true,
},
}, },
}, },
}); db.rawDatabase,
);
}); });
afterAll(async () => { afterAll(async () => {

View File

@ -19,7 +19,7 @@ beforeAll(async () => {
getLogger.setMuteError(true); getLogger.setMuteError(true);
db = await dbInit('user_pat', getLogger); db = await dbInit('user_pat', getLogger);
patStore = db.stores.patStore; patStore = db.stores.patStore;
app = await setupAppWithAuth(db.stores); app = await setupAppWithAuth(db.stores, {}, db.rawDatabase);
await app.request await app.request
.post(`/auth/demo/login`) .post(`/auth/demo/login`)