mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-31 00:16:47 +01:00
feat: walking skeleton of private projects (#4753)
This commit is contained in:
parent
387f48617d
commit
15baea1d25
@ -29,6 +29,7 @@ import maintenanceMiddleware from './middleware/maintenance-middleware';
|
|||||||
import { unless } from './middleware/unless-middleware';
|
import { unless } from './middleware/unless-middleware';
|
||||||
import { catchAllErrorHandler } from './middleware/catch-all-error-handler';
|
import { catchAllErrorHandler } from './middleware/catch-all-error-handler';
|
||||||
import NotFoundError from './error/notfound-error';
|
import NotFoundError from './error/notfound-error';
|
||||||
|
import privateProjectMiddleware from './features/private-project/privateProjectMiddleware';
|
||||||
|
|
||||||
export default async function getApp(
|
export default async function getApp(
|
||||||
config: IUnleashConfig,
|
config: IUnleashConfig,
|
||||||
@ -157,6 +158,8 @@ export default async function getApp(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.use(baseUriPath, privateProjectMiddleware(config, services));
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
baseUriPath,
|
baseUriPath,
|
||||||
rbacMiddleware(config, stores, services.accessService),
|
rbacMiddleware(config, stores, services.accessService),
|
||||||
|
@ -36,6 +36,7 @@ import { AccountStore } from './account-store';
|
|||||||
import ProjectStatsStore from './project-stats-store';
|
import ProjectStatsStore from './project-stats-store';
|
||||||
import { Db } from './db';
|
import { Db } from './db';
|
||||||
import { ImportTogglesStore } from '../features/export-import-toggles/import-toggles-store';
|
import { ImportTogglesStore } from '../features/export-import-toggles/import-toggles-store';
|
||||||
|
import PrivateProjectStore from '../features/private-project/privateProjectStore';
|
||||||
|
|
||||||
export const createStores = (
|
export const createStores = (
|
||||||
config: IUnleashConfig,
|
config: IUnleashConfig,
|
||||||
@ -128,6 +129,7 @@ export const createStores = (
|
|||||||
),
|
),
|
||||||
projectStatsStore: new ProjectStatsStore(db, eventBus, getLogger),
|
projectStatsStore: new ProjectStatsStore(db, eventBus, getLogger),
|
||||||
importTogglesStore: new ImportTogglesStore(db),
|
importTogglesStore: new ImportTogglesStore(db),
|
||||||
|
privateProjectStore: new PrivateProjectStore(db, getLogger),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,6 +41,10 @@ import {
|
|||||||
} from '../segment/createSegmentService';
|
} from '../segment/createSegmentService';
|
||||||
import StrategyStore from '../../db/strategy-store';
|
import StrategyStore from '../../db/strategy-store';
|
||||||
import FakeStrategiesStore from '../../../test/fixtures/fake-strategies-store';
|
import FakeStrategiesStore from '../../../test/fixtures/fake-strategies-store';
|
||||||
|
import {
|
||||||
|
createFakeprivateProjectChecker,
|
||||||
|
createPrivateProjectChecker,
|
||||||
|
} from '../private-project/createPrivateProjectChecker';
|
||||||
|
|
||||||
export const createFeatureToggleService = (
|
export const createFeatureToggleService = (
|
||||||
db: Db,
|
db: Db,
|
||||||
@ -98,6 +102,9 @@ export const createFeatureToggleService = (
|
|||||||
db,
|
db,
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const privateProjectChecker = createPrivateProjectChecker(db, config);
|
||||||
|
|
||||||
const featureToggleService = new FeatureToggleService(
|
const featureToggleService = new FeatureToggleService(
|
||||||
{
|
{
|
||||||
featureStrategiesStore,
|
featureStrategiesStore,
|
||||||
@ -114,6 +121,7 @@ export const createFeatureToggleService = (
|
|||||||
segmentService,
|
segmentService,
|
||||||
accessService,
|
accessService,
|
||||||
changeRequestAccessReadModel,
|
changeRequestAccessReadModel,
|
||||||
|
privateProjectChecker,
|
||||||
);
|
);
|
||||||
return featureToggleService;
|
return featureToggleService;
|
||||||
};
|
};
|
||||||
@ -147,6 +155,7 @@ export const createFakeFeatureToggleService = (
|
|||||||
);
|
);
|
||||||
const segmentService = createFakeSegmentService(config);
|
const segmentService = createFakeSegmentService(config);
|
||||||
const changeRequestAccessReadModel = createFakeChangeRequestAccessService();
|
const changeRequestAccessReadModel = createFakeChangeRequestAccessService();
|
||||||
|
const fakeprivateProjectChecker = createFakeprivateProjectChecker();
|
||||||
const featureToggleService = new FeatureToggleService(
|
const featureToggleService = new FeatureToggleService(
|
||||||
{
|
{
|
||||||
featureStrategiesStore,
|
featureStrategiesStore,
|
||||||
@ -163,6 +172,7 @@ export const createFakeFeatureToggleService = (
|
|||||||
segmentService,
|
segmentService,
|
||||||
accessService,
|
accessService,
|
||||||
changeRequestAccessReadModel,
|
changeRequestAccessReadModel,
|
||||||
|
fakeprivateProjectChecker,
|
||||||
);
|
);
|
||||||
return featureToggleService;
|
return featureToggleService;
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
import { Db, IUnleashConfig } from 'lib/server-impl';
|
||||||
|
import PrivateProjectStore from './privateProjectStore';
|
||||||
|
import { PrivateProjectChecker } from './privateProjectChecker';
|
||||||
|
import { FakeprivateProjectChecker } from './fakePrivateProjectChecker';
|
||||||
|
|
||||||
|
export const createPrivateProjectChecker = (
|
||||||
|
db: Db,
|
||||||
|
config: IUnleashConfig,
|
||||||
|
): PrivateProjectChecker => {
|
||||||
|
const { getLogger } = config;
|
||||||
|
const privateProjectStore = new PrivateProjectStore(db, getLogger);
|
||||||
|
|
||||||
|
return new PrivateProjectChecker({
|
||||||
|
privateProjectStore: privateProjectStore,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createFakeprivateProjectChecker =
|
||||||
|
(): FakeprivateProjectChecker => {
|
||||||
|
return new FakeprivateProjectChecker();
|
||||||
|
};
|
@ -0,0 +1,8 @@
|
|||||||
|
import { IPrivateProjectChecker } from './privateProjectCheckerType';
|
||||||
|
|
||||||
|
export class FakeprivateProjectChecker implements IPrivateProjectChecker {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
async getUserAccessibleProjects(userId: number): Promise<string[]> {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
}
|
17
src/lib/features/private-project/privateProjectChecker.ts
Normal file
17
src/lib/features/private-project/privateProjectChecker.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { IUnleashStores } from '../../types';
|
||||||
|
import { IPrivateProjectStore } from './privateProjectStoreType';
|
||||||
|
import { IPrivateProjectChecker } from './privateProjectCheckerType';
|
||||||
|
|
||||||
|
export class PrivateProjectChecker implements IPrivateProjectChecker {
|
||||||
|
private privateProjectStore: IPrivateProjectStore;
|
||||||
|
|
||||||
|
constructor({
|
||||||
|
privateProjectStore,
|
||||||
|
}: Pick<IUnleashStores, 'privateProjectStore'>) {
|
||||||
|
this.privateProjectStore = privateProjectStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserAccessibleProjects(userId: number): Promise<string[]> {
|
||||||
|
return this.privateProjectStore.getUserAccessibleProjects(userId);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
export interface IPrivateProjectChecker {
|
||||||
|
getUserAccessibleProjects(userId: number): Promise<string[]>;
|
||||||
|
}
|
40
src/lib/features/private-project/privateProjectMiddleware.ts
Normal file
40
src/lib/features/private-project/privateProjectMiddleware.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { IUnleashConfig, IUnleashServices } from '../../types';
|
||||||
|
import { findParam } from '../../middleware';
|
||||||
|
import { NextFunction, Response } from 'express';
|
||||||
|
|
||||||
|
const privateProjectMiddleware = (
|
||||||
|
{
|
||||||
|
getLogger,
|
||||||
|
flagResolver,
|
||||||
|
}: Pick<IUnleashConfig, 'getLogger' | 'flagResolver'>,
|
||||||
|
{ projectService, accessService }: IUnleashServices,
|
||||||
|
): any => {
|
||||||
|
const logger = getLogger('/middleware/project-middleware.ts');
|
||||||
|
logger.debug('Enabling private project middleware');
|
||||||
|
|
||||||
|
if (!flagResolver.isEnabled('privateProjects')) {
|
||||||
|
return (req, res, next) => next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return async (req, res: Response, next: NextFunction) => {
|
||||||
|
req.checkPrivateProjectPermissions = async () => {
|
||||||
|
const { user } = req;
|
||||||
|
|
||||||
|
let projectId =
|
||||||
|
findParam('projectId', req) || findParam('project', req);
|
||||||
|
|
||||||
|
if (projectId === undefined) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const permissions = await accessService.getPermissionsForUser(user);
|
||||||
|
|
||||||
|
return (
|
||||||
|
permissions.map((p) => p.permission).includes('ADMIN') ||
|
||||||
|
projectService.isProjectUser(user.id, projectId)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default privateProjectMiddleware;
|
42
src/lib/features/private-project/privateProjectStore.ts
Normal file
42
src/lib/features/private-project/privateProjectStore.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { Db } from '../../db/db';
|
||||||
|
import { Logger, LogProvider } from '../../logger';
|
||||||
|
import { IPrivateProjectStore } from './privateProjectStoreType';
|
||||||
|
|
||||||
|
class PrivateProjectStore implements IPrivateProjectStore {
|
||||||
|
private db: Db;
|
||||||
|
|
||||||
|
private logger: Logger;
|
||||||
|
|
||||||
|
constructor(db: Db, getLogger: LogProvider) {
|
||||||
|
this.db = db;
|
||||||
|
this.logger = getLogger('project-permission-store.ts');
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(): void {}
|
||||||
|
|
||||||
|
async getUserAccessibleProjects(userId: number): Promise<string[]> {
|
||||||
|
const projects = await this.db
|
||||||
|
.from((db) => {
|
||||||
|
db.select('project')
|
||||||
|
.from('role_user')
|
||||||
|
.leftJoin('roles', 'role_user.role_id', 'roles.id')
|
||||||
|
.where('user_id', userId)
|
||||||
|
.union((queryBuilder) => {
|
||||||
|
queryBuilder
|
||||||
|
.select('project')
|
||||||
|
.from('group_role')
|
||||||
|
.leftJoin(
|
||||||
|
'group_user',
|
||||||
|
'group_user.group_id',
|
||||||
|
'group_role.group_id',
|
||||||
|
)
|
||||||
|
.where('user_id', userId);
|
||||||
|
})
|
||||||
|
.as('query');
|
||||||
|
})
|
||||||
|
.pluck('project');
|
||||||
|
return projects;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PrivateProjectStore;
|
@ -0,0 +1,3 @@
|
|||||||
|
export interface IPrivateProjectStore {
|
||||||
|
getUserAccessibleProjects(userId: number): Promise<string[]>;
|
||||||
|
}
|
@ -7,6 +7,7 @@ import {
|
|||||||
import { IUnleashConfig } from '../types/option';
|
import { IUnleashConfig } from '../types/option';
|
||||||
import { IUnleashStores } from '../types/stores';
|
import { IUnleashStores } from '../types/stores';
|
||||||
import User from '../types/user';
|
import User from '../types/user';
|
||||||
|
import { Request } from 'express';
|
||||||
|
|
||||||
interface PermissionChecker {
|
interface PermissionChecker {
|
||||||
hasPermission(
|
hasPermission(
|
||||||
@ -17,9 +18,9 @@ interface PermissionChecker {
|
|||||||
): Promise<boolean>;
|
): Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function findParam(
|
export function findParam(
|
||||||
name: string,
|
name: string,
|
||||||
{ params, body }: any,
|
{ params, body }: Request,
|
||||||
defaultValue?: string,
|
defaultValue?: string,
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
let found = params ? params[name] : undefined;
|
let found = params ? params[name] : undefined;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { IRouter, Router, Request, Response, RequestHandler } from 'express';
|
import { IRouter, Router, Request, Response, RequestHandler } from 'express';
|
||||||
import { Logger } from 'lib/logger';
|
import { Logger } from 'lib/logger';
|
||||||
import { IUnleashConfig } from '../types/option';
|
import { IUnleashConfig, NONE } from '../types';
|
||||||
import { NONE } from '../types/permissions';
|
|
||||||
import { handleErrors } from './util';
|
import { handleErrors } from './util';
|
||||||
import requireContentType from '../middleware/content_type_checker';
|
import requireContentType from '../middleware/content_type_checker';
|
||||||
import { PermissionError } from '../error';
|
import { PermissionError } from '../error';
|
||||||
@ -55,6 +54,17 @@ const checkPermission =
|
|||||||
return res.status(403).json(new PermissionError(permissions)).end();
|
return res.status(403).json(new PermissionError(permissions)).end();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const checkPrivateProjectPermissions = () => async (req, res, next) => {
|
||||||
|
if (
|
||||||
|
!req.checkPrivateProjectPermissions ||
|
||||||
|
(await req.checkPrivateProjectPermissions())
|
||||||
|
) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(404).end();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for Controllers to standardize binding to express Router.
|
* Base class for Controllers to standardize binding to express Router.
|
||||||
*
|
*
|
||||||
@ -100,6 +110,7 @@ export default class Controller {
|
|||||||
this.app[options.method](
|
this.app[options.method](
|
||||||
options.path,
|
options.path,
|
||||||
checkPermission(options.permission),
|
checkPermission(options.permission),
|
||||||
|
checkPrivateProjectPermissions(),
|
||||||
this.useContentTypeMiddleware(options),
|
this.useContentTypeMiddleware(options),
|
||||||
this.useRouteErrorHandler(options.handler.bind(this)),
|
this.useRouteErrorHandler(options.handler.bind(this)),
|
||||||
);
|
);
|
||||||
@ -186,6 +197,7 @@ export default class Controller {
|
|||||||
this.app.post(
|
this.app.post(
|
||||||
path,
|
path,
|
||||||
checkPermission(permission),
|
checkPermission(permission),
|
||||||
|
checkPrivateProjectPermissions(),
|
||||||
filehandler.bind(this),
|
filehandler.bind(this),
|
||||||
this.useRouteErrorHandler(handler.bind(this)),
|
this.useRouteErrorHandler(handler.bind(this)),
|
||||||
);
|
);
|
||||||
|
@ -9,6 +9,7 @@ import FeatureToggleService from './feature-toggle-service';
|
|||||||
import { AccessService } from './access-service';
|
import { AccessService } from './access-service';
|
||||||
import { IChangeRequestAccessReadModel } from 'lib/features/change-request-access-service/change-request-access-read-model';
|
import { IChangeRequestAccessReadModel } from 'lib/features/change-request-access-service/change-request-access-read-model';
|
||||||
import { ISegmentService } from 'lib/segments/segment-service-interface';
|
import { ISegmentService } from 'lib/segments/segment-service-interface';
|
||||||
|
import { IPrivateProjectChecker } from '../features/private-project/privateProjectCheckerType';
|
||||||
|
|
||||||
test('Should only store events for potentially stale on', async () => {
|
test('Should only store events for potentially stale on', async () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
@ -49,6 +50,7 @@ test('Should only store events for potentially stale on', async () => {
|
|||||||
{} as ISegmentService,
|
{} as ISegmentService,
|
||||||
{} as AccessService,
|
{} as AccessService,
|
||||||
{} as IChangeRequestAccessReadModel,
|
{} as IChangeRequestAccessReadModel,
|
||||||
|
{} as IPrivateProjectChecker,
|
||||||
);
|
);
|
||||||
|
|
||||||
await featureToggleService.updatePotentiallyStaleFeatures();
|
await featureToggleService.updatePotentiallyStaleFeatures();
|
||||||
|
@ -95,6 +95,7 @@ import { unique } from '../util/unique';
|
|||||||
import { ISegmentService } from 'lib/segments/segment-service-interface';
|
import { ISegmentService } from 'lib/segments/segment-service-interface';
|
||||||
import { IChangeRequestAccessReadModel } from '../features/change-request-access-service/change-request-access-read-model';
|
import { IChangeRequestAccessReadModel } from '../features/change-request-access-service/change-request-access-read-model';
|
||||||
import { checkFeatureFlagNamesAgainstPattern } from '../features/feature-naming-pattern/feature-naming-validation';
|
import { checkFeatureFlagNamesAgainstPattern } from '../features/feature-naming-pattern/feature-naming-validation';
|
||||||
|
import { IPrivateProjectChecker } from '../features/private-project/privateProjectCheckerType';
|
||||||
|
|
||||||
interface IFeatureContext {
|
interface IFeatureContext {
|
||||||
featureName: string;
|
featureName: string;
|
||||||
@ -154,6 +155,8 @@ class FeatureToggleService {
|
|||||||
|
|
||||||
private changeRequestAccessReadModel: IChangeRequestAccessReadModel;
|
private changeRequestAccessReadModel: IChangeRequestAccessReadModel;
|
||||||
|
|
||||||
|
private privateProjectChecker: IPrivateProjectChecker;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{
|
{
|
||||||
featureStrategiesStore,
|
featureStrategiesStore,
|
||||||
@ -184,6 +187,7 @@ class FeatureToggleService {
|
|||||||
segmentService: ISegmentService,
|
segmentService: ISegmentService,
|
||||||
accessService: AccessService,
|
accessService: AccessService,
|
||||||
changeRequestAccessReadModel: IChangeRequestAccessReadModel,
|
changeRequestAccessReadModel: IChangeRequestAccessReadModel,
|
||||||
|
privateProjectChecker: IPrivateProjectChecker,
|
||||||
) {
|
) {
|
||||||
this.logger = getLogger('services/feature-toggle-service.ts');
|
this.logger = getLogger('services/feature-toggle-service.ts');
|
||||||
this.featureStrategiesStore = featureStrategiesStore;
|
this.featureStrategiesStore = featureStrategiesStore;
|
||||||
@ -199,6 +203,7 @@ class FeatureToggleService {
|
|||||||
this.accessService = accessService;
|
this.accessService = accessService;
|
||||||
this.flagResolver = flagResolver;
|
this.flagResolver = flagResolver;
|
||||||
this.changeRequestAccessReadModel = changeRequestAccessReadModel;
|
this.changeRequestAccessReadModel = changeRequestAccessReadModel;
|
||||||
|
this.privateProjectChecker = privateProjectChecker;
|
||||||
}
|
}
|
||||||
|
|
||||||
async validateFeaturesContext(
|
async validateFeaturesContext(
|
||||||
@ -1017,11 +1022,20 @@ class FeatureToggleService {
|
|||||||
userId?: number,
|
userId?: number,
|
||||||
archived: boolean = false,
|
archived: boolean = false,
|
||||||
): Promise<FeatureToggle[]> {
|
): Promise<FeatureToggle[]> {
|
||||||
return this.featureToggleClientStore.getAdmin({
|
const features = await this.featureToggleClientStore.getAdmin({
|
||||||
featureQuery: query,
|
featureQuery: query,
|
||||||
userId,
|
userId,
|
||||||
archived,
|
archived,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.flagResolver.isEnabled('privateProjects') && userId) {
|
||||||
|
const projects =
|
||||||
|
await this.privateProjectChecker.getUserAccessibleProjects(
|
||||||
|
userId,
|
||||||
|
);
|
||||||
|
return features.filter((f) => projects.includes(f.project));
|
||||||
|
}
|
||||||
|
return features;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFeatureOverview(
|
async getFeatureOverview(
|
||||||
|
@ -60,6 +60,10 @@ import ConfigurationRevisionService from '../features/feature-toggle/configurati
|
|||||||
import { createFeatureToggleService } from '../features';
|
import { createFeatureToggleService } from '../features';
|
||||||
import EventAnnouncerService from './event-announcer-service';
|
import EventAnnouncerService from './event-announcer-service';
|
||||||
import { createGroupService } from '../features/group/createGroupService';
|
import { createGroupService } from '../features/group/createGroupService';
|
||||||
|
import {
|
||||||
|
createFakeprivateProjectChecker,
|
||||||
|
createPrivateProjectChecker,
|
||||||
|
} from '../features/private-project/createPrivateProjectChecker';
|
||||||
|
|
||||||
// TODO: will be moved to scheduler feature directory
|
// TODO: will be moved to scheduler feature directory
|
||||||
export const scheduleServices = async (
|
export const scheduleServices = async (
|
||||||
@ -184,12 +188,16 @@ export const createServices = (
|
|||||||
changeRequestAccessReadModel,
|
changeRequestAccessReadModel,
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
|
const privateProjectChecker = db
|
||||||
|
? createPrivateProjectChecker(db, config)
|
||||||
|
: createFakeprivateProjectChecker();
|
||||||
const featureToggleServiceV2 = new FeatureToggleService(
|
const featureToggleServiceV2 = new FeatureToggleService(
|
||||||
stores,
|
stores,
|
||||||
config,
|
config,
|
||||||
segmentService,
|
segmentService,
|
||||||
accessService,
|
accessService,
|
||||||
changeRequestAccessReadModel,
|
changeRequestAccessReadModel,
|
||||||
|
privateProjectChecker,
|
||||||
);
|
);
|
||||||
const environmentService = new EnvironmentService(stores, config);
|
const environmentService = new EnvironmentService(stores, config);
|
||||||
const featureTagService = new FeatureTagService(stores, config);
|
const featureTagService = new FeatureTagService(stores, config);
|
||||||
|
@ -33,6 +33,7 @@ import { IFavoriteProjectsStore } from './stores/favorite-projects';
|
|||||||
import { IAccountStore } from './stores/account-store';
|
import { IAccountStore } from './stores/account-store';
|
||||||
import { IProjectStatsStore } from './stores/project-stats-store-type';
|
import { IProjectStatsStore } from './stores/project-stats-store-type';
|
||||||
import { IImportTogglesStore } from '../features/export-import-toggles/import-toggles-store-type';
|
import { IImportTogglesStore } from '../features/export-import-toggles/import-toggles-store-type';
|
||||||
|
import { IPrivateProjectStore } from '../features/private-project/privateProjectStoreType';
|
||||||
|
|
||||||
export interface IUnleashStores {
|
export interface IUnleashStores {
|
||||||
accessStore: IAccessStore;
|
accessStore: IAccessStore;
|
||||||
@ -70,6 +71,7 @@ export interface IUnleashStores {
|
|||||||
favoriteProjectsStore: IFavoriteProjectsStore;
|
favoriteProjectsStore: IFavoriteProjectsStore;
|
||||||
projectStatsStore: IProjectStatsStore;
|
projectStatsStore: IProjectStatsStore;
|
||||||
importTogglesStore: IImportTogglesStore;
|
importTogglesStore: IImportTogglesStore;
|
||||||
|
privateProjectStore: IPrivateProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@ -107,4 +109,5 @@ export {
|
|||||||
IFavoriteFeaturesStore,
|
IFavoriteFeaturesStore,
|
||||||
IFavoriteProjectsStore,
|
IFavoriteProjectsStore,
|
||||||
IImportTogglesStore,
|
IImportTogglesStore,
|
||||||
|
IPrivateProjectStore,
|
||||||
};
|
};
|
||||||
|
@ -43,7 +43,7 @@ process.nextTick(async () => {
|
|||||||
featureNamingPattern: true,
|
featureNamingPattern: true,
|
||||||
doraMetrics: true,
|
doraMetrics: true,
|
||||||
variantTypeNumber: true,
|
variantTypeNumber: true,
|
||||||
privateProjects: true,
|
privateProjects: false,
|
||||||
accessOverview: true,
|
accessOverview: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -21,6 +21,7 @@ import { SegmentService } from '../../../lib/services/segment-service';
|
|||||||
import { GroupService } from '../../../lib/services/group-service';
|
import { GroupService } from '../../../lib/services/group-service';
|
||||||
import { FavoritesService } from '../../../lib/services';
|
import { FavoritesService } from '../../../lib/services';
|
||||||
import { ChangeRequestAccessReadModel } from '../../../lib/features/change-request-access-service/sql-change-request-access-read-model';
|
import { ChangeRequestAccessReadModel } from '../../../lib/features/change-request-access-service/sql-change-request-access-read-model';
|
||||||
|
import { createPrivateProjectChecker } from '../../../lib/features/private-project/createPrivateProjectChecker';
|
||||||
|
|
||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
let stores: IUnleashStores;
|
let stores: IUnleashStores;
|
||||||
@ -244,12 +245,17 @@ beforeAll(async () => {
|
|||||||
db.rawDatabase,
|
db.rawDatabase,
|
||||||
accessService,
|
accessService,
|
||||||
);
|
);
|
||||||
|
const privateProjectChecker = createPrivateProjectChecker(
|
||||||
|
db.rawDatabase,
|
||||||
|
config,
|
||||||
|
);
|
||||||
featureToggleService = new FeatureToggleService(
|
featureToggleService = new FeatureToggleService(
|
||||||
stores,
|
stores,
|
||||||
config,
|
config,
|
||||||
new SegmentService(stores, changeRequestAccessReadModel, config),
|
new SegmentService(stores, changeRequestAccessReadModel, config),
|
||||||
accessService,
|
accessService,
|
||||||
changeRequestAccessReadModel,
|
changeRequestAccessReadModel,
|
||||||
|
privateProjectChecker,
|
||||||
);
|
);
|
||||||
favoritesService = new FavoritesService(stores, config);
|
favoritesService = new FavoritesService(stores, config);
|
||||||
projectService = new ProjectService(
|
projectService = new ProjectService(
|
||||||
|
@ -12,6 +12,7 @@ import { SegmentService } from '../../../lib/services/segment-service';
|
|||||||
import { GroupService } from '../../../lib/services/group-service';
|
import { GroupService } from '../../../lib/services/group-service';
|
||||||
import { FavoritesService } from '../../../lib/services';
|
import { FavoritesService } from '../../../lib/services';
|
||||||
import { ChangeRequestAccessReadModel } from '../../../lib/features/change-request-access-service/sql-change-request-access-read-model';
|
import { ChangeRequestAccessReadModel } from '../../../lib/features/change-request-access-service/sql-change-request-access-read-model';
|
||||||
|
import { createPrivateProjectChecker } from '../../../lib/features/private-project/createPrivateProjectChecker';
|
||||||
|
|
||||||
let db;
|
let db;
|
||||||
let stores;
|
let stores;
|
||||||
@ -31,12 +32,17 @@ beforeAll(async () => {
|
|||||||
db.rawDatabase,
|
db.rawDatabase,
|
||||||
accessService,
|
accessService,
|
||||||
);
|
);
|
||||||
|
const privateProjectChecker = createPrivateProjectChecker(
|
||||||
|
db.rawDatabase,
|
||||||
|
config,
|
||||||
|
);
|
||||||
const featureToggleService = new FeatureToggleService(
|
const featureToggleService = new FeatureToggleService(
|
||||||
stores,
|
stores,
|
||||||
config,
|
config,
|
||||||
new SegmentService(stores, changeRequestAccessReadModel, config),
|
new SegmentService(stores, changeRequestAccessReadModel, config),
|
||||||
accessService,
|
accessService,
|
||||||
changeRequestAccessReadModel,
|
changeRequestAccessReadModel,
|
||||||
|
privateProjectChecker,
|
||||||
);
|
);
|
||||||
const project = {
|
const project = {
|
||||||
id: 'test-project',
|
id: 'test-project',
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
} from '../../../lib/error';
|
} from '../../../lib/error';
|
||||||
import { ISegmentService } from '../../../lib/segments/segment-service-interface';
|
import { ISegmentService } from '../../../lib/segments/segment-service-interface';
|
||||||
import { ChangeRequestAccessReadModel } from '../../../lib/features/change-request-access-service/sql-change-request-access-read-model';
|
import { ChangeRequestAccessReadModel } from '../../../lib/features/change-request-access-service/sql-change-request-access-read-model';
|
||||||
|
import { createPrivateProjectChecker } from '../../../lib/features/private-project/createPrivateProjectChecker';
|
||||||
|
|
||||||
let stores;
|
let stores;
|
||||||
let db;
|
let db;
|
||||||
@ -58,12 +59,17 @@ beforeAll(async () => {
|
|||||||
changeRequestAccessReadModel,
|
changeRequestAccessReadModel,
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
|
const privateProjectChecker = createPrivateProjectChecker(
|
||||||
|
db.rawDatabase,
|
||||||
|
config,
|
||||||
|
);
|
||||||
service = new FeatureToggleService(
|
service = new FeatureToggleService(
|
||||||
stores,
|
stores,
|
||||||
config,
|
config,
|
||||||
segmentService,
|
segmentService,
|
||||||
accessService,
|
accessService,
|
||||||
changeRequestAccessReadModel,
|
changeRequestAccessReadModel,
|
||||||
|
privateProjectChecker,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -449,6 +455,10 @@ test('If change requests are enabled, cannot change variants without going via C
|
|||||||
db.rawDatabase,
|
db.rawDatabase,
|
||||||
accessService,
|
accessService,
|
||||||
);
|
);
|
||||||
|
const privateProjectChecker = createPrivateProjectChecker(
|
||||||
|
db.rawDatabase,
|
||||||
|
unleashConfig,
|
||||||
|
);
|
||||||
// Force all feature flags on to make sure we have Change requests on
|
// Force all feature flags on to make sure we have Change requests on
|
||||||
const customFeatureService = new FeatureToggleService(
|
const customFeatureService = new FeatureToggleService(
|
||||||
stores,
|
stores,
|
||||||
@ -461,6 +471,7 @@ test('If change requests are enabled, cannot change variants without going via C
|
|||||||
segmentService,
|
segmentService,
|
||||||
accessService,
|
accessService,
|
||||||
changeRequestAccessReadModel,
|
changeRequestAccessReadModel,
|
||||||
|
privateProjectChecker,
|
||||||
);
|
);
|
||||||
|
|
||||||
const newVariant: IVariant = {
|
const newVariant: IVariant = {
|
||||||
@ -532,6 +543,10 @@ test('If CRs are protected for any environment in the project stops bulk update
|
|||||||
db.rawDatabase,
|
db.rawDatabase,
|
||||||
accessService,
|
accessService,
|
||||||
);
|
);
|
||||||
|
const privateProjectChecker = createPrivateProjectChecker(
|
||||||
|
db.rawDatabase,
|
||||||
|
unleashConfig,
|
||||||
|
);
|
||||||
// Force all feature flags on to make sure we have Change requests on
|
// Force all feature flags on to make sure we have Change requests on
|
||||||
const customFeatureService = new FeatureToggleService(
|
const customFeatureService = new FeatureToggleService(
|
||||||
stores,
|
stores,
|
||||||
@ -544,6 +559,7 @@ test('If CRs are protected for any environment in the project stops bulk update
|
|||||||
segmentService,
|
segmentService,
|
||||||
accessService,
|
accessService,
|
||||||
changeRequestAccessReadModel,
|
changeRequestAccessReadModel,
|
||||||
|
privateProjectChecker,
|
||||||
);
|
);
|
||||||
|
|
||||||
const toggle = await service.createFeatureToggle(
|
const toggle = await service.createFeatureToggle(
|
||||||
|
@ -25,6 +25,7 @@ import { GroupService } from '../../../lib/services/group-service';
|
|||||||
import { AccessService } from '../../../lib/services/access-service';
|
import { AccessService } from '../../../lib/services/access-service';
|
||||||
import { ISegmentService } from '../../../lib/segments/segment-service-interface';
|
import { ISegmentService } from '../../../lib/segments/segment-service-interface';
|
||||||
import { ChangeRequestAccessReadModel } from '../../../lib/features/change-request-access-service/sql-change-request-access-read-model';
|
import { ChangeRequestAccessReadModel } from '../../../lib/features/change-request-access-service/sql-change-request-access-read-model';
|
||||||
|
import { createPrivateProjectChecker } from '../../../lib/features/private-project/createPrivateProjectChecker';
|
||||||
|
|
||||||
let stores: IUnleashStores;
|
let stores: IUnleashStores;
|
||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
@ -47,12 +48,17 @@ beforeAll(async () => {
|
|||||||
changeRequestAccessReadModel,
|
changeRequestAccessReadModel,
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
|
const privateProjectChecker = createPrivateProjectChecker(
|
||||||
|
db.rawDatabase,
|
||||||
|
config,
|
||||||
|
);
|
||||||
featureToggleService = new FeatureToggleService(
|
featureToggleService = new FeatureToggleService(
|
||||||
stores,
|
stores,
|
||||||
config,
|
config,
|
||||||
segmentService,
|
segmentService,
|
||||||
accessService,
|
accessService,
|
||||||
changeRequestAccessReadModel,
|
changeRequestAccessReadModel,
|
||||||
|
privateProjectChecker,
|
||||||
);
|
);
|
||||||
service = new PlaygroundService(config, {
|
service = new PlaygroundService(config, {
|
||||||
featureToggleServiceV2: featureToggleService,
|
featureToggleServiceV2: featureToggleService,
|
||||||
|
@ -11,6 +11,7 @@ import { SegmentService } from '../../../lib/services/segment-service';
|
|||||||
import { GroupService } from '../../../lib/services/group-service';
|
import { GroupService } from '../../../lib/services/group-service';
|
||||||
import { FavoritesService } from '../../../lib/services';
|
import { FavoritesService } from '../../../lib/services';
|
||||||
import { ChangeRequestAccessReadModel } from '../../../lib/features/change-request-access-service/sql-change-request-access-read-model';
|
import { ChangeRequestAccessReadModel } from '../../../lib/features/change-request-access-service/sql-change-request-access-read-model';
|
||||||
|
import { createPrivateProjectChecker } from '../../../lib/features/private-project/createPrivateProjectChecker';
|
||||||
|
|
||||||
let stores: IUnleashStores;
|
let stores: IUnleashStores;
|
||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
@ -36,12 +37,17 @@ beforeAll(async () => {
|
|||||||
db.rawDatabase,
|
db.rawDatabase,
|
||||||
accessService,
|
accessService,
|
||||||
);
|
);
|
||||||
|
const privateProjectChecker = createPrivateProjectChecker(
|
||||||
|
db.rawDatabase,
|
||||||
|
config,
|
||||||
|
);
|
||||||
featureToggleService = new FeatureToggleService(
|
featureToggleService = new FeatureToggleService(
|
||||||
stores,
|
stores,
|
||||||
config,
|
config,
|
||||||
new SegmentService(stores, changeRequestAccessReadModel, config),
|
new SegmentService(stores, changeRequestAccessReadModel, config),
|
||||||
accessService,
|
accessService,
|
||||||
changeRequestAccessReadModel,
|
changeRequestAccessReadModel,
|
||||||
|
privateProjectChecker,
|
||||||
);
|
);
|
||||||
favoritesService = new FavoritesService(stores, config);
|
favoritesService = new FavoritesService(stores, config);
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import { FavoritesService } from '../../../lib/services';
|
|||||||
import { FeatureEnvironmentEvent } from '../../../lib/types/events';
|
import { FeatureEnvironmentEvent } from '../../../lib/types/events';
|
||||||
import { addDays, subDays } from 'date-fns';
|
import { addDays, subDays } from 'date-fns';
|
||||||
import { ChangeRequestAccessReadModel } from '../../../lib/features/change-request-access-service/sql-change-request-access-read-model';
|
import { ChangeRequestAccessReadModel } from '../../../lib/features/change-request-access-service/sql-change-request-access-read-model';
|
||||||
|
import { createPrivateProjectChecker } from '../../../lib/features/private-project/createPrivateProjectChecker';
|
||||||
|
|
||||||
let stores;
|
let stores;
|
||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
@ -57,12 +58,17 @@ beforeAll(async () => {
|
|||||||
db.rawDatabase,
|
db.rawDatabase,
|
||||||
accessService,
|
accessService,
|
||||||
);
|
);
|
||||||
|
const privateProjectChecker = createPrivateProjectChecker(
|
||||||
|
db.rawDatabase,
|
||||||
|
config,
|
||||||
|
);
|
||||||
featureToggleService = new FeatureToggleService(
|
featureToggleService = new FeatureToggleService(
|
||||||
stores,
|
stores,
|
||||||
config,
|
config,
|
||||||
new SegmentService(stores, changeRequestAccessReadModel, config),
|
new SegmentService(stores, changeRequestAccessReadModel, config),
|
||||||
accessService,
|
accessService,
|
||||||
changeRequestAccessReadModel,
|
changeRequestAccessReadModel,
|
||||||
|
privateProjectChecker,
|
||||||
);
|
);
|
||||||
|
|
||||||
favoritesService = new FavoritesService(stores, config);
|
favoritesService = new FavoritesService(stores, config);
|
||||||
|
7
src/test/fixtures/store.ts
vendored
7
src/test/fixtures/store.ts
vendored
@ -15,7 +15,11 @@ import FakeUserFeedbackStore from './fake-user-feedback-store';
|
|||||||
import FakeFeatureTagStore from './fake-feature-tag-store';
|
import FakeFeatureTagStore from './fake-feature-tag-store';
|
||||||
import FakeEnvironmentStore from './fake-environment-store';
|
import FakeEnvironmentStore from './fake-environment-store';
|
||||||
import FakeStrategiesStore from './fake-strategies-store';
|
import FakeStrategiesStore from './fake-strategies-store';
|
||||||
import { IImportTogglesStore, IUnleashStores } from '../../lib/types';
|
import {
|
||||||
|
IImportTogglesStore,
|
||||||
|
IPrivateProjectStore,
|
||||||
|
IUnleashStores,
|
||||||
|
} from '../../lib/types';
|
||||||
import FakeSessionStore from './fake-session-store';
|
import FakeSessionStore from './fake-session-store';
|
||||||
import FakeFeatureEnvironmentStore from './fake-feature-environment-store';
|
import FakeFeatureEnvironmentStore from './fake-feature-environment-store';
|
||||||
import FakeApiTokenStore from './fake-api-token-store';
|
import FakeApiTokenStore from './fake-api-token-store';
|
||||||
@ -78,6 +82,7 @@ const createStores: () => IUnleashStores = () => {
|
|||||||
favoriteProjectsStore: new FakeFavoriteProjectsStore(),
|
favoriteProjectsStore: new FakeFavoriteProjectsStore(),
|
||||||
projectStatsStore: new FakeProjectStatsStore(),
|
projectStatsStore: new FakeProjectStatsStore(),
|
||||||
importTogglesStore: {} as IImportTogglesStore,
|
importTogglesStore: {} as IImportTogglesStore,
|
||||||
|
privateProjectStore: {} as IPrivateProjectStore,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user