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:
parent
2a083edc14
commit
410142cb42
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
@ -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);
|
||||||
|
@ -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',
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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')
|
||||||
|
@ -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 () => {
|
||||||
|
@ -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 () => {
|
||||||
|
@ -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`)
|
||||||
|
Loading…
Reference in New Issue
Block a user