Skip to content

Commit 2d4a887

Browse files
committed
[orgs] Persist slug
1 parent 5348a8b commit 2d4a887

File tree

4 files changed

+39
-7
lines changed

4 files changed

+39
-7
lines changed

components/gitpod-db/src/typeorm/team-db-impl.ts

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7+
import { list as blocklist } from "the-big-username-blacklist";
78
import { Team, TeamMemberInfo, TeamMemberRole, TeamMembershipInvite, User } from "@gitpod/gitpod-protocol";
89
import { inject, injectable } from "inversify";
910
import { TypeORM } from "./typeorm";
1011
import { Repository } from "typeorm";
1112
import { v4 as uuidv4 } from "uuid";
13+
import { randomBytes } from "crypto";
1214
import { TeamDB } from "../team-db";
1315
import { DBTeam } from "./entity/db-team";
1416
import { DBTeamMembership } from "./entity/db-team-membership";
@@ -121,17 +123,37 @@ export class TeamDBImpl implements TeamDB {
121123
return soleOwnedTeams;
122124
}
123125

124-
public async updateTeam(teamId: string, team: Pick<Team, "name">): Promise<Team> {
126+
public async updateTeam(teamId: string, team: Pick<Team, "name" | "slug">): Promise<Team> {
127+
const name = team.name && team.name.trim();
128+
const slug = team.slug && team.slug.trim();
129+
if (!name && !slug) {
130+
throw new ResponseError(ErrorCodes.BAD_REQUEST, "No update provided");
131+
}
132+
125133
const teamRepo = await this.getTeamRepo();
126134
const existingTeam = await teamRepo.findOne({ id: teamId, deleted: false, markedDeleted: false });
127135
if (!existingTeam) {
128136
throw new ResponseError(ErrorCodes.NOT_FOUND, "Organization not found");
129137
}
130-
const name = team.name && team.name.trim();
131-
if (!name || name.length === 0 || name.length > 32) {
132-
throw new ResponseError(ErrorCodes.INVALID_VALUE, "The name must be between 1 and 32 characters long");
138+
139+
// no changes
140+
if (existingTeam.name === name && existingTeam.slug === slug) {
141+
return existingTeam;
142+
}
143+
144+
if (!!name) {
145+
if (name.length > 32) {
146+
throw new ResponseError(ErrorCodes.INVALID_VALUE, "The name must be between 1 and 32 characters long");
147+
}
148+
existingTeam.name = name;
149+
}
150+
if (!!slug) {
151+
if (slug.length > 100) {
152+
throw new ResponseError(ErrorCodes.INVALID_VALUE, "Slug must be between 1 and 100 characters long");
153+
}
154+
existingTeam.slug = slug;
133155
}
134-
existingTeam.name = name;
156+
135157
return teamRepo.save(existingTeam);
136158
}
137159

@@ -145,8 +167,17 @@ export class TeamDBImpl implements TeamDB {
145167
"Please choose a name containing only letters, numbers, -, _, ', or spaces.",
146168
);
147169
}
170+
const slug = name.toLocaleLowerCase().replace(/[^a-zA-Z0-9]+/g, "-") + "-" + randomBytes(10).toString("hex");
171+
if (blocklist.indexOf(slug) !== -1) {
172+
throw new ResponseError(ErrorCodes.BAD_REQUEST, "Creating a team with this name is not allowed");
173+
}
148174

149175
const teamRepo = await this.getTeamRepo();
176+
const existingTeam = await teamRepo.findOne({ slug, deleted: false, markedDeleted: false });
177+
if (!!existingTeam) {
178+
throw new ResponseError(ErrorCodes.CONFLICT, "A team with this name already exists");
179+
}
180+
150181
const team: Team = {
151182
id: uuidv4(),
152183
name,

components/gitpod-protocol/src/gitpod-service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
174174

175175
// Teams
176176
getTeam(teamId: string): Promise<Team>;
177-
updateTeam(teamId: string, team: Pick<Team, "name">): Promise<Team>;
177+
updateTeam(teamId: string, team: Pick<Team, "name" | "slug">): Promise<Team>;
178178
getTeams(): Promise<Team[]>;
179179
getTeamMembers(teamId: string): Promise<TeamMemberInfo[]>;
180180
createTeam(name: string): Promise<Team>;

components/gitpod-protocol/src/teams-projects-protocol.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ export type Team = Organization;
135135
export interface Organization {
136136
id: string;
137137
name: string;
138+
slug?: string;
138139
creationTime: string;
139140
markedDeleted?: boolean;
140141
/** This is a flag that triggers the HARD DELETION of this entity */

components/server/src/workspace/gitpod-server-impl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2246,7 +2246,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
22462246
return team;
22472247
}
22482248

2249-
public async updateTeam(ctx: TraceContext, teamId: string, team: Pick<Team, "name">): Promise<Team> {
2249+
public async updateTeam(ctx: TraceContext, teamId: string, team: Pick<Team, "name" | "slug">): Promise<Team> {
22502250
traceAPIParams(ctx, { teamId });
22512251
this.checkUser("updateTeam");
22522252

0 commit comments

Comments
 (0)