1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

chore: change generated project id format to use incrementing numbers instead of hashes (#7456)

This commit is contained in:
Thomas Heartman 2024-06-27 09:21:09 +02:00 committed by GitHub
parent 9202365c97
commit a9a87bc84d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 21 additions and 53 deletions

View File

@ -2650,10 +2650,10 @@ describe('automatic ID generation for create project', () => {
auditUser, auditUser,
); );
expect(project.id).toMatch(/^new-name-/); expect(project.id).toBe('new-name');
}); });
test('two projects with the same name get different ids', async () => { test('projects with the same name get ids with incrementing counters', async () => {
const createProject = async () => const createProject = async () =>
projectService.createProject( projectService.createProject(
{ name: 'some name' }, { name: 'some name' },
@ -2663,10 +2663,11 @@ describe('automatic ID generation for create project', () => {
const project1 = await createProject(); const project1 = await createProject();
const project2 = await createProject(); const project2 = await createProject();
const project3 = await createProject();
expect(project1.id).toMatch(/^some-name-/); expect(project1.id).toBe('some-name');
expect(project2.id).toMatch(/^some-name-/); expect(project2.id).toBe('some-name-1');
expect(project1.id).not.toBe(project2.id); expect(project3.id).toBe('some-name-2');
}); });
test.each(['', undefined, ' '])( test.each(['', undefined, ' '])(
@ -2679,7 +2680,7 @@ describe('automatic ID generation for create project', () => {
auditUser, auditUser,
); );
expect(project.id).toMatch(new RegExp(`^${name}-`)); expect(project.id).toBe(name);
}, },
); );

View File

@ -1,4 +1,3 @@
import fc from 'fast-check';
import { createTestConfig } from '../../../test/config/test-config'; import { createTestConfig } from '../../../test/config/test-config';
import { BadDataError } from '../../error'; import { BadDataError } from '../../error';
import { type IBaseEvent, RoleName, TEST_AUDIT_USER } from '../../types'; import { type IBaseEvent, RoleName, TEST_AUDIT_USER } from '../../types';
@ -302,29 +301,3 @@ describe('enterprise extension: enable change requests', () => {
).resolves.toBeTruthy(); ).resolves.toBeTruthy();
}); });
}); });
describe('project ID generation', () => {
const createService = () => {
const config = createTestConfig();
const service = createFakeProjectService(config);
return service;
};
test('the name that comes out is always url friendly', () => {
const service = createService();
fc.assert(
fc.property(fc.string(), (name) => {
const projectId = service.generateProjectId(name);
expect(projectId).toMatch(encodeURIComponent(projectId));
}),
);
});
test('it adds a 12 character hex to the end of the id', () => {
const service = createService();
const projectId = service.generateProjectId('one');
expect(projectId).toMatch(/^one-[a-f0-9]{12}$/);
});
});

View File

@ -1,6 +1,6 @@
import { subDays } from 'date-fns'; import { subDays } from 'date-fns';
import { ValidationError } from 'joi'; import { ValidationError } from 'joi';
import slug from 'slug'; import createSlug from 'slug';
import type { IAuditUser, IUser } from '../../types/user'; import type { IAuditUser, IUser } from '../../types/user';
import type { import type {
AccessService, AccessService,
@ -62,7 +62,7 @@ import type {
import type FeatureToggleService from '../feature-toggle/feature-toggle-service'; import type FeatureToggleService from '../feature-toggle/feature-toggle-service';
import IncompatibleProjectError from '../../error/incompatible-project-error'; import IncompatibleProjectError from '../../error/incompatible-project-error';
import ProjectWithoutOwnerError from '../../error/project-without-owner-error'; import ProjectWithoutOwnerError from '../../error/project-without-owner-error';
import { arraysHaveSameItems, randomId } from '../../util'; import { arraysHaveSameItems } from '../../util';
import type { GroupService } from '../../services/group-service'; import type { GroupService } from '../../services/group-service';
import type { IGroupRole } from '../../types/group'; import type { IGroupRole } from '../../types/group';
import type { FavoritesService } from '../../services/favorites-service'; import type { FavoritesService } from '../../services/favorites-service';
@ -304,21 +304,17 @@ export default class ProjectService {
await this.validateEnvironmentsExist(environments); await this.validateEnvironmentsExist(environments);
} }
} }
async generateProjectId(name: string): Promise<string> {
generateProjectId(name: string): string { const generateUniqueId = async (name: string, suffix?: number) => {
const urlFriendly = slug(name); const slug = createSlug(name);
const tail = randomId().slice(-12); const id = suffix ? `${slug}-${suffix}` : slug;
const id = `${urlFriendly}-${tail}`; if (await this.projectStore.hasProject(id)) {
return id; return await generateUniqueId(name, (suffix ?? 0) + 1);
} } else {
return id;
async generateUniqueProjectId(name: string): Promise<string> { }
const id = this.generateProjectId(name); };
if (await this.projectStore.hasProject(id)) { return generateUniqueId(name);
return await this.generateUniqueProjectId(name);
} else {
return id;
}
} }
async createProject( async createProject(
@ -337,9 +333,7 @@ export default class ProjectService {
await this.validateProjectEnvironments(newProject.environments); await this.validateProjectEnvironments(newProject.environments);
if (!newProject.id?.trim()) { if (!newProject.id?.trim()) {
newProject.id = await this.generateUniqueProjectId( newProject.id = await this.generateProjectId(newProject.name);
newProject.name,
);
return await projectSchema.validateAsync(newProject); return await projectSchema.validateAsync(newProject);
} else { } else {
const validatedData = const validatedData =