mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01: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