mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: add ip to state-service and group-service (#7120)
The add ip to two services. Despite state is being deprecated, I think we better get it out of the way.
This commit is contained in:
		
							parent
							
								
									a744cdf6d8
								
							
						
					
					
						commit
						345c34a945
					
				| @ -115,7 +115,7 @@ export default class ClientInstanceService { | ||||
|             if (appsToAnnounce.length > 0) { | ||||
|                 const events = appsToAnnounce.map((app) => ({ | ||||
|                     type: APPLICATION_CREATED, | ||||
|                     createdBy: app.createdBy || SYSTEM_USER.username, | ||||
|                     createdBy: app.createdBy || SYSTEM_USER.username!, | ||||
|                     data: app, | ||||
|                     createdByUserId: app.createdByUserId || SYSTEM_USER.id, | ||||
|                 })); | ||||
|  | ||||
| @ -123,7 +123,7 @@ class StateController extends Controller { | ||||
|             userName, | ||||
|             dropBeforeImport: paramToBool(drop, false), | ||||
|             keepExisting: paramToBool(keep, true), | ||||
|             userId: req.user.id, | ||||
|             auditUser: req.audit, | ||||
|         }); | ||||
|         res.sendStatus(202); | ||||
|     } | ||||
|  | ||||
| @ -19,7 +19,7 @@ import { | ||||
|     type IUnleashOptions, | ||||
|     type IUnleashServices, | ||||
|     RoleName, | ||||
|     SYSTEM_USER, | ||||
|     SYSTEM_USER_AUDIT, | ||||
| } from './types'; | ||||
| 
 | ||||
| import User, { type IAuditUser, type IUser } from './types/user'; | ||||
| @ -101,7 +101,7 @@ async function createApp( | ||||
|             dropBeforeImport: config.import.dropBeforeImport, | ||||
|             userName: 'import', | ||||
|             keepExisting: config.import.keepExisting, | ||||
|             userId: SYSTEM_USER.id, | ||||
|             auditUser: SYSTEM_USER_AUDIT, | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -21,6 +21,8 @@ import { | ||||
|     GROUP_CREATED, | ||||
|     GROUP_USER_ADDED, | ||||
|     GROUP_USER_REMOVED, | ||||
|     GroupUserAdded, | ||||
|     GroupUserRemoved, | ||||
|     type IBaseEvent, | ||||
| } from '../types/events'; | ||||
| import NameExistsError from '../error/name-exists-error'; | ||||
| @ -235,6 +237,7 @@ export class GroupService { | ||||
|         return this.groupStore.getProjectGroupRoles(projectId); | ||||
|     } | ||||
| 
 | ||||
|     /** @deprecated use syncExternalGroupsWithAudit */ | ||||
|     async syncExternalGroups( | ||||
|         userId: number, | ||||
|         externalGroups: string[], | ||||
| @ -286,6 +289,52 @@ export class GroupService { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async syncExternalGroupsWithAudit( | ||||
|         userId: number, | ||||
|         externalGroups: string[], | ||||
|         auditUser: IAuditUser, | ||||
|     ): Promise<void> { | ||||
|         if (Array.isArray(externalGroups)) { | ||||
|             const newGroups = await this.groupStore.getNewGroupsForExternalUser( | ||||
|                 userId, | ||||
|                 externalGroups, | ||||
|             ); | ||||
|             await this.groupStore.addUserToGroups( | ||||
|                 userId, | ||||
|                 newGroups.map((g) => g.id), | ||||
|                 auditUser.username, | ||||
|             ); | ||||
|             const oldGroups = await this.groupStore.getOldGroupsForExternalUser( | ||||
|                 userId, | ||||
|                 externalGroups, | ||||
|             ); | ||||
|             await this.groupStore.deleteUsersFromGroup(oldGroups); | ||||
| 
 | ||||
|             const events: IBaseEvent[] = []; | ||||
|             for (const group of newGroups) { | ||||
|                 events.push( | ||||
|                     new GroupUserAdded({ | ||||
|                         userId, | ||||
|                         groupId: group.id, | ||||
|                         auditUser, | ||||
|                     }), | ||||
|                 ); | ||||
|             } | ||||
| 
 | ||||
|             for (const group of oldGroups) { | ||||
|                 events.push( | ||||
|                     new GroupUserRemoved({ | ||||
|                         userId, | ||||
|                         groupId: group.groupId, | ||||
|                         auditUser, | ||||
|                     }), | ||||
|                 ); | ||||
|             } | ||||
| 
 | ||||
|             await this.eventService.storeEvents(events); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private mapGroupWithUsers( | ||||
|         group: IGroup, | ||||
|         allGroupUsers: IGroupUser[], | ||||
|  | ||||
| @ -14,7 +14,7 @@ import { | ||||
| import { GLOBAL_ENV } from '../types/environment'; | ||||
| import variantsExportV3 from '../../test/examples/variantsexport_v3.json'; | ||||
| import EventService from '../features/events/event-service'; | ||||
| import { SYSTEM_USER_ID } from '../types'; | ||||
| import { SYSTEM_USER_AUDIT } from '../types'; | ||||
| import { EventEmitter } from 'stream'; | ||||
| const oldExportExample = require('./state-service-export-v1.json'); | ||||
| const TESTUSERID = 3333; | ||||
| @ -103,7 +103,7 @@ test('should import a feature', async () => { | ||||
|         ], | ||||
|     }; | ||||
| 
 | ||||
|     await stateService.import({ userId: SYSTEM_USER_ID, data }); | ||||
|     await stateService.import({ auditUser: SYSTEM_USER_AUDIT, data }); | ||||
| 
 | ||||
|     const events = await stores.eventStore.getEvents(); | ||||
|     expect(events).toHaveLength(1); | ||||
| @ -130,7 +130,7 @@ test('should not import an existing feature', async () => { | ||||
|     await stateService.import({ | ||||
|         data, | ||||
|         keepExisting: true, | ||||
|         userId: SYSTEM_USER_ID, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|     }); | ||||
| 
 | ||||
|     const events = await stores.eventStore.getEvents(); | ||||
| @ -157,7 +157,7 @@ test('should not keep existing feature if drop-before-import', async () => { | ||||
|         data, | ||||
|         keepExisting: true, | ||||
|         dropBeforeImport: true, | ||||
|         userId: SYSTEM_USER_ID, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|     }); | ||||
| 
 | ||||
|     const events = await stores.eventStore.getEvents(); | ||||
| @ -182,7 +182,7 @@ test('should drop feature before import if specified', async () => { | ||||
|     await stateService.import({ | ||||
|         data, | ||||
|         dropBeforeImport: true, | ||||
|         userId: SYSTEM_USER_ID, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|     }); | ||||
| 
 | ||||
|     const events = await stores.eventStore.getEvents(); | ||||
| @ -204,7 +204,7 @@ test('should import a strategy', async () => { | ||||
|         ], | ||||
|     }; | ||||
| 
 | ||||
|     await stateService.import({ userId: SYSTEM_USER_ID, data }); | ||||
|     await stateService.import({ auditUser: SYSTEM_USER_AUDIT, data }); | ||||
| 
 | ||||
|     const events = await stores.eventStore.getEvents(); | ||||
|     expect(events).toHaveLength(1); | ||||
| @ -228,7 +228,7 @@ test('should not import an existing strategy', async () => { | ||||
| 
 | ||||
|     await stateService.import({ | ||||
|         data, | ||||
|         userId: SYSTEM_USER_ID, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|         keepExisting: true, | ||||
|     }); | ||||
| 
 | ||||
| @ -250,7 +250,7 @@ test('should drop strategies before import if specified', async () => { | ||||
| 
 | ||||
|     await stateService.import({ | ||||
|         data, | ||||
|         userId: SYSTEM_USER_ID, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|         dropBeforeImport: true, | ||||
|     }); | ||||
| 
 | ||||
| @ -268,7 +268,7 @@ test('should drop neither features nor strategies when neither is imported', asy | ||||
| 
 | ||||
|     await stateService.import({ | ||||
|         data, | ||||
|         userId: SYSTEM_USER_ID, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|         dropBeforeImport: true, | ||||
|     }); | ||||
| 
 | ||||
| @ -286,11 +286,11 @@ test('should not accept gibberish', async () => { | ||||
|     const data2 = '{somerandomtext/'; | ||||
| 
 | ||||
|     await expect(async () => | ||||
|         stateService.import({ userId: SYSTEM_USER_ID, data: data1 }), | ||||
|         stateService.import({ auditUser: SYSTEM_USER_AUDIT, data: data1 }), | ||||
|     ).rejects.toThrow(); | ||||
| 
 | ||||
|     await expect(async () => | ||||
|         stateService.import({ userId: SYSTEM_USER_ID, data: data2 }), | ||||
|         stateService.import({ auditUser: SYSTEM_USER_AUDIT, data: data2 }), | ||||
|     ).rejects.toThrow(); | ||||
| }); | ||||
| 
 | ||||
| @ -386,7 +386,7 @@ test('should import a tag and tag type', async () => { | ||||
|         tags: [{ type: 'simple', value: 'test' }], | ||||
|     }; | ||||
| 
 | ||||
|     await stateService.import({ userId: SYSTEM_USER_ID, data }); | ||||
|     await stateService.import({ auditUser: SYSTEM_USER_AUDIT, data }); | ||||
| 
 | ||||
|     const events = await stores.eventStore.getEvents(); | ||||
|     expect(events).toHaveLength(2); | ||||
| @ -423,7 +423,7 @@ test('Should not import an existing tag', async () => { | ||||
|     ); | ||||
|     await stateService.import({ | ||||
|         data, | ||||
|         userId: SYSTEM_USER_ID, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|         keepExisting: true, | ||||
|     }); | ||||
|     const events = await stores.eventStore.getEvents(); | ||||
| @ -460,7 +460,7 @@ test('Should not keep existing tags if drop-before-import', async () => { | ||||
|     }; | ||||
|     await stateService.import({ | ||||
|         data, | ||||
|         userId: SYSTEM_USER_ID, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|         dropBeforeImport: true, | ||||
|     }); | ||||
|     const tagTypes = await stores.tagTypeStore.getAll(); | ||||
| @ -570,7 +570,7 @@ test('should import a project', async () => { | ||||
|         ], | ||||
|     }; | ||||
| 
 | ||||
|     await stateService.import({ userId: SYSTEM_USER_ID, data }); | ||||
|     await stateService.import({ auditUser: SYSTEM_USER_AUDIT, data }); | ||||
| 
 | ||||
|     const events = await stores.eventStore.getEvents(); | ||||
|     expect(events).toHaveLength(1); | ||||
| @ -595,13 +595,13 @@ test('Should not import an existing project', async () => { | ||||
| 
 | ||||
|     await stateService.import({ | ||||
|         data, | ||||
|         userId: SYSTEM_USER_ID, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|         keepExisting: true, | ||||
|     }); | ||||
|     const events = await stores.eventStore.getEvents(); | ||||
|     expect(events).toHaveLength(0); | ||||
| 
 | ||||
|     await stateService.import({ userId: SYSTEM_USER_ID, data }); | ||||
|     await stateService.import({ auditUser: SYSTEM_USER_AUDIT, data }); | ||||
| }); | ||||
| 
 | ||||
| test('Should drop projects before import if specified', async () => { | ||||
| @ -624,7 +624,7 @@ test('Should drop projects before import if specified', async () => { | ||||
|     }); | ||||
|     await stateService.import({ | ||||
|         data, | ||||
|         userId: SYSTEM_USER_ID, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|         dropBeforeImport: true, | ||||
|     }); | ||||
|     const hasProject = await stores.projectStore.hasProject('fancy'); | ||||
| @ -773,8 +773,7 @@ test('featureStrategies can keep existing', async () => { | ||||
|     const exported = await stateService.export({}); | ||||
|     await stateService.import({ | ||||
|         data: exported, | ||||
|         userId: SYSTEM_USER_ID, | ||||
|         userName: 'testing', | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|         keepExisting: true, | ||||
|     }); | ||||
|     expect(await stores.featureStrategiesStore.getAll()).toHaveLength(1); | ||||
| @ -830,8 +829,7 @@ test('featureStrategies should not keep existing if dropBeforeImport', async () | ||||
|     exported.featureStrategies = []; | ||||
|     await stateService.import({ | ||||
|         data: exported, | ||||
|         userId: SYSTEM_USER_ID, | ||||
|         userName: 'testing', | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|         keepExisting: true, | ||||
|         dropBeforeImport: true, | ||||
|     }); | ||||
| @ -842,9 +840,8 @@ test('Import v1 and exporting v2 should work', async () => { | ||||
|     const { stateService } = getSetup(); | ||||
|     await stateService.import({ | ||||
|         data: oldExportExample, | ||||
|         userId: SYSTEM_USER_ID, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|         dropBeforeImport: true, | ||||
|         userName: 'testing', | ||||
|     }); | ||||
|     const exported = await stateService.export({}); | ||||
|     const strategiesCount = oldExportExample.features.reduce( | ||||
| @ -879,8 +876,7 @@ test('Importing states with deprecated strategies should keep their deprecated s | ||||
|     }; | ||||
|     await stateService.import({ | ||||
|         data: deprecatedStrategyExample, | ||||
|         userId: SYSTEM_USER_ID, | ||||
|         userName: 'strategy-importer', | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|         dropBeforeImport: true, | ||||
|         keepExisting: false, | ||||
|     }); | ||||
| @ -894,9 +890,8 @@ test('Exporting a deprecated strategy and then importing it should keep correct | ||||
|     await stateService.import({ | ||||
|         data: variantsExportV3, | ||||
|         keepExisting: false, | ||||
|         userId: SYSTEM_USER_ID, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|         dropBeforeImport: true, | ||||
|         userName: 'strategy importer', | ||||
|     }); | ||||
|     const rolloutRandom = await stores.strategyStore.get( | ||||
|         'gradualRolloutRandom', | ||||
|  | ||||
| @ -1,19 +1,19 @@ | ||||
| import { stateSchema } from './state-schema'; | ||||
| import { | ||||
|     DROP_ENVIRONMENTS, | ||||
|     DROP_FEATURE_TAGS, | ||||
|     DROP_FEATURES, | ||||
|     DROP_PROJECTS, | ||||
|     DROP_STRATEGIES, | ||||
|     DROP_TAG_TYPES, | ||||
|     DROP_TAGS, | ||||
|     ENVIRONMENT_IMPORT, | ||||
|     FEATURE_IMPORT, | ||||
|     FEATURE_TAG_IMPORT, | ||||
|     PROJECT_IMPORT, | ||||
|     STRATEGY_IMPORT, | ||||
|     TAG_IMPORT, | ||||
|     TAG_TYPE_IMPORT, | ||||
|     DropEnvironmentsEvent, | ||||
|     DropFeaturesEvent, | ||||
|     DropFeatureTagsEvent, | ||||
|     DropProjectsEvent, | ||||
|     DropStrategiesEvent, | ||||
|     DropTagsEvent, | ||||
|     DropTagTypesEvent, | ||||
|     EnvironmentImport, | ||||
|     FeatureImport, | ||||
|     FeatureTagImport, | ||||
|     ProjectImport, | ||||
|     StrategyImport, | ||||
|     TagImport, | ||||
|     TagTypeImport, | ||||
| } from '../types/events'; | ||||
| 
 | ||||
| import { filterEqual, filterExisting, parseFile, readFile } from './state-util'; | ||||
| @ -53,6 +53,7 @@ import { GLOBAL_ENV } from '../types/environment'; | ||||
| import type { ISegmentStore } from '../features/segment/segment-store-type'; | ||||
| import type { PartialSome } from '../types/partial'; | ||||
| import type EventService from '../features/events/event-service'; | ||||
| import type { IAuditUser } from '../server-impl'; | ||||
| 
 | ||||
| export interface IBackupOption { | ||||
|     includeFeatureToggles: boolean; | ||||
| @ -117,8 +118,7 @@ export default class StateService { | ||||
|     async importFile({ | ||||
|         file, | ||||
|         dropBeforeImport = false, | ||||
|         userName = 'import-user', | ||||
|         userId, | ||||
|         auditUser, | ||||
|         keepExisting = true, | ||||
|     }: IImportFile): Promise<void> { | ||||
|         return readFile(file) | ||||
| @ -126,10 +126,9 @@ export default class StateService { | ||||
|             .then((data) => | ||||
|                 this.import({ | ||||
|                     data, | ||||
|                     userName, | ||||
|                     auditUser, | ||||
|                     dropBeforeImport, | ||||
|                     keepExisting, | ||||
|                     userId, | ||||
|                 }), | ||||
|             ); | ||||
|     } | ||||
| @ -169,8 +168,7 @@ export default class StateService { | ||||
| 
 | ||||
|     async import({ | ||||
|         data, | ||||
|         userName = 'importUser', | ||||
|         userId, | ||||
|         auditUser, | ||||
|         dropBeforeImport = false, | ||||
|         keepExisting = true, | ||||
|     }: IImportData): Promise<void> { | ||||
| @ -186,10 +184,9 @@ export default class StateService { | ||||
|         if (importData.environments) { | ||||
|             importedEnvironments = await this.importEnvironments({ | ||||
|                 environments: data.environments, | ||||
|                 userName, | ||||
|                 dropBeforeImport, | ||||
|                 keepExisting, | ||||
|                 userId, | ||||
|                 auditUser, | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
| @ -197,10 +194,9 @@ export default class StateService { | ||||
|             await this.importProjects({ | ||||
|                 projects: data.projects, | ||||
|                 importedEnvironments, | ||||
|                 userName, | ||||
|                 dropBeforeImport, | ||||
|                 keepExisting, | ||||
|                 userId, | ||||
|                 auditUser, | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
| @ -217,11 +213,10 @@ export default class StateService { | ||||
| 
 | ||||
|             await this.importFeatures({ | ||||
|                 features, | ||||
|                 userName, | ||||
|                 dropBeforeImport, | ||||
|                 keepExisting, | ||||
|                 featureEnvironments, | ||||
|                 userId, | ||||
|                 auditUser, | ||||
|             }); | ||||
| 
 | ||||
|             if (featureEnvironments) { | ||||
| @ -240,10 +235,9 @@ export default class StateService { | ||||
|         if (importData.strategies) { | ||||
|             await this.importStrategies({ | ||||
|                 strategies: data.strategies, | ||||
|                 userName, | ||||
|                 dropBeforeImport, | ||||
|                 keepExisting, | ||||
|                 userId, | ||||
|                 auditUser, | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
| @ -263,18 +257,16 @@ export default class StateService { | ||||
|                             tagValue: t.tagValue || t.value, | ||||
|                             tagType: t.tagType || t.type, | ||||
|                         })) || [], | ||||
|                 userName, | ||||
|                 dropBeforeImport, | ||||
|                 keepExisting, | ||||
|                 userId, | ||||
|                 auditUser, | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         if (importData.segments) { | ||||
|             await this.importSegments( | ||||
|                 data.segments, | ||||
|                 userName, | ||||
|                 userId, | ||||
|                 auditUser, | ||||
|                 dropBeforeImport, | ||||
|             ); | ||||
|         } | ||||
| @ -365,14 +357,12 @@ export default class StateService { | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 | ||||
|     async importFeatures({ | ||||
|         features, | ||||
|         userName, | ||||
|         userId, | ||||
|         dropBeforeImport, | ||||
|         keepExisting, | ||||
|         featureEnvironments, | ||||
|         auditUser, | ||||
|     }): Promise<void> { | ||||
|         this.logger.info(`Importing ${features.length} feature flags`); | ||||
|         const oldToggles = dropBeforeImport | ||||
| @ -382,12 +372,9 @@ export default class StateService { | ||||
|         if (dropBeforeImport) { | ||||
|             this.logger.info('Dropping existing feature flags'); | ||||
|             await this.toggleStore.deleteAll(); | ||||
|             await this.eventService.storeEvent({ | ||||
|                 type: DROP_FEATURES, | ||||
|                 createdBy: userName, | ||||
|                 createdByUserId: userId, | ||||
|                 data: { name: 'all-features' }, | ||||
|             }); | ||||
|             await this.eventService.storeEvent( | ||||
|                 new DropFeaturesEvent({ auditUser }), | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         await Promise.all( | ||||
| @ -396,7 +383,7 @@ export default class StateService { | ||||
|                 .filter(filterEqual(oldToggles)) | ||||
|                 .map(async (feature) => { | ||||
|                     await this.toggleStore.create(feature.project, { | ||||
|                         createdByUserId: userId, | ||||
|                         createdByUserId: auditUser.id, | ||||
|                         ...feature, | ||||
|                     }); | ||||
|                     await this.featureEnvironmentStore.connectFeatureToEnvironmentsForProject( | ||||
| @ -404,12 +391,12 @@ export default class StateService { | ||||
|                         feature.project, | ||||
|                         this.enabledIn(feature.name, featureEnvironments), | ||||
|                     ); | ||||
|                     await this.eventService.storeEvent({ | ||||
|                         type: FEATURE_IMPORT, | ||||
|                         createdByUserId: userId, | ||||
|                         createdBy: userName, | ||||
|                         data: feature, | ||||
|                     }); | ||||
|                     await this.eventService.storeEvent( | ||||
|                         new FeatureImport({ | ||||
|                             feature, | ||||
|                             auditUser, | ||||
|                         }), | ||||
|                     ); | ||||
|                 }), | ||||
|         ); | ||||
|     } | ||||
| @ -417,10 +404,9 @@ export default class StateService { | ||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 | ||||
|     async importStrategies({ | ||||
|         strategies, | ||||
|         userName, | ||||
|         userId, | ||||
|         dropBeforeImport, | ||||
|         keepExisting, | ||||
|         auditUser, | ||||
|     }): Promise<void> { | ||||
|         this.logger.info(`Importing ${strategies.length} strategies`); | ||||
|         const oldStrategies = dropBeforeImport | ||||
| @ -430,12 +416,9 @@ export default class StateService { | ||||
|         if (dropBeforeImport) { | ||||
|             this.logger.info('Dropping existing strategies'); | ||||
|             await this.strategyStore.dropCustomStrategies(); | ||||
|             await this.eventService.storeEvent({ | ||||
|                 type: DROP_STRATEGIES, | ||||
|                 createdBy: userName, | ||||
|                 createdByUserId: userId, | ||||
|                 data: { name: 'all-strategies' }, | ||||
|             }); | ||||
|             await this.eventService.storeEvent( | ||||
|                 new DropStrategiesEvent({ auditUser }), | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         await Promise.all( | ||||
| @ -444,12 +427,9 @@ export default class StateService { | ||||
|                 .filter(filterEqual(oldStrategies)) | ||||
|                 .map((strategy) => | ||||
|                     this.strategyStore.importStrategy(strategy).then(() => { | ||||
|                         this.eventService.storeEvent({ | ||||
|                             type: STRATEGY_IMPORT, | ||||
|                             createdBy: userName, | ||||
|                             createdByUserId: userId, | ||||
|                             data: strategy, | ||||
|                         }); | ||||
|                         this.eventService.storeEvent( | ||||
|                             new StrategyImport({ strategy, auditUser }), | ||||
|                         ); | ||||
|                     }), | ||||
|                 ), | ||||
|         ); | ||||
| @ -458,8 +438,7 @@ export default class StateService { | ||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 | ||||
|     async importEnvironments({ | ||||
|         environments, | ||||
|         userName, | ||||
|         userId, | ||||
|         auditUser, | ||||
|         dropBeforeImport, | ||||
|         keepExisting, | ||||
|     }): Promise<IEnvironment[]> { | ||||
| @ -470,12 +449,9 @@ export default class StateService { | ||||
|         if (dropBeforeImport) { | ||||
|             this.logger.info('Dropping existing environments'); | ||||
|             await this.environmentStore.deleteAll(); | ||||
|             await this.eventService.storeEvent({ | ||||
|                 type: DROP_ENVIRONMENTS, | ||||
|                 createdBy: userName, | ||||
|                 createdByUserId: userId, | ||||
|                 data: { name: 'all-environments' }, | ||||
|             }); | ||||
|             await this.eventService.storeEvent( | ||||
|                 new DropEnvironmentsEvent({ auditUser }), | ||||
|             ); | ||||
|         } | ||||
|         const envsImport = environments.filter((env) => | ||||
|             keepExisting ? !oldEnvs.some((old) => old.name === env.name) : true, | ||||
| @ -484,12 +460,9 @@ export default class StateService { | ||||
|         if (envsImport.length > 0) { | ||||
|             importedEnvs = | ||||
|                 await this.environmentStore.importEnvironments(envsImport); | ||||
|             const importedEnvironmentEvents = importedEnvs.map((env) => ({ | ||||
|                 type: ENVIRONMENT_IMPORT, | ||||
|                 createdBy: userName, | ||||
|                 createdByUserId: userId, | ||||
|                 data: env, | ||||
|             })); | ||||
|             const importedEnvironmentEvents = importedEnvs.map( | ||||
|                 (env) => new EnvironmentImport({ auditUser, env }), | ||||
|             ); | ||||
|             await this.eventService.storeEvents(importedEnvironmentEvents); | ||||
|         } | ||||
|         return importedEnvs; | ||||
| @ -499,8 +472,7 @@ export default class StateService { | ||||
|     async importProjects({ | ||||
|         projects, | ||||
|         importedEnvironments, | ||||
|         userName, | ||||
|         userId, | ||||
|         auditUser, | ||||
|         dropBeforeImport, | ||||
|         keepExisting, | ||||
|     }): Promise<void> { | ||||
| @ -511,12 +483,9 @@ export default class StateService { | ||||
|         if (dropBeforeImport) { | ||||
|             this.logger.info('Dropping existing projects'); | ||||
|             await this.projectStore.deleteAll(); | ||||
|             await this.eventService.storeEvent({ | ||||
|                 type: DROP_PROJECTS, | ||||
|                 createdBy: userName, | ||||
|                 createdByUserId: userId, | ||||
|                 data: { name: 'all-projects' }, | ||||
|             }); | ||||
|             await this.eventService.storeEvent( | ||||
|                 new DropProjectsEvent({ auditUser }), | ||||
|             ); | ||||
|         } | ||||
|         const projectsToImport = projects.filter((project) => | ||||
|             keepExisting | ||||
| @ -528,12 +497,9 @@ export default class StateService { | ||||
|                 projectsToImport, | ||||
|                 importedEnvironments, | ||||
|             ); | ||||
|             const importedProjectEvents = importedProjects.map((project) => ({ | ||||
|                 type: PROJECT_IMPORT, | ||||
|                 createdBy: userName, | ||||
|                 createdByUserId: userId, | ||||
|                 data: project, | ||||
|             })); | ||||
|             const importedProjectEvents = importedProjects.map( | ||||
|                 (project) => new ProjectImport({ project, auditUser }), | ||||
|             ); | ||||
|             await this.eventService.storeEvents(importedProjectEvents); | ||||
|         } | ||||
|     } | ||||
| @ -543,8 +509,7 @@ export default class StateService { | ||||
|         tagTypes, | ||||
|         tags, | ||||
|         featureTags, | ||||
|         userName, | ||||
|         userId, | ||||
|         auditUser, | ||||
|         dropBeforeImport, | ||||
|         keepExisting, | ||||
|     }): Promise<void> { | ||||
| @ -566,40 +531,23 @@ export default class StateService { | ||||
|             await this.tagStore.deleteAll(); | ||||
|             await this.tagTypeStore.deleteAll(); | ||||
|             await this.eventService.storeEvents([ | ||||
|                 { | ||||
|                     type: DROP_FEATURE_TAGS, | ||||
|                     createdBy: userName, | ||||
|                     createdByUserId: userId, | ||||
|                     data: { name: 'all-feature-tags' }, | ||||
|                 }, | ||||
|                 { | ||||
|                     type: DROP_TAGS, | ||||
|                     createdBy: userName, | ||||
|                     createdByUserId: userId, | ||||
|                     data: { name: 'all-tags' }, | ||||
|                 }, | ||||
|                 { | ||||
|                     type: DROP_TAG_TYPES, | ||||
|                     createdBy: userName, | ||||
|                     createdByUserId: userId, | ||||
|                     data: { name: 'all-tag-types' }, | ||||
|                 }, | ||||
|                 new DropFeatureTagsEvent({ auditUser }), | ||||
|                 new DropTagsEvent({ auditUser }), | ||||
|                 new DropTagTypesEvent({ auditUser }), | ||||
|             ]); | ||||
|         } | ||||
|         await this.importTagTypes( | ||||
|             tagTypes, | ||||
|             keepExisting, | ||||
|             oldTagTypes, | ||||
|             userName, | ||||
|             userId, | ||||
|             auditUser, | ||||
|         ); | ||||
|         await this.importTags(tags, keepExisting, oldTags, userName, userId); | ||||
|         await this.importTags(tags, keepExisting, oldTags, auditUser); | ||||
|         await this.importFeatureTags( | ||||
|             featureTags, | ||||
|             keepExisting, | ||||
|             oldFeatureTags, | ||||
|             userName, | ||||
|             userId, | ||||
|             auditUser, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
| @ -615,8 +563,7 @@ export default class StateService { | ||||
|         featureTags: IFeatureTag[], | ||||
|         keepExisting: boolean, | ||||
|         oldFeatureTags: IFeatureTag[], | ||||
|         userName: string, | ||||
|         userId: number, | ||||
|         auditUser: IAuditUser, | ||||
|     ): Promise<void> { | ||||
|         const featureTagsToInsert = featureTags | ||||
|             .filter((tag) => | ||||
| @ -627,18 +574,15 @@ export default class StateService { | ||||
|                     : true, | ||||
|             ) | ||||
|             .map((tag) => ({ | ||||
|                 createdByUserId: userId, | ||||
|                 createdByUserId: auditUser.id, | ||||
|                 ...tag, | ||||
|             })); | ||||
|         if (featureTagsToInsert.length > 0) { | ||||
|             const importedFeatureTags = | ||||
|                 await this.featureTagStore.tagFeatures(featureTagsToInsert); | ||||
|             const importedFeatureTagEvents = importedFeatureTags.map((tag) => ({ | ||||
|                 type: FEATURE_TAG_IMPORT, | ||||
|                 createdBy: userName, | ||||
|                 createdByUserId: userId, | ||||
|                 data: tag, | ||||
|             })); | ||||
|             const importedFeatureTagEvents = importedFeatureTags.map( | ||||
|                 (featureTag) => new FeatureTagImport({ featureTag, auditUser }), | ||||
|             ); | ||||
|             await this.eventService.storeEvents(importedFeatureTagEvents); | ||||
|         } | ||||
|     } | ||||
| @ -650,8 +594,7 @@ export default class StateService { | ||||
|         tags: ITag[], | ||||
|         keepExisting: boolean, | ||||
|         oldTags: ITag[], | ||||
|         userName: string, | ||||
|         userId: number, | ||||
|         auditUser: IAuditUser, | ||||
|     ): Promise<void> { | ||||
|         const tagsToInsert = tags.filter((tag) => | ||||
|             keepExisting | ||||
| @ -660,12 +603,9 @@ export default class StateService { | ||||
|         ); | ||||
|         if (tagsToInsert.length > 0) { | ||||
|             const importedTags = await this.tagStore.bulkImport(tagsToInsert); | ||||
|             const importedTagEvents = importedTags.map((tag) => ({ | ||||
|                 type: TAG_IMPORT, | ||||
|                 createdBy: userName, | ||||
|                 createdByUserId: userId, | ||||
|                 data: tag, | ||||
|             })); | ||||
|             const importedTagEvents = importedTags.map( | ||||
|                 (tag) => new TagImport({ tag, auditUser }), | ||||
|             ); | ||||
|             await this.eventService.storeEvents(importedTagEvents); | ||||
|         } | ||||
|     } | ||||
| @ -674,8 +614,7 @@ export default class StateService { | ||||
|         tagTypes: ITagType[], | ||||
|         keepExisting: boolean, | ||||
|         oldTagTypes: ITagType[], | ||||
|         userName: string, | ||||
|         userId: number, | ||||
|         auditUser: IAuditUser, | ||||
|     ): Promise<void> { | ||||
|         const tagTypesToInsert = tagTypes.filter((tagType) => | ||||
|             keepExisting | ||||
| @ -685,20 +624,16 @@ export default class StateService { | ||||
|         if (tagTypesToInsert.length > 0) { | ||||
|             const importedTagTypes = | ||||
|                 await this.tagTypeStore.bulkImport(tagTypesToInsert); | ||||
|             const importedTagTypeEvents = importedTagTypes.map((tagType) => ({ | ||||
|                 type: TAG_TYPE_IMPORT, | ||||
|                 createdBy: userName, | ||||
|                 createdByUserId: userId, | ||||
|                 data: tagType, | ||||
|             })); | ||||
|             const importedTagTypeEvents = importedTagTypes.map( | ||||
|                 (tagType) => new TagTypeImport({ tagType, auditUser }), | ||||
|             ); | ||||
|             await this.eventService.storeEvents(importedTagTypeEvents); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async importSegments( | ||||
|         segments: PartialSome<ISegment, 'id'>[], | ||||
|         userName: string, | ||||
|         userId: number, | ||||
|         auditUser: IAuditUser, | ||||
|         dropBeforeImport: boolean, | ||||
|     ): Promise<void> { | ||||
|         if (dropBeforeImport) { | ||||
| @ -707,7 +642,9 @@ export default class StateService { | ||||
| 
 | ||||
|         await Promise.all( | ||||
|             segments.map((segment) => | ||||
|                 this.segmentStore.create(segment, { username: userName }), | ||||
|                 this.segmentStore.create(segment, { | ||||
|                     username: auditUser.username, | ||||
|                 }), | ||||
|             ), | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| @ -1,7 +1,16 @@ | ||||
| import type { FeatureToggle, IStrategyConfig, ITag, IVariant } from './model'; | ||||
| import type { | ||||
|     FeatureToggle, | ||||
|     IEnvironment, | ||||
|     IProject, | ||||
|     IStrategyConfig, | ||||
|     ITag, | ||||
|     IVariant, | ||||
| } from './model'; | ||||
| import type { IApiToken } from './models/api-token'; | ||||
| import type { IAuditUser, IUserWithRootRole } from './user'; | ||||
| import type { FeatureLifecycleCompletedSchema } from '../openapi'; | ||||
| import type { ITagType } from '../features/tag-type/tag-type-store-type'; | ||||
| import type { IFeatureAndTag } from './stores/feature-tag-store'; | ||||
| 
 | ||||
| export const APPLICATION_CREATED = 'application-created' as const; | ||||
| 
 | ||||
| @ -356,7 +365,8 @@ export interface IBaseEvent { | ||||
|     tags?: ITag[]; | ||||
| } | ||||
| 
 | ||||
| export interface IEvent extends IBaseEvent { | ||||
| // This represents the read model for events
 | ||||
| export interface IEvent extends Omit<IBaseEvent, 'ip'> { | ||||
|     id: number; | ||||
|     createdAt: Date; | ||||
| } | ||||
| @ -366,7 +376,7 @@ export interface IEventList { | ||||
|     events: IEvent[]; | ||||
| } | ||||
| 
 | ||||
| class BaseEvent implements IBaseEvent { | ||||
| export class BaseEvent implements IBaseEvent { | ||||
|     readonly type: IEventType; | ||||
| 
 | ||||
|     readonly createdBy: string; | ||||
| @ -633,6 +643,83 @@ export class FeatureCreatedEvent extends BaseEvent { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class ProjectImport extends BaseEvent { | ||||
|     readonly data: IProject; | ||||
|     constructor(p: { | ||||
|         project: IProject; | ||||
|         auditUser: IAuditUser; | ||||
|     }) { | ||||
|         super(PROJECT_IMPORT, p.auditUser); | ||||
|         this.data = p.project; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class FeatureImport extends BaseEvent { | ||||
|     readonly data: any; | ||||
|     constructor(p: { | ||||
|         feature: any; | ||||
|         auditUser: IAuditUser; | ||||
|     }) { | ||||
|         super(FEATURE_IMPORT, p.auditUser); | ||||
|         this.data = p.feature; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class StrategyImport extends BaseEvent { | ||||
|     readonly data: any; | ||||
|     constructor(p: { | ||||
|         strategy: any; | ||||
|         auditUser: IAuditUser; | ||||
|     }) { | ||||
|         super(STRATEGY_IMPORT, p.auditUser); | ||||
|         this.data = p.strategy; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class EnvironmentImport extends BaseEvent { | ||||
|     readonly data: IEnvironment; | ||||
|     constructor(p: { | ||||
|         env: IEnvironment; | ||||
|         auditUser: IAuditUser; | ||||
|     }) { | ||||
|         super(ENVIRONMENT_IMPORT, p.auditUser); | ||||
|         this.data = p.env; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class TagTypeImport extends BaseEvent { | ||||
|     readonly data: ITagType; | ||||
|     constructor(p: { | ||||
|         tagType: ITagType; | ||||
|         auditUser: IAuditUser; | ||||
|     }) { | ||||
|         super(TAG_TYPE_IMPORT, p.auditUser); | ||||
|         this.data = p.tagType; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class TagImport extends BaseEvent { | ||||
|     readonly data: ITag; | ||||
|     constructor(p: { | ||||
|         tag: ITag; | ||||
|         auditUser: IAuditUser; | ||||
|     }) { | ||||
|         super(TAG_IMPORT, p.auditUser); | ||||
|         this.data = p.tag; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class FeatureTagImport extends BaseEvent { | ||||
|     readonly data: IFeatureAndTag; | ||||
|     constructor(p: { | ||||
|         featureTag: IFeatureAndTag; | ||||
|         auditUser: IAuditUser; | ||||
|     }) { | ||||
|         super(FEATURE_TAG_IMPORT, p.auditUser); | ||||
|         this.data = p.featureTag; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class FeatureCompletedEvent extends BaseEvent { | ||||
|     readonly featureName: string; | ||||
|     readonly data: FeatureLifecycleCompletedSchema; | ||||
| @ -1202,6 +1289,36 @@ export class ProjectAccessGroupRolesUpdated extends BaseEvent { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class GroupUserRemoved extends BaseEvent { | ||||
|     readonly preData: any; | ||||
|     constructor(p: { | ||||
|         userId: number; | ||||
|         groupId: number; | ||||
|         auditUser: IAuditUser; | ||||
|     }) { | ||||
|         super(GROUP_USER_REMOVED, p.auditUser); | ||||
|         this.preData = { | ||||
|             groupId: p.groupId, | ||||
|             userId: p.userId, | ||||
|         }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class GroupUserAdded extends BaseEvent { | ||||
|     readonly data: any; | ||||
|     constructor(p: { | ||||
|         userId: number; | ||||
|         groupId: number; | ||||
|         auditUser: IAuditUser; | ||||
|     }) { | ||||
|         super(GROUP_USER_ADDED, p.auditUser); | ||||
|         this.data = { | ||||
|             groupId: p.groupId, | ||||
|             userId: p.userId, | ||||
|         }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class ProjectAccessUserRolesDeleted extends BaseEvent { | ||||
|     readonly project: string; | ||||
| 
 | ||||
| @ -1558,6 +1675,83 @@ export class FeaturesExportedEvent extends BaseEvent { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class DropProjectsEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|     }) { | ||||
|         super(DROP_PROJECTS, eventData.auditUser); | ||||
|         this.data = { name: 'all-projects' }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class DropFeaturesEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|     }) { | ||||
|         super(DROP_FEATURES, eventData.auditUser); | ||||
|         this.data = { name: 'all-features' }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class DropStrategiesEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|     }) { | ||||
|         super(DROP_STRATEGIES, eventData.auditUser); | ||||
|         this.data = { name: 'all-strategies' }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class DropEnvironmentsEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|     }) { | ||||
|         super(DROP_ENVIRONMENTS, eventData.auditUser); | ||||
|         this.data = { name: 'all-environments' }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class DropFeatureTagsEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|     }) { | ||||
|         super(DROP_FEATURE_TAGS, eventData.auditUser); | ||||
|         this.data = { name: 'all-feature-tags' }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class DropTagsEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|     }) { | ||||
|         super(DROP_TAGS, eventData.auditUser); | ||||
|         this.data = { name: 'all-tags' }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class DropTagTypesEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|     constructor(eventData: { | ||||
|         auditUser: IAuditUser; | ||||
|     }) { | ||||
|         super(DROP_TAG_TYPES, eventData.auditUser); | ||||
|         this.data = { name: 'all-tag-types' }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class RoleCreatedEvent extends BaseEvent { | ||||
|     readonly data: any; | ||||
| 
 | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| import type { ITagType } from '../features/tag-type/tag-type-store-type'; | ||||
| import type { LogProvider } from '../logger'; | ||||
| import type { IRole } from './stores/access-store'; | ||||
| import type { IUser } from './user'; | ||||
| import type { IAuditUser, IUser } from './user'; | ||||
| import type { ALL_OPERATORS } from '../util'; | ||||
| import type { IProjectStats } from '../features/project/project-service'; | ||||
| import type { CreateFeatureStrategySchema } from '../openapi'; | ||||
| @ -479,8 +479,11 @@ export interface IImportFile extends ImportCommon { | ||||
| interface ImportCommon { | ||||
|     dropBeforeImport?: boolean; | ||||
|     keepExisting?: boolean; | ||||
|     /** @deprecated should use auditUser instead */ | ||||
|     userName?: string; | ||||
|     userId: number; | ||||
|     /** @deprecated should use auditUser instead */ | ||||
|     userId?: number; | ||||
|     auditUser: IAuditUser; | ||||
| } | ||||
| 
 | ||||
| export interface IImportData extends ImportCommon { | ||||
|  | ||||
| @ -206,8 +206,7 @@ test('Can roundtrip. I.e. export and then import', async () => { | ||||
|         data, | ||||
|         dropBeforeImport: true, | ||||
|         keepExisting: false, | ||||
|         userName: 'export-tester', | ||||
|         userId: -9999, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|     }); | ||||
| }); | ||||
| 
 | ||||
| @ -270,8 +269,7 @@ test('Roundtrip with tags works', async () => { | ||||
|         data, | ||||
|         dropBeforeImport: true, | ||||
|         keepExisting: false, | ||||
|         userName: 'export-tester', | ||||
|         userId: -9999, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|     }); | ||||
| 
 | ||||
|     const f = await app.services.featureTagService.listTags(featureName); | ||||
| @ -341,8 +339,7 @@ test('Roundtrip with strategies in multiple environments works', async () => { | ||||
|         data, | ||||
|         dropBeforeImport: true, | ||||
|         keepExisting: false, | ||||
|         userName: 'export-tester', | ||||
|         userId: -9999, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|     }); | ||||
|     const f = await app.services.featureToggleServiceV2.getFeature({ | ||||
|         featureName, | ||||
| @ -423,8 +420,7 @@ test(`should not delete api_tokens on import when drop-flag is set`, async () => | ||||
|         data, | ||||
|         dropBeforeImport: true, | ||||
|         keepExisting: false, | ||||
|         userName: userName, | ||||
|         userId: -9999, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|     }); | ||||
| 
 | ||||
|     const apiTokens = await app.services.apiTokenService.getAllTokens(); | ||||
|  | ||||
| @ -4,7 +4,7 @@ import StateService from '../../../lib/services/state-service'; | ||||
| import oldFormat from '../../examples/variantsexport_v3.json'; | ||||
| import { WeightType } from '../../../lib/types/model'; | ||||
| import { EventService } from '../../../lib/services'; | ||||
| import type { IUnleashStores } from '../../../lib/types'; | ||||
| import { SYSTEM_USER_AUDIT, type IUnleashStores } from '../../../lib/types'; | ||||
| 
 | ||||
| let stores: IUnleashStores; | ||||
| let db: ITestDb; | ||||
| @ -143,7 +143,7 @@ test('Should import variants from old format and convert to new format (per envi | ||||
|         data: oldFormat, | ||||
|         keepExisting: false, | ||||
|         dropBeforeImport: true, | ||||
|         userId: -9999, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|     }); | ||||
|     const featureEnvironments = await stores.featureEnvironmentStore.getAll(); | ||||
|     expect(featureEnvironments).toHaveLength(6); // There are 3 environments enabled and 2 features
 | ||||
| @ -158,14 +158,14 @@ test('Should import variants in new format (per environment)', async () => { | ||||
|         data: oldFormat, | ||||
|         keepExisting: false, | ||||
|         dropBeforeImport: true, | ||||
|         userId: -9999, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|     }); | ||||
|     const exportedJson = await stateService.export({}); | ||||
|     await stateService.import({ | ||||
|         data: exportedJson, | ||||
|         keepExisting: false, | ||||
|         dropBeforeImport: true, | ||||
|         userId: -9999, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|     }); | ||||
|     const featureEnvironments = await stores.featureEnvironmentStore.getAll(); | ||||
|     expect(featureEnvironments).toHaveLength(6); // 3 environments, 2 features === 6 rows
 | ||||
| @ -190,10 +190,9 @@ test('Importing states with deprecated strategies should keep their deprecated s | ||||
|     }; | ||||
|     await stateService.import({ | ||||
|         data: deprecatedStrategyExample, | ||||
|         userName: 'strategy-importer', | ||||
|         dropBeforeImport: true, | ||||
|         keepExisting: false, | ||||
|         userId: -9999, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|     }); | ||||
|     const deprecatedStrategy = | ||||
|         await stores.strategyStore.get('deprecatedstrat'); | ||||
| @ -205,8 +204,7 @@ test('Exporting a deprecated strategy and then importing it should keep correct | ||||
|         data: oldFormat, | ||||
|         keepExisting: false, | ||||
|         dropBeforeImport: true, | ||||
|         userName: 'strategy importer', | ||||
|         userId: -9999, | ||||
|         auditUser: SYSTEM_USER_AUDIT, | ||||
|     }); | ||||
|     const rolloutRandom = await stores.strategyStore.get( | ||||
|         'gradualRolloutRandom', | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user