mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
feat: link templates applied on flags (#9976)
This commit is contained in:
parent
0429c1985a
commit
57d11ae6b3
@ -61,6 +61,10 @@ import { FakeFeatureCollaboratorsReadModel } from './fake-feature-collaborators-
|
||||
import { FeatureCollaboratorsReadModel } from './feature-collaborators-read-model';
|
||||
import { FeatureLinksReadModel } from '../feature-links/feature-links-read-model';
|
||||
import { FakeFeatureLinksReadModel } from '../feature-links/fake-feature-links-read-model';
|
||||
import {
|
||||
createFakeFeatureLinkService,
|
||||
createFeatureLinkService,
|
||||
} from '../feature-links/createFeatureLinkService';
|
||||
|
||||
export const createFeatureToggleService = (
|
||||
db: Db,
|
||||
@ -132,6 +136,8 @@ export const createFeatureToggleService = (
|
||||
|
||||
const featureLinksReadModel = new FeatureLinksReadModel(db, eventBus);
|
||||
|
||||
const featureLinkService = createFeatureLinkService(config)(db);
|
||||
|
||||
const featureToggleService = new FeatureToggleService(
|
||||
{
|
||||
featureStrategiesStore,
|
||||
@ -155,6 +161,7 @@ export const createFeatureToggleService = (
|
||||
featureLifecycleReadModel,
|
||||
featureCollaboratorsReadModel,
|
||||
featureLinksReadModel,
|
||||
featureLinkService,
|
||||
},
|
||||
);
|
||||
return featureToggleService;
|
||||
@ -197,6 +204,7 @@ export const createFakeFeatureToggleService = (config: IUnleashConfig) => {
|
||||
const featureCollaboratorsReadModel =
|
||||
new FakeFeatureCollaboratorsReadModel();
|
||||
const featureLinksReadModel = new FakeFeatureLinksReadModel();
|
||||
const { featureLinkService } = createFakeFeatureLinkService(config);
|
||||
|
||||
const featureToggleService = new FeatureToggleService(
|
||||
{
|
||||
@ -226,6 +234,7 @@ export const createFakeFeatureToggleService = (config: IUnleashConfig) => {
|
||||
featureLifecycleReadModel,
|
||||
featureCollaboratorsReadModel,
|
||||
featureLinksReadModel,
|
||||
featureLinkService,
|
||||
},
|
||||
);
|
||||
return {
|
||||
|
@ -117,6 +117,7 @@ import type {
|
||||
IFeatureLink,
|
||||
IFeatureLinksReadModel,
|
||||
} from '../feature-links/feature-links-read-model-type';
|
||||
import type FeatureLinkService from '../feature-links/feature-link-service';
|
||||
|
||||
interface IFeatureContext {
|
||||
featureName: string;
|
||||
@ -176,6 +177,7 @@ export type ServicesAndReadModels = {
|
||||
dependentFeaturesService: DependentFeaturesService;
|
||||
featureLifecycleReadModel: IFeatureLifecycleReadModel;
|
||||
featureCollaboratorsReadModel: IFeatureCollaboratorsReadModel;
|
||||
featureLinkService: FeatureLinkService;
|
||||
featureLinksReadModel: IFeatureLinksReadModel;
|
||||
};
|
||||
|
||||
@ -218,6 +220,8 @@ class FeatureToggleService {
|
||||
|
||||
private featureLinksReadModel: IFeatureLinksReadModel;
|
||||
|
||||
private featureLinkService: FeatureLinkService;
|
||||
|
||||
private dependentFeaturesService: DependentFeaturesService;
|
||||
|
||||
private eventBus: EventEmitter;
|
||||
@ -247,6 +251,7 @@ class FeatureToggleService {
|
||||
featureLifecycleReadModel,
|
||||
featureCollaboratorsReadModel,
|
||||
featureLinksReadModel,
|
||||
featureLinkService,
|
||||
}: ServicesAndReadModels,
|
||||
) {
|
||||
this.logger = getLogger('services/feature-toggle-service.ts');
|
||||
@ -269,6 +274,7 @@ class FeatureToggleService {
|
||||
this.featureLifecycleReadModel = featureLifecycleReadModel;
|
||||
this.featureCollaboratorsReadModel = featureCollaboratorsReadModel;
|
||||
this.featureLinksReadModel = featureLinksReadModel;
|
||||
this.featureLinkService = featureLinkService;
|
||||
this.eventBus = eventBus;
|
||||
this.resourceLimits = resourceLimits;
|
||||
}
|
||||
@ -1340,6 +1346,8 @@ class FeatureToggleService {
|
||||
}),
|
||||
);
|
||||
|
||||
await this.addLinksFromTemplates(projectId, featureName, auditUser);
|
||||
|
||||
return createdToggle;
|
||||
}
|
||||
throw new NotFoundError(
|
||||
@ -2543,6 +2551,32 @@ class FeatureToggleService {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async addLinksFromTemplates(
|
||||
projectId: string,
|
||||
featureName: string,
|
||||
auditUser: IAuditUser,
|
||||
) {
|
||||
if (!this.flagResolver.isEnabled('projectLinkTemplates')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const featureLinksFromTemplates = (
|
||||
await this.projectStore.getProjectLinkTemplates(projectId)
|
||||
).map((template) => ({
|
||||
title: template.title,
|
||||
url: template.urlTemplate
|
||||
.replace(/{{project}}/g, projectId)
|
||||
.replace(/{{feature}}/g, featureName),
|
||||
featureName,
|
||||
}));
|
||||
|
||||
return Promise.all(
|
||||
featureLinksFromTemplates.map((link) =>
|
||||
this.featureLinkService.createLink(projectId, link, auditUser),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default FeatureToggleService;
|
||||
|
@ -25,16 +25,19 @@ import {
|
||||
import type { ISegmentService } from '../../segment/segment-service-interface';
|
||||
import {
|
||||
createEventsService,
|
||||
createFeatureLinkService,
|
||||
createFeatureToggleService,
|
||||
createSegmentService,
|
||||
} from '../..';
|
||||
import { insertLastSeenAt } from '../../../../test/e2e/helpers/test-helper';
|
||||
import type { EventService } from '../../../services';
|
||||
import type FeatureLinkService from '../../feature-links/feature-link-service';
|
||||
|
||||
let stores: IUnleashStores;
|
||||
let db: ITestDb;
|
||||
let service: FeatureToggleService;
|
||||
let segmentService: ISegmentService;
|
||||
let featureLinkService: FeatureLinkService;
|
||||
let eventService: EventService;
|
||||
let environmentService: EnvironmentService;
|
||||
let unleashConfig: IUnleashConfig;
|
||||
@ -49,19 +52,27 @@ const mockConstraints = (): IConstraint[] => {
|
||||
const irrelevantDate = new Date();
|
||||
|
||||
beforeAll(async () => {
|
||||
const config = createTestConfig({
|
||||
experimental: { flags: {} },
|
||||
});
|
||||
const flags = {
|
||||
featureLinks: true,
|
||||
projectLinkTemplates: true,
|
||||
};
|
||||
const config = createTestConfig({ experimental: { flags } });
|
||||
|
||||
db = await dbInit(
|
||||
'feature_toggle_service_v2_service_serial',
|
||||
config.getLogger,
|
||||
{ dbInitMethod: 'legacy' as const },
|
||||
{
|
||||
dbInitMethod: 'legacy' as const,
|
||||
experimental: { flags },
|
||||
},
|
||||
);
|
||||
unleashConfig = config;
|
||||
stores = db.stores;
|
||||
|
||||
segmentService = createSegmentService(db.rawDatabase, config);
|
||||
|
||||
featureLinkService = createFeatureLinkService(config)(db.rawDatabase);
|
||||
|
||||
service = createFeatureToggleService(db.rawDatabase, config);
|
||||
|
||||
eventService = createEventsService(db.rawDatabase, config);
|
||||
@ -878,3 +889,47 @@ test('Should enable disabled strategies on feature environment enabled', async (
|
||||
const strategy = await service.getStrategy(createdConfig.id);
|
||||
expect(strategy).toMatchObject({ ...config, disabled: false });
|
||||
});
|
||||
|
||||
test('Should add links from templates when creating a feature flag', async () => {
|
||||
const projectId = 'default';
|
||||
const featureName = 'test-link-feature';
|
||||
|
||||
await stores.projectStore.updateProjectEnterpriseSettings({
|
||||
id: projectId,
|
||||
linkTemplates: [
|
||||
{
|
||||
title: 'Issue tracker',
|
||||
urlTemplate:
|
||||
'https://issues.example.com/project/{{project}}/tasks/{{feature}}',
|
||||
},
|
||||
{
|
||||
title: 'Docs',
|
||||
urlTemplate: 'https://docs.example.com/{{project}}/{{feature}}',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await service.createFeatureToggle(
|
||||
projectId,
|
||||
{ name: featureName },
|
||||
TEST_AUDIT_USER,
|
||||
);
|
||||
|
||||
const links = await featureLinkService.getAll();
|
||||
|
||||
expect(links.length).toBe(2);
|
||||
expect(links).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: 'Issue tracker',
|
||||
url: `https://issues.example.com/project/${projectId}/tasks/${featureName}`,
|
||||
featureName,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: 'Docs',
|
||||
url: `https://docs.example.com/${projectId}/${featureName}`,
|
||||
featureName,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
@ -69,6 +69,7 @@ import {
|
||||
createFakeAccessService,
|
||||
createFakeEnvironmentService,
|
||||
createFakeEventsService,
|
||||
createFakeFeatureLinkService,
|
||||
createFakeFeatureToggleService,
|
||||
createFakeProjectService,
|
||||
createFakeUserSubscriptionsService,
|
||||
@ -158,7 +159,6 @@ import {
|
||||
createFakeContextService,
|
||||
} from '../features/context/createContextService';
|
||||
import { UniqueConnectionService } from '../features/unique-connection/unique-connection-service';
|
||||
import { createFakeFeatureLinkService } from '../features/feature-links/createFeatureLinkService';
|
||||
import { UnknownFlagsService } from '../features/metrics/unknown-flags/unknown-flags-service';
|
||||
|
||||
export const createServices = (
|
||||
@ -323,6 +323,15 @@ export const createServices = (
|
||||
const importService = db
|
||||
? withTransactional(deferredExportImportTogglesService(config), db)
|
||||
: withFakeTransactional(createFakeExportImportTogglesService(config));
|
||||
|
||||
const transactionalFeatureLinkService = db
|
||||
? withTransactional(createFeatureLinkService(config), db)
|
||||
: withFakeTransactional(
|
||||
createFakeFeatureLinkService(config).featureLinkService,
|
||||
);
|
||||
|
||||
const featureLinkService = transactionalFeatureLinkService;
|
||||
|
||||
const featureToggleService = db
|
||||
? withTransactional((db) => createFeatureToggleService(db, config), db)
|
||||
: withFakeTransactional(
|
||||
@ -417,14 +426,6 @@ export const createServices = (
|
||||
? withTransactional(createUserSubscriptionsService(config), db)
|
||||
: withFakeTransactional(createFakeUserSubscriptionsService(config));
|
||||
|
||||
const transactionalFeatureLinkService = db
|
||||
? withTransactional(createFeatureLinkService(config), db)
|
||||
: withFakeTransactional(
|
||||
createFakeFeatureLinkService(config).featureLinkService,
|
||||
);
|
||||
|
||||
const featureLinkService = transactionalFeatureLinkService;
|
||||
|
||||
return {
|
||||
transactionalAccessService,
|
||||
accessService,
|
||||
|
Loading…
Reference in New Issue
Block a user