mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: project owners in project service (#6935)
Schema and integrating into service and controller for project owners --------- Co-authored-by: Thomas Heartman <thomas@getunleash.io>
This commit is contained in:
		
							parent
							
								
									7d01dbb748
								
							
						
					
					
						commit
						66ec9a2f2f
					
				| @ -43,6 +43,7 @@ import FeatureSearchStore from '../features/feature-search/feature-search-store' | |||||||
| import { InactiveUsersStore } from '../users/inactive/inactive-users-store'; | import { InactiveUsersStore } from '../users/inactive/inactive-users-store'; | ||||||
| import { TrafficDataUsageStore } from '../features/traffic-data-usage/traffic-data-usage-store'; | import { TrafficDataUsageStore } from '../features/traffic-data-usage/traffic-data-usage-store'; | ||||||
| import { SegmentReadModel } from '../features/segment/segment-read-model'; | import { SegmentReadModel } from '../features/segment/segment-read-model'; | ||||||
|  | import { ProjectOwnersReadModel } from '../features/project/project-owners-read-model'; | ||||||
| 
 | 
 | ||||||
| export const createStores = ( | export const createStores = ( | ||||||
|     config: IUnleashConfig, |     config: IUnleashConfig, | ||||||
| @ -148,6 +149,7 @@ export const createStores = ( | |||||||
|         inactiveUsersStore: new InactiveUsersStore(db, eventBus, getLogger), |         inactiveUsersStore: new InactiveUsersStore(db, eventBus, getLogger), | ||||||
|         trafficDataUsageStore: new TrafficDataUsageStore(db, getLogger), |         trafficDataUsageStore: new TrafficDataUsageStore(db, getLogger), | ||||||
|         segmentReadModel: new SegmentReadModel(db), |         segmentReadModel: new SegmentReadModel(db), | ||||||
|  |         projectOwnersReadModel: new ProjectOwnersReadModel(db), | ||||||
|     }; |     }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -41,6 +41,8 @@ import { | |||||||
| import FakeFeatureTagStore from '../../../test/fixtures/fake-feature-tag-store'; | import FakeFeatureTagStore from '../../../test/fixtures/fake-feature-tag-store'; | ||||||
| import FeatureTypeStore from '../../db/feature-type-store'; | import FeatureTypeStore from '../../db/feature-type-store'; | ||||||
| import FakeFeatureTypeStore from '../../../test/fixtures/fake-feature-type-store'; | import FakeFeatureTypeStore from '../../../test/fixtures/fake-feature-type-store'; | ||||||
|  | import { ProjectOwnersReadModel } from './project-owners-read-model'; | ||||||
|  | import { FakeProjectOwnersReadModel } from './fake-project-owners-read-model'; | ||||||
| 
 | 
 | ||||||
| export const createProjectService = ( | export const createProjectService = ( | ||||||
|     db: Db, |     db: Db, | ||||||
| @ -54,6 +56,7 @@ export const createProjectService = ( | |||||||
|         getLogger, |         getLogger, | ||||||
|         flagResolver, |         flagResolver, | ||||||
|     ); |     ); | ||||||
|  |     const projectOwnersReadModel = new ProjectOwnersReadModel(db); | ||||||
|     const groupStore = new GroupStore(db); |     const groupStore = new GroupStore(db); | ||||||
|     const featureToggleStore = new FeatureToggleStore( |     const featureToggleStore = new FeatureToggleStore( | ||||||
|         db, |         db, | ||||||
| @ -115,6 +118,7 @@ export const createProjectService = ( | |||||||
|             featureTypeStore, |             featureTypeStore, | ||||||
|             accountStore, |             accountStore, | ||||||
|             projectStatsStore, |             projectStatsStore, | ||||||
|  |             projectOwnersReadModel, | ||||||
|         }, |         }, | ||||||
|         config, |         config, | ||||||
|         accessService, |         accessService, | ||||||
| @ -131,6 +135,7 @@ export const createFakeProjectService = ( | |||||||
| ): ProjectService => { | ): ProjectService => { | ||||||
|     const { getLogger } = config; |     const { getLogger } = config; | ||||||
|     const eventStore = new FakeEventStore(); |     const eventStore = new FakeEventStore(); | ||||||
|  |     const projectOwnersReadModel = new FakeProjectOwnersReadModel(); | ||||||
|     const projectStore = new FakeProjectStore(); |     const projectStore = new FakeProjectStore(); | ||||||
|     const groupStore = new FakeGroupStore(); |     const groupStore = new FakeGroupStore(); | ||||||
|     const featureToggleStore = new FakeFeatureToggleStore(); |     const featureToggleStore = new FakeFeatureToggleStore(); | ||||||
| @ -169,6 +174,7 @@ export const createFakeProjectService = ( | |||||||
|     return new ProjectService( |     return new ProjectService( | ||||||
|         { |         { | ||||||
|             projectStore, |             projectStore, | ||||||
|  |             projectOwnersReadModel, | ||||||
|             eventStore, |             eventStore, | ||||||
|             featureToggleStore, |             featureToggleStore, | ||||||
|             environmentStore, |             environmentStore, | ||||||
|  | |||||||
							
								
								
									
										16
									
								
								src/lib/features/project/fake-project-owners-read-model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/lib/features/project/fake-project-owners-read-model.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | import type { IProjectWithCount } from '../../types'; | ||||||
|  | import type { | ||||||
|  |     IProjectOwnersReadModel, | ||||||
|  |     IProjectWithCountAndOwners, | ||||||
|  | } from './project-owners-read-model.type'; | ||||||
|  | 
 | ||||||
|  | export class FakeProjectOwnersReadModel implements IProjectOwnersReadModel { | ||||||
|  |     async addOwners( | ||||||
|  |         projects: IProjectWithCount[], | ||||||
|  |     ): Promise<IProjectWithCountAndOwners[]> { | ||||||
|  |         return projects.map((project) => ({ | ||||||
|  |             ...project, | ||||||
|  |             owners: [{ ownerType: 'system' }], | ||||||
|  |         })); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -197,9 +197,19 @@ export default class ProjectController extends Controller { | |||||||
|             user.id, |             user.id, | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         // if (this.flagResolver.isEnabled('projectsListNewCards')) {
 |         if (this.flagResolver.isEnabled('projectsListNewCards')) { | ||||||
|         //   TODO: get project owners and add to response
 |             const projectsWithOwners = | ||||||
|         // }
 |                 await this.projectService.addOwnersToProjects(projects); | ||||||
|  | 
 | ||||||
|  |             this.openApiService.respondWithValidation( | ||||||
|  |                 200, | ||||||
|  |                 res, | ||||||
|  |                 projectsSchema.$id, | ||||||
|  |                 { version: 1, projects: serializeDates(projectsWithOwners) }, | ||||||
|  |             ); | ||||||
|  | 
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         this.openApiService.respondWithValidation( |         this.openApiService.respondWithValidation( | ||||||
|             200, |             200, | ||||||
|  | |||||||
| @ -19,7 +19,10 @@ const mockProjectWithCounts = (name: string) => ({ | |||||||
| 
 | 
 | ||||||
| describe('unit tests', () => { | describe('unit tests', () => { | ||||||
|     test('maps owners to projects', () => { |     test('maps owners to projects', () => { | ||||||
|         const projects = [{ name: 'project1' }, { name: 'project2' }] as any; |         const projects = [ | ||||||
|  |             { id: 'project1', name: 'Project one' }, | ||||||
|  |             { id: 'project2', name: 'Project two' }, | ||||||
|  |         ] as any; | ||||||
| 
 | 
 | ||||||
|         const owners = { |         const owners = { | ||||||
|             project1: [{ ownerType: 'user' as const, name: 'Owner Name' }], |             project1: [{ ownerType: 'user' as const, name: 'Owner Name' }], | ||||||
| @ -32,13 +35,21 @@ describe('unit tests', () => { | |||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         expect(projectsWithOwners).toMatchObject([ |         expect(projectsWithOwners).toMatchObject([ | ||||||
|             { name: 'project1', owners: [{ name: 'Owner Name' }] }, |             { | ||||||
|             { name: 'project2', owners: [{ name: 'Owner Name' }] }, |                 id: 'project1', | ||||||
|  |                 name: 'Project one', | ||||||
|  |                 owners: [{ name: 'Owner Name' }], | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |                 id: 'project2', | ||||||
|  |                 name: 'Project two', | ||||||
|  |                 owners: [{ name: 'Owner Name' }], | ||||||
|  |             }, | ||||||
|         ]); |         ]); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     test('returns "system" when a project has no owners', async () => { |     test('returns "system" when a project has no owners', async () => { | ||||||
|         const projects = [{ name: 'project1' }, { name: 'project2' }] as any; |         const projects = [{ id: 'project1' }, { id: 'project2' }] as any; | ||||||
| 
 | 
 | ||||||
|         const owners = {}; |         const owners = {}; | ||||||
| 
 | 
 | ||||||
| @ -48,8 +59,14 @@ describe('unit tests', () => { | |||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         expect(projectsWithOwners).toMatchObject([ |         expect(projectsWithOwners).toMatchObject([ | ||||||
|             { name: 'project1', owners: [{ ownerType: 'system' }] }, |             { | ||||||
|             { name: 'project2', owners: [{ ownerType: 'system' }] }, |                 id: 'project1', | ||||||
|  |                 owners: [{ ownerType: 'system' }], | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |                 id: 'project2', | ||||||
|  |                 owners: [{ ownerType: 'system' }], | ||||||
|  |             }, | ||||||
|         ]); |         ]); | ||||||
|     }); |     }); | ||||||
| }); | }); | ||||||
| @ -66,7 +83,7 @@ let group2: IGroup; | |||||||
| 
 | 
 | ||||||
| beforeAll(async () => { | beforeAll(async () => { | ||||||
|     db = await dbInit('project_owners_read_model_serial', getLogger); |     db = await dbInit('project_owners_read_model_serial', getLogger); | ||||||
|     readModel = new ProjectOwnersReadModel(db.rawDatabase, db.stores.roleStore); |     readModel = new ProjectOwnersReadModel(db.rawDatabase); | ||||||
|     ownerRoleId = (await db.stores.roleStore.getRoleByName(RoleName.OWNER)).id; |     ownerRoleId = (await db.stores.roleStore.getRoleByName(RoleName.OWNER)).id; | ||||||
| 
 | 
 | ||||||
|     const ownerData = { |     const ownerData = { | ||||||
| @ -107,14 +124,7 @@ afterAll(async () => { | |||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| afterEach(async () => { | afterEach(async () => { | ||||||
|     if (db) { |     db.stores.roleStore; | ||||||
|         const projects = await db.stores.projectStore.getAll(); |  | ||||||
|         for (const project of projects) { |  | ||||||
|             // Clean only project roles, not all roles
 |  | ||||||
|             await db.stores.roleStore.removeRolesForProject(project.id); |  | ||||||
|         } |  | ||||||
|         await db.stores.projectStore.deleteAll(); |  | ||||||
|     } |  | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| describe('integration tests', () => { | describe('integration tests', () => { | ||||||
|  | |||||||
| @ -1,5 +1,12 @@ | |||||||
| import type { Db } from '../../db/db'; | import type { Db } from '../../db/db'; | ||||||
| import { RoleName, type IProjectWithCount, type IRoleStore } from '../../types'; | import { RoleName, type IProjectWithCount } from '../../types'; | ||||||
|  | import type { | ||||||
|  |     GroupProjectOwner, | ||||||
|  |     IProjectOwnersReadModel, | ||||||
|  |     IProjectWithCountAndOwners, | ||||||
|  |     ProjectOwnersDictionary, | ||||||
|  |     UserProjectOwner, | ||||||
|  | } from './project-owners-read-model.type'; | ||||||
| 
 | 
 | ||||||
| const T = { | const T = { | ||||||
|     ROLE_USER: 'role_user', |     ROLE_USER: 'role_user', | ||||||
| @ -8,34 +15,11 @@ const T = { | |||||||
|     USERS: 'users', |     USERS: 'users', | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| type SystemOwner = { ownerType: 'system' }; | export class ProjectOwnersReadModel implements IProjectOwnersReadModel { | ||||||
| type UserProjectOwner = { |  | ||||||
|     ownerType: 'user'; |  | ||||||
|     name: string; |  | ||||||
|     email?: string; |  | ||||||
|     imageUrl?: string; |  | ||||||
| }; |  | ||||||
| type GroupProjectOwner = { |  | ||||||
|     ownerType: 'group'; |  | ||||||
|     name: string; |  | ||||||
| }; |  | ||||||
| type ProjectOwners = |  | ||||||
|     | [SystemOwner] |  | ||||||
|     | Array<UserProjectOwner | GroupProjectOwner>; |  | ||||||
| 
 |  | ||||||
| export type ProjectOwnersDictionary = Record<string, ProjectOwners>; |  | ||||||
| 
 |  | ||||||
| type IProjectWithCountAndOwners = IProjectWithCount & { |  | ||||||
|     owners: ProjectOwners; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export class ProjectOwnersReadModel { |  | ||||||
|     private db: Db; |     private db: Db; | ||||||
|     roleStore: IRoleStore; |  | ||||||
| 
 | 
 | ||||||
|     constructor(db: Db, roleStore: IRoleStore) { |     constructor(db: Db) { | ||||||
|         this.db = db; |         this.db = db; | ||||||
|         this.roleStore = roleStore; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static addOwnerData( |     static addOwnerData( | ||||||
| @ -44,7 +28,7 @@ export class ProjectOwnersReadModel { | |||||||
|     ): IProjectWithCountAndOwners[] { |     ): IProjectWithCountAndOwners[] { | ||||||
|         return projects.map((project) => ({ |         return projects.map((project) => ({ | ||||||
|             ...project, |             ...project, | ||||||
|             owners: owners[project.name] || [{ ownerType: 'system' }], |             owners: owners[project.id] || [{ ownerType: 'system' }], | ||||||
|         })); |         })); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -119,7 +103,9 @@ export class ProjectOwnersReadModel { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getAllProjectOwners(): Promise<ProjectOwnersDictionary> { |     async getAllProjectOwners(): Promise<ProjectOwnersDictionary> { | ||||||
|         const ownerRole = await this.roleStore.getRoleByName(RoleName.OWNER); |         const ownerRole = await this.db(T.ROLES) | ||||||
|  |             .where({ name: RoleName.OWNER }) | ||||||
|  |             .first(); | ||||||
|         const usersDict = await this.getAllProjectUsersByRole(ownerRole.id); |         const usersDict = await this.getAllProjectUsersByRole(ownerRole.id); | ||||||
|         const groupsDict = await this.getAllProjectGroupsByRole(ownerRole.id); |         const groupsDict = await this.getAllProjectGroupsByRole(ownerRole.id); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										28
									
								
								src/lib/features/project/project-owners-read-model.type.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/lib/features/project/project-owners-read-model.type.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | import type { IProjectWithCount } from '../../types'; | ||||||
|  | 
 | ||||||
|  | export type SystemOwner = { ownerType: 'system' }; | ||||||
|  | export type UserProjectOwner = { | ||||||
|  |     ownerType: 'user'; | ||||||
|  |     name: string; | ||||||
|  |     email?: string; | ||||||
|  |     imageUrl?: string; | ||||||
|  | }; | ||||||
|  | export type GroupProjectOwner = { | ||||||
|  |     ownerType: 'group'; | ||||||
|  |     name: string; | ||||||
|  | }; | ||||||
|  | type ProjectOwners = | ||||||
|  |     | [SystemOwner] | ||||||
|  |     | Array<UserProjectOwner | GroupProjectOwner>; | ||||||
|  | 
 | ||||||
|  | export type ProjectOwnersDictionary = Record<string, ProjectOwners>; | ||||||
|  | 
 | ||||||
|  | export type IProjectWithCountAndOwners = IProjectWithCount & { | ||||||
|  |     owners: ProjectOwners; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export interface IProjectOwnersReadModel { | ||||||
|  |     addOwners( | ||||||
|  |         projects: IProjectWithCount[], | ||||||
|  |     ): Promise<IProjectWithCountAndOwners[]>; | ||||||
|  | } | ||||||
| @ -50,6 +50,7 @@ import { | |||||||
|     RoleName, |     RoleName, | ||||||
|     SYSTEM_USER_ID, |     SYSTEM_USER_ID, | ||||||
|     type ProjectCreated, |     type ProjectCreated, | ||||||
|  |     type IProjectOwnersReadModel, | ||||||
| } from '../../types'; | } from '../../types'; | ||||||
| import type { | import type { | ||||||
|     IProjectAccessModel, |     IProjectAccessModel, | ||||||
| @ -77,8 +78,6 @@ import type { | |||||||
|     IProjectQuery, |     IProjectQuery, | ||||||
| } from './project-store-type'; | } from './project-store-type'; | ||||||
| 
 | 
 | ||||||
| const getCreatedBy = (user: IUser) => user.email || user.username || 'unknown'; |  | ||||||
| 
 |  | ||||||
| type Days = number; | type Days = number; | ||||||
| type Count = number; | type Count = number; | ||||||
| 
 | 
 | ||||||
| @ -112,6 +111,8 @@ function includes( | |||||||
| export default class ProjectService { | export default class ProjectService { | ||||||
|     private projectStore: IProjectStore; |     private projectStore: IProjectStore; | ||||||
| 
 | 
 | ||||||
|  |     private projectOwnersReadModel: IProjectOwnersReadModel; | ||||||
|  | 
 | ||||||
|     private accessService: AccessService; |     private accessService: AccessService; | ||||||
| 
 | 
 | ||||||
|     private eventStore: IEventStore; |     private eventStore: IEventStore; | ||||||
| @ -147,6 +148,7 @@ export default class ProjectService { | |||||||
|     constructor( |     constructor( | ||||||
|         { |         { | ||||||
|             projectStore, |             projectStore, | ||||||
|  |             projectOwnersReadModel, | ||||||
|             eventStore, |             eventStore, | ||||||
|             featureToggleStore, |             featureToggleStore, | ||||||
|             environmentStore, |             environmentStore, | ||||||
| @ -157,6 +159,7 @@ export default class ProjectService { | |||||||
|         }: Pick< |         }: Pick< | ||||||
|             IUnleashStores, |             IUnleashStores, | ||||||
|             | 'projectStore' |             | 'projectStore' | ||||||
|  |             | 'projectOwnersReadModel' | ||||||
|             | 'eventStore' |             | 'eventStore' | ||||||
|             | 'featureToggleStore' |             | 'featureToggleStore' | ||||||
|             | 'environmentStore' |             | 'environmentStore' | ||||||
| @ -174,6 +177,7 @@ export default class ProjectService { | |||||||
|         privateProjectChecker: IPrivateProjectChecker, |         privateProjectChecker: IPrivateProjectChecker, | ||||||
|     ) { |     ) { | ||||||
|         this.projectStore = projectStore; |         this.projectStore = projectStore; | ||||||
|  |         this.projectOwnersReadModel = projectOwnersReadModel; | ||||||
|         this.environmentStore = environmentStore; |         this.environmentStore = environmentStore; | ||||||
|         this.featureEnvironmentStore = featureEnvironmentStore; |         this.featureEnvironmentStore = featureEnvironmentStore; | ||||||
|         this.accessService = accessService; |         this.accessService = accessService; | ||||||
| @ -218,6 +222,12 @@ export default class ProjectService { | |||||||
|         return projects; |         return projects; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     async addOwnersToProjects( | ||||||
|  |         projects: IProjectWithCount[], | ||||||
|  |     ): Promise<IProjectWithCount[]> { | ||||||
|  |         return this.projectOwnersReadModel.addOwners(projects); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     async getProject(id: string): Promise<IProject> { |     async getProject(id: string): Promise<IProject> { | ||||||
|         return this.projectStore.get(id); |         return this.projectStore.get(id); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -89,6 +89,74 @@ export const projectSchema = { | |||||||
|             description: |             description: | ||||||
|                 'The average time from when a feature was created to when it was enabled in the "production" environment during the current window', |                 'The average time from when a feature was created to when it was enabled in the "production" environment during the current window', | ||||||
|         }, |         }, | ||||||
|  |         owners: { | ||||||
|  |             description: | ||||||
|  |                 'The users and/or groups that have the "owner" role in this project. If no such users or groups exist, the list will contain the "system" owner instead.', | ||||||
|  |             oneOf: [ | ||||||
|  |                 { | ||||||
|  |                     type: 'array', | ||||||
|  |                     minItems: 1, | ||||||
|  |                     items: { | ||||||
|  |                         anyOf: [ | ||||||
|  |                             { | ||||||
|  |                                 type: 'object', | ||||||
|  |                                 required: ['ownerType', 'name'], | ||||||
|  |                                 properties: { | ||||||
|  |                                     ownerType: { | ||||||
|  |                                         type: 'string', | ||||||
|  |                                         enum: ['user'], | ||||||
|  |                                     }, | ||||||
|  |                                     name: { | ||||||
|  |                                         type: 'string', | ||||||
|  |                                         example: 'User Name', | ||||||
|  |                                     }, | ||||||
|  |                                     imageUrl: { | ||||||
|  |                                         type: 'string', | ||||||
|  |                                         nullable: true, | ||||||
|  |                                         example: | ||||||
|  |                                             'https://example.com/image.jpg', | ||||||
|  |                                     }, | ||||||
|  |                                     email: { | ||||||
|  |                                         type: 'string', | ||||||
|  |                                         nullable: true, | ||||||
|  |                                         example: 'user@example.com', | ||||||
|  |                                     }, | ||||||
|  |                                 }, | ||||||
|  |                             }, | ||||||
|  |                             { | ||||||
|  |                                 type: 'object', | ||||||
|  |                                 required: ['ownerType', 'name'], | ||||||
|  |                                 properties: { | ||||||
|  |                                     ownerType: { | ||||||
|  |                                         type: 'string', | ||||||
|  |                                         enum: ['group'], | ||||||
|  |                                     }, | ||||||
|  |                                     name: { | ||||||
|  |                                         type: 'string', | ||||||
|  |                                         example: 'Group Name', | ||||||
|  |                                     }, | ||||||
|  |                                 }, | ||||||
|  |                             }, | ||||||
|  |                         ], | ||||||
|  |                     }, | ||||||
|  |                 }, | ||||||
|  |                 { | ||||||
|  |                     type: 'array', | ||||||
|  |                     minItems: 1, | ||||||
|  |                     maxItems: 1, | ||||||
|  |                     items: { | ||||||
|  |                         type: 'object', | ||||||
|  |                         required: ['ownerType'], | ||||||
|  |                         properties: { | ||||||
|  |                             ownerType: { | ||||||
|  |                                 type: 'string', | ||||||
|  |                                 enum: ['system'], | ||||||
|  |                             }, | ||||||
|  |                         }, | ||||||
|  |                     }, | ||||||
|  |                 }, | ||||||
|  |             ], | ||||||
|  |         }, | ||||||
|     }, |     }, | ||||||
|     components: {}, |     components: {}, | ||||||
| } as const; | } as const; | ||||||
|  | |||||||
| @ -40,6 +40,7 @@ import { IFeatureSearchStore } from '../features/feature-search/feature-search-s | |||||||
| import type { IInactiveUsersStore } from '../users/inactive/types/inactive-users-store-type'; | import type { IInactiveUsersStore } from '../users/inactive/types/inactive-users-store-type'; | ||||||
| import { ITrafficDataUsageStore } from '../features/traffic-data-usage/traffic-data-usage-store-type'; | import { ITrafficDataUsageStore } from '../features/traffic-data-usage/traffic-data-usage-store-type'; | ||||||
| import { ISegmentReadModel } from '../features/segment/segment-read-model-type'; | import { ISegmentReadModel } from '../features/segment/segment-read-model-type'; | ||||||
|  | import { IProjectOwnersReadModel } from '../features/project/project-owners-read-model.type'; | ||||||
| 
 | 
 | ||||||
| export interface IUnleashStores { | export interface IUnleashStores { | ||||||
|     accessStore: IAccessStore; |     accessStore: IAccessStore; | ||||||
| @ -84,6 +85,7 @@ export interface IUnleashStores { | |||||||
|     inactiveUsersStore: IInactiveUsersStore; |     inactiveUsersStore: IInactiveUsersStore; | ||||||
|     trafficDataUsageStore: ITrafficDataUsageStore; |     trafficDataUsageStore: ITrafficDataUsageStore; | ||||||
|     segmentReadModel: ISegmentReadModel; |     segmentReadModel: ISegmentReadModel; | ||||||
|  |     projectOwnersReadModel: IProjectOwnersReadModel; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export { | export { | ||||||
| @ -127,4 +129,5 @@ export { | |||||||
|     IFeatureSearchStore, |     IFeatureSearchStore, | ||||||
|     ITrafficDataUsageStore, |     ITrafficDataUsageStore, | ||||||
|     ISegmentReadModel, |     ISegmentReadModel, | ||||||
|  |     IProjectOwnersReadModel, | ||||||
| }; | }; | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								src/test/fixtures/store.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/test/fixtures/store.ts
									
									
									
									
										vendored
									
									
								
							| @ -43,6 +43,7 @@ import FakeFeatureSearchStore from '../../lib/features/feature-search/fake-featu | |||||||
| import { FakeInactiveUsersStore } from '../../lib/users/inactive/fakes/fake-inactive-users-store'; | import { FakeInactiveUsersStore } from '../../lib/users/inactive/fakes/fake-inactive-users-store'; | ||||||
| import { FakeTrafficDataUsageStore } from '../../lib/features/traffic-data-usage/fake-traffic-data-usage-store'; | import { FakeTrafficDataUsageStore } from '../../lib/features/traffic-data-usage/fake-traffic-data-usage-store'; | ||||||
| import { FakeSegmentReadModel } from '../../lib/features/segment/fake-segment-read-model'; | import { FakeSegmentReadModel } from '../../lib/features/segment/fake-segment-read-model'; | ||||||
|  | import { FakeProjectOwnersReadModel } from '../../lib/features/project/fake-project-owners-read-model'; | ||||||
| 
 | 
 | ||||||
| const db = { | const db = { | ||||||
|     select: () => ({ |     select: () => ({ | ||||||
| @ -95,6 +96,7 @@ const createStores: () => IUnleashStores = () => { | |||||||
|         inactiveUsersStore: new FakeInactiveUsersStore(), |         inactiveUsersStore: new FakeInactiveUsersStore(), | ||||||
|         trafficDataUsageStore: new FakeTrafficDataUsageStore(), |         trafficDataUsageStore: new FakeTrafficDataUsageStore(), | ||||||
|         segmentReadModel: new FakeSegmentReadModel(), |         segmentReadModel: new FakeSegmentReadModel(), | ||||||
|  |         projectOwnersReadModel: new FakeProjectOwnersReadModel(), | ||||||
|     }; |     }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user