mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	fix: enable segment importing for oss (#5010)
This commit is contained in:
		
							parent
							
								
									66304cf8e7
								
							
						
					
					
						commit
						7b7a2a706c
					
				| @ -46,6 +46,10 @@ import { | |||||||
| import { DbServiceFactory } from 'lib/db/transaction'; | import { DbServiceFactory } from 'lib/db/transaction'; | ||||||
| import { DependentFeaturesReadModel } from '../dependent-features/dependent-features-read-model'; | import { DependentFeaturesReadModel } from '../dependent-features/dependent-features-read-model'; | ||||||
| import { FakeDependentFeaturesReadModel } from '../dependent-features/fake-dependent-features-read-model'; | import { FakeDependentFeaturesReadModel } from '../dependent-features/fake-dependent-features-read-model'; | ||||||
|  | import { | ||||||
|  |     createFakeSegmentService, | ||||||
|  |     createSegmentService, | ||||||
|  | } from '../segment/createSegmentService'; | ||||||
| 
 | 
 | ||||||
| export const createFakeExportImportTogglesService = ( | export const createFakeExportImportTogglesService = ( | ||||||
|     config: IUnleashConfig, |     config: IUnleashConfig, | ||||||
| @ -106,6 +110,8 @@ export const createFakeExportImportTogglesService = ( | |||||||
|     ); |     ); | ||||||
|     const dependentFeaturesReadModel = new FakeDependentFeaturesReadModel(); |     const dependentFeaturesReadModel = new FakeDependentFeaturesReadModel(); | ||||||
| 
 | 
 | ||||||
|  |     const segmentService = createFakeSegmentService(config); | ||||||
|  | 
 | ||||||
|     const exportImportService = new ExportImportService( |     const exportImportService = new ExportImportService( | ||||||
|         { |         { | ||||||
|             importTogglesStore, |             importTogglesStore, | ||||||
| @ -126,6 +132,7 @@ export const createFakeExportImportTogglesService = ( | |||||||
|             contextService, |             contextService, | ||||||
|             strategyService, |             strategyService, | ||||||
|             tagTypeService, |             tagTypeService, | ||||||
|  |             segmentService, | ||||||
|         }, |         }, | ||||||
|         dependentFeaturesReadModel, |         dependentFeaturesReadModel, | ||||||
|     ); |     ); | ||||||
| @ -220,6 +227,8 @@ export const deferredExportImportTogglesService = ( | |||||||
|         ); |         ); | ||||||
|         const dependentFeaturesReadModel = new DependentFeaturesReadModel(db); |         const dependentFeaturesReadModel = new DependentFeaturesReadModel(db); | ||||||
| 
 | 
 | ||||||
|  |         const segmentService = createSegmentService(db, config); | ||||||
|  | 
 | ||||||
|         const exportImportService = new ExportImportService( |         const exportImportService = new ExportImportService( | ||||||
|             { |             { | ||||||
|                 importTogglesStore, |                 importTogglesStore, | ||||||
| @ -240,6 +249,7 @@ export const deferredExportImportTogglesService = ( | |||||||
|                 contextService, |                 contextService, | ||||||
|                 strategyService, |                 strategyService, | ||||||
|                 tagTypeService, |                 tagTypeService, | ||||||
|  |                 segmentService, | ||||||
|             }, |             }, | ||||||
|             dependentFeaturesReadModel, |             dependentFeaturesReadModel, | ||||||
|         ); |         ); | ||||||
|  | |||||||
| @ -170,9 +170,18 @@ const tags = [ | |||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| const tagTypes = [ | const tagTypes = [ | ||||||
|     { name: 'bestt', description: 'test' }, |     { | ||||||
|     { name: 'special_tag', description: 'this is my special tag' }, |         name: 'bestt', | ||||||
|     { name: 'special_tag', description: 'this is my special tag' }, // deliberate duplicate
 |         description: 'test', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         name: 'special_tag', | ||||||
|  |         description: 'this is my special tag', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         name: 'special_tag', | ||||||
|  |         description: 'this is my special tag', | ||||||
|  |     }, // deliberate duplicate
 | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| const importPayload: ImportTogglesSchema = { | const importPayload: ImportTogglesSchema = { | ||||||
| @ -199,13 +208,19 @@ const importPayload: ImportTogglesSchema = { | |||||||
| 
 | 
 | ||||||
| const createUserEditorAccess = async (name, email) => { | const createUserEditorAccess = async (name, email) => { | ||||||
|     const { userStore } = stores; |     const { userStore } = stores; | ||||||
|     const user = await userStore.insert({ name, email }); |     const user = await userStore.insert({ | ||||||
|  |         name, | ||||||
|  |         email, | ||||||
|  |     }); | ||||||
|     return user; |     return user; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const createUserAdminAccess = async (name, email) => { | const createUserAdminAccess = async (name, email) => { | ||||||
|     const { userStore } = stores; |     const { userStore } = stores; | ||||||
|     const user = await userStore.insert({ name, email }); |     const user = await userStore.insert({ | ||||||
|  |         name, | ||||||
|  |         email, | ||||||
|  |     }); | ||||||
|     await accessService.addUserToRole(user.id, adminRole.id, 'default'); |     await accessService.addUserToRole(user.id, adminRole.id, 'default'); | ||||||
|     return user; |     return user; | ||||||
| }; | }; | ||||||
| @ -301,7 +316,12 @@ test('validate import data', async () => { | |||||||
|         data: { |         data: { | ||||||
|             ...importPayload.data, |             ...importPayload.data, | ||||||
|             featureStrategies: [{ name: 'customStrategy' }], |             featureStrategies: [{ name: 'customStrategy' }], | ||||||
|             segments: [{ id: 1, name: 'customSegment' }], |             segments: [ | ||||||
|  |                 { | ||||||
|  |                     id: 1, | ||||||
|  |                     name: 'customSegment', | ||||||
|  |                 }, | ||||||
|  |             ], | ||||||
|             contextFields: [ |             contextFields: [ | ||||||
|                 { |                 { | ||||||
|                     ...contextField, |                     ...contextField, | ||||||
| @ -328,6 +348,11 @@ test('validate import data', async () => { | |||||||
|                     'We detected the following context fields that do not have matching legal values with the imported ones:', |                     'We detected the following context fields that do not have matching legal values with the imported ones:', | ||||||
|                 affectedItems: [contextField.name], |                 affectedItems: [contextField.name], | ||||||
|             }, |             }, | ||||||
|  |             { | ||||||
|  |                 affectedItems: ['customSegment'], | ||||||
|  |                 message: | ||||||
|  |                     'We detected the following segments in the import file that need to be created first:', | ||||||
|  |             }, | ||||||
|         ], |         ], | ||||||
|         warnings: [ |         warnings: [ | ||||||
|             { |             { | ||||||
|  | |||||||
| @ -51,6 +51,7 @@ import { findDuplicates } from '../../util/findDuplicates'; | |||||||
| import { FeatureNameCheckResultWithFeaturePattern } from '../feature-toggle/feature-toggle-service'; | import { FeatureNameCheckResultWithFeaturePattern } from '../feature-toggle/feature-toggle-service'; | ||||||
| import { IDependentFeaturesReadModel } from '../dependent-features/dependent-features-read-model-type'; | import { IDependentFeaturesReadModel } from '../dependent-features/dependent-features-read-model-type'; | ||||||
| import groupBy from 'lodash.groupby'; | import groupBy from 'lodash.groupby'; | ||||||
|  | import { ISegmentService } from '../../segments/segment-service-interface'; | ||||||
| 
 | 
 | ||||||
| export type IImportService = { | export type IImportService = { | ||||||
|     validate( |     validate( | ||||||
| @ -103,6 +104,8 @@ export default class ExportImportService | |||||||
| 
 | 
 | ||||||
|     private tagTypeService: TagTypeService; |     private tagTypeService: TagTypeService; | ||||||
| 
 | 
 | ||||||
|  |     private segmentService: ISegmentService; | ||||||
|  | 
 | ||||||
|     private featureTagService: FeatureTagService; |     private featureTagService: FeatureTagService; | ||||||
| 
 | 
 | ||||||
|     private importPermissionsService: ImportPermissionsService; |     private importPermissionsService: ImportPermissionsService; | ||||||
| @ -133,6 +136,7 @@ export default class ExportImportService | |||||||
|             eventService, |             eventService, | ||||||
|             tagTypeService, |             tagTypeService, | ||||||
|             featureTagService, |             featureTagService, | ||||||
|  |             segmentService, | ||||||
|         }: Pick< |         }: Pick< | ||||||
|             IUnleashServices, |             IUnleashServices, | ||||||
|             | 'featureToggleService' |             | 'featureToggleService' | ||||||
| @ -142,6 +146,7 @@ export default class ExportImportService | |||||||
|             | 'eventService' |             | 'eventService' | ||||||
|             | 'tagTypeService' |             | 'tagTypeService' | ||||||
|             | 'featureTagService' |             | 'featureTagService' | ||||||
|  |             | 'segmentService' | ||||||
|         >, |         >, | ||||||
|         dependentFeaturesReadModel: IDependentFeaturesReadModel, |         dependentFeaturesReadModel: IDependentFeaturesReadModel, | ||||||
|     ) { |     ) { | ||||||
| @ -158,6 +163,7 @@ export default class ExportImportService | |||||||
|         this.strategyService = strategyService; |         this.strategyService = strategyService; | ||||||
|         this.contextService = contextService; |         this.contextService = contextService; | ||||||
|         this.accessService = accessService; |         this.accessService = accessService; | ||||||
|  |         this.segmentService = segmentService; | ||||||
|         this.eventService = eventService; |         this.eventService = eventService; | ||||||
|         this.tagTypeService = tagTypeService; |         this.tagTypeService = tagTypeService; | ||||||
|         this.featureTagService = featureTagService; |         this.featureTagService = featureTagService; | ||||||
| @ -187,6 +193,7 @@ export default class ExportImportService | |||||||
|             duplicateFeatures, |             duplicateFeatures, | ||||||
|             featureNameCheckResult, |             featureNameCheckResult, | ||||||
|             featureLimitResult, |             featureLimitResult, | ||||||
|  |             unsupportedSegments, | ||||||
|         ] = await Promise.all([ |         ] = await Promise.all([ | ||||||
|             this.getUnsupportedStrategies(dto), |             this.getUnsupportedStrategies(dto), | ||||||
|             this.getUsedCustomStrategies(dto), |             this.getUsedCustomStrategies(dto), | ||||||
| @ -202,6 +209,7 @@ export default class ExportImportService | |||||||
|             this.getDuplicateFeatures(dto), |             this.getDuplicateFeatures(dto), | ||||||
|             this.getInvalidFeatureNames(dto), |             this.getInvalidFeatureNames(dto), | ||||||
|             this.getFeatureLimit(dto), |             this.getFeatureLimit(dto), | ||||||
|  |             this.getUnsupportedSegments(dto), | ||||||
|         ]); |         ]); | ||||||
| 
 | 
 | ||||||
|         const errors = ImportValidationMessages.compileErrors({ |         const errors = ImportValidationMessages.compileErrors({ | ||||||
| @ -212,6 +220,7 @@ export default class ExportImportService | |||||||
|             duplicateFeatures, |             duplicateFeatures, | ||||||
|             featureNameCheckResult, |             featureNameCheckResult, | ||||||
|             featureLimitResult, |             featureLimitResult, | ||||||
|  |             segments: unsupportedSegments, | ||||||
|         }); |         }); | ||||||
|         const warnings = ImportValidationMessages.compileWarnings({ |         const warnings = ImportValidationMessages.compileWarnings({ | ||||||
|             archivedFeatures, |             archivedFeatures, | ||||||
| @ -240,6 +249,7 @@ export default class ExportImportService | |||||||
|             this.verifyContextFields(dto), |             this.verifyContextFields(dto), | ||||||
|             this.importPermissionsService.verifyPermissions(dto, user, mode), |             this.importPermissionsService.verifyPermissions(dto, user, mode), | ||||||
|             this.verifyFeatures(dto), |             this.verifyFeatures(dto), | ||||||
|  |             this.verifySegments(dto), | ||||||
|         ]); |         ]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -426,6 +436,38 @@ export default class ExportImportService | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private async getUnsupportedSegments( | ||||||
|  |         dto: ImportTogglesSchema, | ||||||
|  |     ): Promise<string[]> { | ||||||
|  |         const supportedSegments = await this.segmentService.getAll(); | ||||||
|  |         const targetProject = dto.project; | ||||||
|  |         return dto.data.segments | ||||||
|  |             ? dto.data.segments | ||||||
|  |                   .filter( | ||||||
|  |                       (importingSegment) => | ||||||
|  |                           !supportedSegments.find( | ||||||
|  |                               (existingSegment) => | ||||||
|  |                                   importingSegment.name === | ||||||
|  |                                       existingSegment.name && | ||||||
|  |                                   (!existingSegment.project || | ||||||
|  |                                       existingSegment.project === | ||||||
|  |                                           targetProject), | ||||||
|  |                           ), | ||||||
|  |                   ) | ||||||
|  | 
 | ||||||
|  |                   .map((it) => it.name) | ||||||
|  |             : []; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private async verifySegments(dto: ImportTogglesSchema) { | ||||||
|  |         const unsupportedSegments = await this.getUnsupportedSegments(dto); | ||||||
|  |         if (unsupportedSegments.length > 0) { | ||||||
|  |             throw new BadDataError( | ||||||
|  |                 `Unsupported segments: ${unsupportedSegments.join(', ')}`, | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private async verifyContextFields(dto: ImportTogglesSchema) { |     private async verifyContextFields(dto: ImportTogglesSchema) { | ||||||
|         const unsupportedContextFields = await this.getUnsupportedContextFields( |         const unsupportedContextFields = await this.getUnsupportedContextFields( | ||||||
|             dto, |             dto, | ||||||
|  | |||||||
| @ -1018,6 +1018,11 @@ test('validate import data', async () => { | |||||||
|                     'We detected you want to create 2 new features to a project that already has 0 existing features, exceeding the maximum limit of 1.', |                     'We detected you want to create 2 new features to a project that already has 0 existing features, exceeding the maximum limit of 1.', | ||||||
|                 affectedItems: [], |                 affectedItems: [], | ||||||
|             }, |             }, | ||||||
|  |             { | ||||||
|  |                 message: | ||||||
|  |                     'We detected the following segments in the import file that need to be created first:', | ||||||
|  |                 affectedItems: ['customSegment'], | ||||||
|  |             }, | ||||||
|         ], |         ], | ||||||
|         warnings: [ |         warnings: [ | ||||||
|             { |             { | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ export interface IErrorsParams { | |||||||
|     duplicateFeatures: string[]; |     duplicateFeatures: string[]; | ||||||
|     featureNameCheckResult: FeatureNameCheckResultWithFeaturePattern; |     featureNameCheckResult: FeatureNameCheckResultWithFeaturePattern; | ||||||
|     featureLimitResult: ProjectFeaturesLimit; |     featureLimitResult: ProjectFeaturesLimit; | ||||||
|  |     segments: string[]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface IWarningParams { | export interface IWarningParams { | ||||||
| @ -46,6 +47,7 @@ export class ImportValidationMessages { | |||||||
|         duplicateFeatures, |         duplicateFeatures, | ||||||
|         featureNameCheckResult, |         featureNameCheckResult, | ||||||
|         featureLimitResult, |         featureLimitResult, | ||||||
|  |         segments, | ||||||
|     }: IErrorsParams): ImportTogglesValidateItemSchema[] { |     }: IErrorsParams): ImportTogglesValidateItemSchema[] { | ||||||
|         const errors: ImportTogglesValidateItemSchema[] = []; |         const errors: ImportTogglesValidateItemSchema[] = []; | ||||||
| 
 | 
 | ||||||
| @ -106,6 +108,14 @@ export class ImportValidationMessages { | |||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if (segments.length > 0) { | ||||||
|  |             errors.push({ | ||||||
|  |                 message: | ||||||
|  |                     'We detected the following segments in the import file that need to be created first:', | ||||||
|  |                 affectedItems: segments, | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         return errors; |         return errors; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -142,7 +142,7 @@ export const exportResultSchema = { | |||||||
|             items: { |             items: { | ||||||
|                 type: 'object', |                 type: 'object', | ||||||
|                 additionalProperties: false, |                 additionalProperties: false, | ||||||
|                 required: ['id'], |                 required: ['id', 'name'], | ||||||
|                 properties: { |                 properties: { | ||||||
|                     id: { |                     id: { | ||||||
|                         type: 'number', |                         type: 'number', | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user