mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-02 01:17:58 +02: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