mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-09 00:18:00 +01:00
feat: enterprise project settings (#4844)
This commit is contained in:
parent
ebb76a5354
commit
960bc110ce
@ -14,6 +14,7 @@ import {
|
|||||||
IProjectInsert,
|
IProjectInsert,
|
||||||
IProjectQuery,
|
IProjectQuery,
|
||||||
IProjectSettings,
|
IProjectSettings,
|
||||||
|
IProjectEnterpriseSettingsUpdate,
|
||||||
IProjectStore,
|
IProjectStore,
|
||||||
ProjectEnvironment,
|
ProjectEnvironment,
|
||||||
} from '../types/stores/project-store';
|
} from '../types/stores/project-store';
|
||||||
@ -244,10 +245,6 @@ class ProjectStore implements IProjectStore {
|
|||||||
project: project.id,
|
project: project.id,
|
||||||
project_mode: project.mode,
|
project_mode: project.mode,
|
||||||
default_stickiness: project.defaultStickiness,
|
default_stickiness: project.defaultStickiness,
|
||||||
feature_limit: project.featureLimit,
|
|
||||||
feature_naming_pattern: project.featureNaming?.pattern,
|
|
||||||
feature_naming_example: project.featureNaming?.example,
|
|
||||||
feature_naming_description: project.featureNaming?.description,
|
|
||||||
})
|
})
|
||||||
.returning('*');
|
.returning('*');
|
||||||
return this.mapRow({ ...row[0], ...settingsRow[0] });
|
return this.mapRow({ ...row[0], ...settingsRow[0] });
|
||||||
@ -272,9 +269,30 @@ class ProjectStore implements IProjectStore {
|
|||||||
await this.db(SETTINGS_TABLE)
|
await this.db(SETTINGS_TABLE)
|
||||||
.where({ project: data.id })
|
.where({ project: data.id })
|
||||||
.update({
|
.update({
|
||||||
project_mode: data.mode,
|
|
||||||
default_stickiness: data.defaultStickiness,
|
default_stickiness: data.defaultStickiness,
|
||||||
feature_limit: data.featureLimit,
|
feature_limit: data.featureLimit,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await this.db(SETTINGS_TABLE).insert({
|
||||||
|
project: data.id,
|
||||||
|
default_stickiness: data.defaultStickiness,
|
||||||
|
feature_limit: data.featureLimit,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.logger.error('Could not update project, error: ', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateProjectEnterpriseSettings(
|
||||||
|
data: IProjectEnterpriseSettingsUpdate,
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (await this.hasProjectSettings(data.id)) {
|
||||||
|
await this.db(SETTINGS_TABLE)
|
||||||
|
.where({ project: data.id })
|
||||||
|
.update({
|
||||||
|
project_mode: data.mode,
|
||||||
feature_naming_pattern: data.featureNaming?.pattern,
|
feature_naming_pattern: data.featureNaming?.pattern,
|
||||||
feature_naming_example: data.featureNaming?.example,
|
feature_naming_example: data.featureNaming?.example,
|
||||||
feature_naming_description:
|
feature_naming_description:
|
||||||
@ -284,15 +302,16 @@ class ProjectStore implements IProjectStore {
|
|||||||
await this.db(SETTINGS_TABLE).insert({
|
await this.db(SETTINGS_TABLE).insert({
|
||||||
project: data.id,
|
project: data.id,
|
||||||
project_mode: data.mode,
|
project_mode: data.mode,
|
||||||
default_stickiness: data.defaultStickiness,
|
|
||||||
feature_limit: data.featureLimit,
|
|
||||||
feature_naming_pattern: data.featureNaming?.pattern,
|
feature_naming_pattern: data.featureNaming?.pattern,
|
||||||
feature_naming_example: data.featureNaming?.example,
|
feature_naming_example: data.featureNaming?.example,
|
||||||
feature_naming_description: data.featureNaming?.description,
|
feature_naming_description: data.featureNaming?.description,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.error('Could not update project, error: ', err);
|
this.logger.error(
|
||||||
|
'Could not update project settings, error: ',
|
||||||
|
err,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,13 +113,16 @@ const createProjects = async (
|
|||||||
type: 'production',
|
type: 'production',
|
||||||
});
|
});
|
||||||
for (const project of projects) {
|
for (const project of projects) {
|
||||||
await db.stores.projectStore.create({
|
const storedProject = {
|
||||||
name: project,
|
name: project,
|
||||||
description: '',
|
description: '',
|
||||||
id: project,
|
id: project,
|
||||||
mode: 'open' as const,
|
mode: 'open' as const,
|
||||||
featureLimit,
|
featureLimit,
|
||||||
});
|
};
|
||||||
|
await db.stores.projectStore.create(storedProject);
|
||||||
|
await db.stores.projectStore.update(storedProject);
|
||||||
|
|
||||||
await app.linkProjectToEnvironment(project, DEFAULT_ENV);
|
await app.linkProjectToEnvironment(project, DEFAULT_ENV);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -884,10 +887,8 @@ test('validate import data', async () => {
|
|||||||
|
|
||||||
// note: this must be done after creating the feature on the earlier lines,
|
// note: this must be done after creating the feature on the earlier lines,
|
||||||
// to prevent the pattern from blocking the creation.
|
// to prevent the pattern from blocking the creation.
|
||||||
await projectStore.update({
|
await projectStore.updateProjectEnterpriseSettings({
|
||||||
id: DEFAULT_PROJECT,
|
id: DEFAULT_PROJECT,
|
||||||
name: 'default',
|
|
||||||
description: '',
|
|
||||||
mode: 'open',
|
mode: 'open',
|
||||||
featureNaming: { pattern: 'testpattern.+' },
|
featureNaming: { pattern: 'testpattern.+' },
|
||||||
});
|
});
|
||||||
@ -996,6 +997,9 @@ test(`should give errors with flag names if the flags don't match the project pa
|
|||||||
description: '',
|
description: '',
|
||||||
id: project,
|
id: project,
|
||||||
mode: 'open' as const,
|
mode: 'open' as const,
|
||||||
|
});
|
||||||
|
await db.stores.projectStore.updateProjectEnterpriseSettings({
|
||||||
|
id: project,
|
||||||
featureNaming: { pattern },
|
featureNaming: { pattern },
|
||||||
});
|
});
|
||||||
await app.linkProjectToEnvironment(project, DEFAULT_ENV);
|
await app.linkProjectToEnvironment(project, DEFAULT_ENV);
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`updateProjectEnterpriseSettings schema 1`] = `
|
||||||
|
{
|
||||||
|
"errors": [
|
||||||
|
{
|
||||||
|
"instancePath": "",
|
||||||
|
"keyword": "required",
|
||||||
|
"message": "must have required property 'version'",
|
||||||
|
"params": {
|
||||||
|
"missingProperty": "version",
|
||||||
|
},
|
||||||
|
"schemaPath": "#/required",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"schema": "#/components/schemas/projectOverviewSchema",
|
||||||
|
}
|
||||||
|
`;
|
22
src/lib/openapi/spec/project-overview-schema.test.ts
Normal file
22
src/lib/openapi/spec/project-overview-schema.test.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { ProjectOverviewSchema } from './project-overview-schema';
|
||||||
|
import { validateSchema } from '../validate';
|
||||||
|
|
||||||
|
test('updateProjectEnterpriseSettings schema', () => {
|
||||||
|
const data: ProjectOverviewSchema = {
|
||||||
|
name: 'project',
|
||||||
|
version: 3,
|
||||||
|
featureNaming: {
|
||||||
|
description: 'naming description',
|
||||||
|
example: 'a',
|
||||||
|
pattern: '[aZ]',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
validateSchema('#/components/schemas/projectOverviewSchema', data),
|
||||||
|
).toBeUndefined();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
validateSchema('#/components/schemas/projectOverviewSchema', {}),
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
@ -41,7 +41,11 @@ import {
|
|||||||
IFeatureNaming,
|
IFeatureNaming,
|
||||||
CreateProject,
|
CreateProject,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { IProjectQuery, IProjectStore } from '../types/stores/project-store';
|
import {
|
||||||
|
IProjectQuery,
|
||||||
|
IProjectEnterpriseSettingsUpdate,
|
||||||
|
IProjectStore,
|
||||||
|
} from '../types/stores/project-store';
|
||||||
import {
|
import {
|
||||||
IProjectAccessModel,
|
IProjectAccessModel,
|
||||||
IRoleDescriptor,
|
IRoleDescriptor,
|
||||||
@ -83,7 +87,7 @@ interface ICalculateStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class ProjectService {
|
export default class ProjectService {
|
||||||
private store: IProjectStore;
|
private projectStore: IProjectStore;
|
||||||
|
|
||||||
private accessService: AccessService;
|
private accessService: AccessService;
|
||||||
|
|
||||||
@ -113,6 +117,8 @@ export default class ProjectService {
|
|||||||
|
|
||||||
private flagResolver: IFlagResolver;
|
private flagResolver: IFlagResolver;
|
||||||
|
|
||||||
|
private isEnterprise: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{
|
{
|
||||||
projectStore,
|
projectStore,
|
||||||
@ -141,7 +147,7 @@ export default class ProjectService {
|
|||||||
favoriteService: FavoritesService,
|
favoriteService: FavoritesService,
|
||||||
privateProjectChecker: IPrivateProjectChecker,
|
privateProjectChecker: IPrivateProjectChecker,
|
||||||
) {
|
) {
|
||||||
this.store = projectStore;
|
this.projectStore = projectStore;
|
||||||
this.environmentStore = environmentStore;
|
this.environmentStore = environmentStore;
|
||||||
this.featureEnvironmentStore = featureEnvironmentStore;
|
this.featureEnvironmentStore = featureEnvironmentStore;
|
||||||
this.accessService = accessService;
|
this.accessService = accessService;
|
||||||
@ -156,13 +162,17 @@ export default class ProjectService {
|
|||||||
this.projectStatsStore = projectStatsStore;
|
this.projectStatsStore = projectStatsStore;
|
||||||
this.logger = config.getLogger('services/project-service.js');
|
this.logger = config.getLogger('services/project-service.js');
|
||||||
this.flagResolver = config.flagResolver;
|
this.flagResolver = config.flagResolver;
|
||||||
|
this.isEnterprise = config.isEnterprise;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getProjects(
|
async getProjects(
|
||||||
query?: IProjectQuery,
|
query?: IProjectQuery,
|
||||||
userId?: number,
|
userId?: number,
|
||||||
): Promise<IProjectWithCount[]> {
|
): Promise<IProjectWithCount[]> {
|
||||||
const projects = await this.store.getProjectsWithCounts(query, userId);
|
const projects = await this.projectStore.getProjectsWithCounts(
|
||||||
|
query,
|
||||||
|
userId,
|
||||||
|
);
|
||||||
if (this.flagResolver.isEnabled('privateProjects') && userId) {
|
if (this.flagResolver.isEnabled('privateProjects') && userId) {
|
||||||
const projectAccess =
|
const projectAccess =
|
||||||
await this.privateProjectChecker.getUserAccessibleProjects(
|
await this.privateProjectChecker.getUserAccessibleProjects(
|
||||||
@ -181,7 +191,7 @@ export default class ProjectService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getProject(id: string): Promise<IProject> {
|
async getProject(id: string): Promise<IProject> {
|
||||||
return this.store.get(id);
|
return this.projectStore.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private validateAndProcessFeatureNamingPattern = (
|
private validateAndProcessFeatureNamingPattern = (
|
||||||
@ -214,14 +224,11 @@ export default class ProjectService {
|
|||||||
newProject: CreateProject,
|
newProject: CreateProject,
|
||||||
user: IUser,
|
user: IUser,
|
||||||
): Promise<IProject> {
|
): Promise<IProject> {
|
||||||
const data = await projectSchema.validateAsync(newProject);
|
const validatedData = await projectSchema.validateAsync(newProject);
|
||||||
|
const data = this.removeModeForNonEnterprise(validatedData);
|
||||||
await this.validateUniqueId(data.id);
|
await this.validateUniqueId(data.id);
|
||||||
|
|
||||||
if (data.featureNaming) {
|
await this.projectStore.create(data);
|
||||||
this.validateAndProcessFeatureNamingPattern(data.featureNaming);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.store.create(data);
|
|
||||||
|
|
||||||
const enabledEnvironments = await this.environmentStore.getAll({
|
const enabledEnvironments = await this.environmentStore.getAll({
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@ -250,7 +257,24 @@ export default class ProjectService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async updateProject(updatedProject: IProject, user: User): Promise<void> {
|
async updateProject(updatedProject: IProject, user: User): Promise<void> {
|
||||||
const preData = await this.store.get(updatedProject.id);
|
const preData = await this.projectStore.get(updatedProject.id);
|
||||||
|
|
||||||
|
await this.projectStore.update(updatedProject);
|
||||||
|
|
||||||
|
await this.eventStore.store({
|
||||||
|
type: PROJECT_UPDATED,
|
||||||
|
project: updatedProject.id,
|
||||||
|
createdBy: getCreatedBy(user),
|
||||||
|
data: updatedProject,
|
||||||
|
preData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateProjectEnterpriseSettings(
|
||||||
|
updatedProject: IProjectEnterpriseSettingsUpdate,
|
||||||
|
user: User,
|
||||||
|
): Promise<void> {
|
||||||
|
const preData = await this.projectStore.get(updatedProject.id);
|
||||||
|
|
||||||
if (updatedProject.featureNaming) {
|
if (updatedProject.featureNaming) {
|
||||||
this.validateAndProcessFeatureNamingPattern(
|
this.validateAndProcessFeatureNamingPattern(
|
||||||
@ -258,7 +282,7 @@ export default class ProjectService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.store.update(updatedProject);
|
await this.projectStore.updateProjectEnterpriseSettings(updatedProject);
|
||||||
|
|
||||||
await this.eventStore.store({
|
await this.eventStore.store({
|
||||||
type: PROJECT_UPDATED,
|
type: PROJECT_UPDATED,
|
||||||
@ -276,7 +300,7 @@ export default class ProjectService {
|
|||||||
const featureEnvs = await this.featureEnvironmentStore.getAll({
|
const featureEnvs = await this.featureEnvironmentStore.getAll({
|
||||||
feature_name: feature.name,
|
feature_name: feature.name,
|
||||||
});
|
});
|
||||||
const newEnvs = await this.store.getEnvironmentsForProject(
|
const newEnvs = await this.projectStore.getEnvironmentsForProject(
|
||||||
newProjectId,
|
newProjectId,
|
||||||
);
|
);
|
||||||
return arraysHaveSameItems(
|
return arraysHaveSameItems(
|
||||||
@ -289,7 +313,7 @@ export default class ProjectService {
|
|||||||
project: string,
|
project: string,
|
||||||
environment: string,
|
environment: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.store.addEnvironmentToProject(project, environment);
|
await this.projectStore.addEnvironmentToProject(project, environment);
|
||||||
}
|
}
|
||||||
|
|
||||||
async changeProject(
|
async changeProject(
|
||||||
@ -355,7 +379,7 @@ export default class ProjectService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.store.delete(id);
|
await this.projectStore.delete(id);
|
||||||
|
|
||||||
await this.eventStore.store({
|
await this.eventStore.store({
|
||||||
type: PROJECT_DELETED,
|
type: PROJECT_DELETED,
|
||||||
@ -373,7 +397,7 @@ export default class ProjectService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async validateUniqueId(id: string): Promise<void> {
|
async validateUniqueId(id: string): Promise<void> {
|
||||||
const exists = await this.store.hasProject(id);
|
const exists = await this.projectStore.hasProject(id);
|
||||||
if (exists) {
|
if (exists) {
|
||||||
throw new NameExistsError('A project with this id already exists.');
|
throw new NameExistsError('A project with this id already exists.');
|
||||||
}
|
}
|
||||||
@ -872,7 +896,7 @@ export default class ProjectService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getMembers(projectId: string): Promise<number> {
|
async getMembers(projectId: string): Promise<number> {
|
||||||
return this.store.getMembersCountByProject(projectId);
|
return this.projectStore.getMembersCountByProject(projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getProjectUsers(
|
async getProjectUsers(
|
||||||
@ -903,7 +927,7 @@ export default class ProjectService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getProjectsByUser(userId: number): Promise<string[]> {
|
async getProjectsByUser(userId: number): Promise<string[]> {
|
||||||
return this.store.getProjectsByUser(userId);
|
return this.projectStore.getProjectsByUser(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getProjectRoleUsage(roleId: number): Promise<IProjectRoleUsage[]> {
|
async getProjectRoleUsage(roleId: number): Promise<IProjectRoleUsage[]> {
|
||||||
@ -911,7 +935,7 @@ export default class ProjectService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async statusJob(): Promise<void> {
|
async statusJob(): Promise<void> {
|
||||||
const projects = await this.store.getAll();
|
const projects = await this.projectStore.getAll();
|
||||||
|
|
||||||
const statusUpdates = await Promise.all(
|
const statusUpdates = await Promise.all(
|
||||||
projects.map((project) => this.getStatusUpdates(project.id)),
|
projects.map((project) => this.getStatusUpdates(project.id)),
|
||||||
@ -990,7 +1014,7 @@ export default class ProjectService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const projectMembersAddedCurrentWindow =
|
const projectMembersAddedCurrentWindow =
|
||||||
await this.store.getMembersCountByProjectAfterDate(
|
await this.projectStore.getMembersCountByProjectAfterDate(
|
||||||
projectId,
|
projectId,
|
||||||
dateMinusThirtyDays,
|
dateMinusThirtyDays,
|
||||||
);
|
);
|
||||||
@ -1023,14 +1047,14 @@ export default class ProjectService {
|
|||||||
favorite,
|
favorite,
|
||||||
projectStats,
|
projectStats,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.store.get(projectId),
|
this.projectStore.get(projectId),
|
||||||
this.store.getEnvironmentsForProject(projectId),
|
this.projectStore.getEnvironmentsForProject(projectId),
|
||||||
this.featureToggleService.getFeatureOverview({
|
this.featureToggleService.getFeatureOverview({
|
||||||
projectId,
|
projectId,
|
||||||
archived,
|
archived,
|
||||||
userId,
|
userId,
|
||||||
}),
|
}),
|
||||||
this.store.getMembersCountByProject(projectId),
|
this.projectStore.getMembersCountByProject(projectId),
|
||||||
userId
|
userId
|
||||||
? this.favoritesService.isFavoriteProject({
|
? this.favoritesService.isFavoriteProject({
|
||||||
project: projectId,
|
project: projectId,
|
||||||
@ -1058,4 +1082,13 @@ export default class ProjectService {
|
|||||||
version: 1,
|
version: 1,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
|
removeModeForNonEnterprise(data): any {
|
||||||
|
if (this.isEnterprise) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
const { mode, ...proData } = data;
|
||||||
|
return proData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -420,7 +420,7 @@ export type CreateProject = Pick<IProject, 'id' | 'name'> & {
|
|||||||
export interface IProject {
|
export interface IProject {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description?: string;
|
||||||
health?: number;
|
health?: number;
|
||||||
createdAt?: Date;
|
createdAt?: Date;
|
||||||
updatedAt?: Date;
|
updatedAt?: Date;
|
||||||
|
@ -16,14 +16,20 @@ import { CreateFeatureStrategySchema } from '../../openapi';
|
|||||||
export interface IProjectInsert {
|
export interface IProjectInsert {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description?: string;
|
||||||
updatedAt?: Date;
|
updatedAt?: Date;
|
||||||
changeRequestsEnabled?: boolean;
|
changeRequestsEnabled?: boolean;
|
||||||
mode: ProjectMode;
|
mode?: ProjectMode;
|
||||||
featureLimit?: number;
|
featureLimit?: number;
|
||||||
featureNaming?: IFeatureNaming;
|
featureNaming?: IFeatureNaming;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IProjectEnterpriseSettingsUpdate {
|
||||||
|
id: string;
|
||||||
|
mode?: ProjectMode;
|
||||||
|
featureNaming?: IFeatureNaming;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IProjectSettings {
|
export interface IProjectSettings {
|
||||||
mode: ProjectMode;
|
mode: ProjectMode;
|
||||||
defaultStickiness: string;
|
defaultStickiness: string;
|
||||||
@ -57,6 +63,10 @@ export interface IProjectStore extends Store<IProject, string> {
|
|||||||
|
|
||||||
update(update: IProjectInsert): Promise<void>;
|
update(update: IProjectInsert): Promise<void>;
|
||||||
|
|
||||||
|
updateProjectEnterpriseSettings(
|
||||||
|
update: IProjectEnterpriseSettingsUpdate,
|
||||||
|
): Promise<void>;
|
||||||
|
|
||||||
importProjects(
|
importProjects(
|
||||||
projects: IProjectInsert[],
|
projects: IProjectInsert[],
|
||||||
environments?: IEnvironment[],
|
environments?: IEnvironment[],
|
||||||
|
@ -9,7 +9,12 @@ import {
|
|||||||
} from '../../../lib/services';
|
} from '../../../lib/services';
|
||||||
import { FeatureStrategySchema } from '../../../lib/openapi';
|
import { FeatureStrategySchema } from '../../../lib/openapi';
|
||||||
import User from '../../../lib/types/user';
|
import User from '../../../lib/types/user';
|
||||||
import { IConstraint, IVariant, SKIP_CHANGE_REQUEST } from '../../../lib/types';
|
import {
|
||||||
|
IConstraint,
|
||||||
|
IUnleashStores,
|
||||||
|
IVariant,
|
||||||
|
SKIP_CHANGE_REQUEST,
|
||||||
|
} from '../../../lib/types';
|
||||||
import EnvironmentService from '../../../lib/services/environment-service';
|
import EnvironmentService from '../../../lib/services/environment-service';
|
||||||
import {
|
import {
|
||||||
ForbiddenError,
|
ForbiddenError,
|
||||||
@ -20,7 +25,7 @@ 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';
|
import { createPrivateProjectChecker } from '../../../lib/features/private-project/createPrivateProjectChecker';
|
||||||
|
|
||||||
let stores;
|
let stores: IUnleashStores;
|
||||||
let db;
|
let db;
|
||||||
let service: FeatureToggleService;
|
let service: FeatureToggleService;
|
||||||
let segmentService: ISegmentService;
|
let segmentService: ISegmentService;
|
||||||
@ -677,10 +682,13 @@ describe('flag name validation', () => {
|
|||||||
name: projectId,
|
name: projectId,
|
||||||
mode: 'open' as const,
|
mode: 'open' as const,
|
||||||
defaultStickiness: 'default',
|
defaultStickiness: 'default',
|
||||||
featureNaming,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
await stores.projectStore.create(project);
|
await stores.projectStore.create(project);
|
||||||
|
await stores.projectStore.updateProjectEnterpriseSettings({
|
||||||
|
id: projectId,
|
||||||
|
featureNaming,
|
||||||
|
});
|
||||||
|
|
||||||
const validFeatures = ['testpattern-feature', 'testpattern-feature2'];
|
const validFeatures = ['testpattern-feature', 'testpattern-feature2'];
|
||||||
const invalidFeatures = ['a', 'b', 'c'];
|
const invalidFeatures = ['a', 'b', 'c'];
|
||||||
|
@ -135,7 +135,6 @@ test('should create new project', async () => {
|
|||||||
id: 'test',
|
id: 'test',
|
||||||
name: 'New project',
|
name: 'New project',
|
||||||
description: 'Blah',
|
description: 'Blah',
|
||||||
mode: 'protected' as const,
|
|
||||||
defaultStickiness: 'default',
|
defaultStickiness: 'default',
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -145,7 +144,6 @@ test('should create new project', async () => {
|
|||||||
expect(project.name).toEqual(ret.name);
|
expect(project.name).toEqual(ret.name);
|
||||||
expect(project.description).toEqual(ret.description);
|
expect(project.description).toEqual(ret.description);
|
||||||
expect(ret.createdAt).toBeTruthy();
|
expect(ret.createdAt).toBeTruthy();
|
||||||
expect(ret.mode).toEqual('protected');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should create new private project', async () => {
|
test('should create new private project', async () => {
|
||||||
@ -153,7 +151,6 @@ test('should create new private project', async () => {
|
|||||||
id: 'testPrivate',
|
id: 'testPrivate',
|
||||||
name: 'New private project',
|
name: 'New private project',
|
||||||
description: 'Blah',
|
description: 'Blah',
|
||||||
mode: 'private' as const,
|
|
||||||
defaultStickiness: 'default',
|
defaultStickiness: 'default',
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -163,7 +160,6 @@ test('should create new private project', async () => {
|
|||||||
expect(project.name).toEqual(ret.name);
|
expect(project.name).toEqual(ret.name);
|
||||||
expect(project.description).toEqual(ret.description);
|
expect(project.description).toEqual(ret.description);
|
||||||
expect(ret.createdAt).toBeTruthy();
|
expect(ret.createdAt).toBeTruthy();
|
||||||
expect(ret.mode).toEqual('private');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should delete project', async () => {
|
test('should delete project', async () => {
|
||||||
@ -1829,12 +1825,14 @@ describe('feature flag naming patterns', () => {
|
|||||||
|
|
||||||
await projectService.createProject(project, user.id);
|
await projectService.createProject(project, user.id);
|
||||||
|
|
||||||
|
await projectService.updateProjectEnterpriseSettings(project, user);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
(await projectService.getProject(project.id)).featureNaming,
|
(await projectService.getProject(project.id)).featureNaming,
|
||||||
).toMatchObject(featureNaming);
|
).toMatchObject(featureNaming);
|
||||||
|
|
||||||
const newPattern = 'new-pattern.+';
|
const newPattern = 'new-pattern.+';
|
||||||
await projectService.updateProject(
|
await projectService.updateProjectEnterpriseSettings(
|
||||||
{
|
{
|
||||||
...project,
|
...project,
|
||||||
featureNaming: { pattern: newPattern },
|
featureNaming: { pattern: newPattern },
|
||||||
|
5
src/test/fixtures/fake-project-store.ts
vendored
5
src/test/fixtures/fake-project-store.ts
vendored
@ -190,4 +190,9 @@ export default class FakeProjectStore implements IProjectStore {
|
|||||||
getProjectModeCounts(): Promise<ProjectModeCount[]> {
|
getProjectModeCounts(): Promise<ProjectModeCount[]> {
|
||||||
return Promise.resolve([]);
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
updateProjectEnterpriseSettings(update: IProjectInsert): Promise<void> {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user