mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
chore: add automatic ID generation algorithm (#7001)
This PR adds a function to automatically generate a project ID on
creation. Using this when the id is missing will be handled in following
PRs.
The function uses the existing `slug` package to create a slug, and then
takes the 12 characters of a uuidv4 string to generate an ID.
The included tests check that the 12 character hash is added and that
the resulting string is url friendly (by checking that
`encodeURIComponent` doesn't change it).
We could also test a lot of edge cases (such as dealing with double
spaces, trimming the string, etc), but I think that's better handled by
the library itself (but you can check out what I removed in
2d9bcb6390
for an idea).
The function doesn't really need to be in the service; it could be moved to a util. But for proximity, I'll create it here first.
This commit is contained in:
parent
6477ccf34b
commit
861ae6aa93
@ -184,6 +184,7 @@
|
||||
"@types/owasp-password-strength-test": "1.3.2",
|
||||
"@types/pg": "8.11.5",
|
||||
"@types/semver": "7.5.8",
|
||||
"@types/slug": "^5.0.8",
|
||||
"@types/stoppable": "1.1.3",
|
||||
"@types/supertest": "6.0.2",
|
||||
"@types/type-is": "1.6.6",
|
||||
@ -202,6 +203,7 @@
|
||||
"nock": "13.5.4",
|
||||
"openapi-enforcer": "1.23.0",
|
||||
"proxyquire": "2.1.3",
|
||||
"slug": "^9.0.0",
|
||||
"source-map-support": "0.5.21",
|
||||
"superagent": "9.0.2",
|
||||
"supertest": "6.3.4",
|
||||
|
@ -1,3 +1,4 @@
|
||||
import fc from 'fast-check';
|
||||
import { createTestConfig } from '../../../test/config/test-config';
|
||||
import { BadDataError } from '../../error';
|
||||
import { type IBaseEvent, RoleName, TEST_AUDIT_USER } from '../../types';
|
||||
@ -301,3 +302,29 @@ describe('enterprise extension: enable change requests', () => {
|
||||
).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}$/);
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { subDays } from 'date-fns';
|
||||
import { ValidationError } from 'joi';
|
||||
import slug from 'slug';
|
||||
import type { IAuditUser, IUser } from '../../types/user';
|
||||
import type {
|
||||
AccessService,
|
||||
@ -60,7 +61,7 @@ import type {
|
||||
import type FeatureToggleService from '../feature-toggle/feature-toggle-service';
|
||||
import IncompatibleProjectError from '../../error/incompatible-project-error';
|
||||
import ProjectWithoutOwnerError from '../../error/project-without-owner-error';
|
||||
import { arraysHaveSameItems } from '../../util';
|
||||
import { arraysHaveSameItems, randomId } from '../../util';
|
||||
import type { GroupService } from '../../services/group-service';
|
||||
import type { IGroupRole } from '../../types/group';
|
||||
import type { FavoritesService } from '../../services/favorites-service';
|
||||
@ -294,6 +295,13 @@ export default class ProjectService {
|
||||
}
|
||||
}
|
||||
|
||||
generateProjectId(name: string): string {
|
||||
const urlFriendly = slug(name);
|
||||
const tail = randomId().slice(-12);
|
||||
const id = `${urlFriendly}-${tail}`;
|
||||
return id;
|
||||
}
|
||||
|
||||
async createProject(
|
||||
newProject: CreateProject,
|
||||
user: IUser,
|
||||
|
41
yarn.lock
41
yarn.lock
@ -1668,6 +1668,11 @@
|
||||
"@types/mime" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/slug@^5.0.8":
|
||||
version "5.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/slug/-/slug-5.0.8.tgz#76d40f033a8a99793f3dcbfb3f187ab9014f9d47"
|
||||
integrity sha512-mblTWR1OST257k1gZ3QvqG+ERSr8Ea6dyM1FH6Jtm4jeXi0/r0/95VNctofuiywPxCVQuE8AuFoqmvJ4iVUlXQ==
|
||||
|
||||
"@types/ssri@*":
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/ssri/-/ssri-7.1.1.tgz#2a2c94abf0d3a8c3b07bb4ff08142dd571407bb5"
|
||||
@ -6598,6 +6603,11 @@ slice-ansi@^7.0.0:
|
||||
ansi-styles "^6.2.1"
|
||||
is-fullwidth-code-point "^5.0.0"
|
||||
|
||||
slug@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/slug/-/slug-9.0.0.tgz#68f968a79ce5156c6606b7b2e233ed0ffab94bdf"
|
||||
integrity sha512-ixytnHlpHPWM56heaGgYe/M8tDAcpJcsg/zBuyElbFDOORzMGOeP3Te6iJBRVYu3WQEiWLQPb70Gh9ig/sZgGQ==
|
||||
|
||||
slugify@~1.4.7:
|
||||
version "1.4.7"
|
||||
resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.4.7.tgz#e42359d505afd84a44513280868e31202a79a628"
|
||||
@ -6793,16 +6803,7 @@ string-length@^4.0.1:
|
||||
char-regex "^1.0.2"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
@ -6841,14 +6842,7 @@ string_decoder@~1.1.1:
|
||||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
@ -7475,7 +7469,7 @@ wordwrap@>=0.0.2:
|
||||
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
|
||||
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==
|
||||
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
@ -7493,15 +7487,6 @@ wrap-ansi@^6.2.0:
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
|
||||
|
Loading…
Reference in New Issue
Block a user