Skip to content

Commit 6ec807a

Browse files
committed
feat(gitlab): add builtin roles to GitLab
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
1 parent 0b39ff1 commit 6ec807a

10 files changed

Lines changed: 379 additions & 53 deletions

File tree

apps/server/src/utils/hook-wrapper.ts

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,21 @@ const user = {
142142
const projectMember = {
143143
upsert: async (projectId: Project['id'], userId: ProjectMembers['userId']) => {
144144
const project = await getHookProjectInfos(projectId)
145+
const projectStore = dbToObj(await getProjectStore(project.id))
146+
const hookProject = transformToHookProject(project, projectStore)
145147
const store = dbToObj(await getAdminPlugin())
146148

147149
const member = project.members.find(m => m.userId === userId)
148150
if (!member) throw new Error('Member not found')
149151

150152
const memberRoles = project.roles
151153
.filter(role => member.roleIds.includes(role.id))
152-
.map(role => ({ ...role, permissions: role.permissions.toString(), oidcGroup: role.oidcGroup ?? undefined }))
154+
.map(role => ({
155+
...role,
156+
permissions: role.permissions.toString(),
157+
oidcGroup: role.oidcGroup ?? undefined,
158+
project: hookProject,
159+
}))
153160

154161
const payload = {
155162
userId: member.userId,
@@ -163,24 +170,28 @@ const projectMember = {
163170
lastLogin: member.user.lastLogin?.toISOString(),
164171
projectId: project.id,
165172
roles: memberRoles,
166-
project: {
167-
id: project.id,
168-
slug: project.slug,
169-
},
173+
project: hookProject,
170174
}
171175

172176
return hooks.upsertProjectMember.execute(payload, store)
173177
},
174178
delete: async (projectId: Project['id'], userId: ProjectMembers['userId']) => {
175179
const project = await getHookProjectInfos(projectId)
180+
const projectStore = dbToObj(await getProjectStore(project.id))
181+
const hookProject = transformToHookProject(project, projectStore)
176182
const store = dbToObj(await getAdminPlugin())
177183

178184
const member = project.members.find(m => m.userId === userId)
179185
if (!member) throw new Error('Member not found')
180186

181187
const memberRoles = project.roles
182188
.filter(role => member.roleIds.includes(role.id))
183-
.map(role => ({ ...role, permissions: role.permissions.toString(), oidcGroup: role.oidcGroup ?? undefined }))
189+
.map(role => ({
190+
...role,
191+
permissions: role.permissions.toString(),
192+
oidcGroup: role.oidcGroup ?? undefined,
193+
project: hookProject,
194+
}))
184195

185196
const payload = {
186197
userId: member.userId,
@@ -194,10 +205,7 @@ const projectMember = {
194205
lastLogin: member.user.lastLogin?.toISOString(),
195206
projectId: project.id,
196207
roles: memberRoles,
197-
project: {
198-
id: project.id,
199-
slug: project.slug,
200-
},
208+
project: hookProject,
201209
}
202210

203211
return hooks.deleteProjectMember.execute(payload, store)
@@ -209,9 +217,14 @@ const projectRole = {
209217
const role = await getRole(roleId)
210218
if (!role) throw new Error('Role not found')
211219

220+
const project = await getHookProjectInfos(role.projectId)
221+
const projectStore = dbToObj(await getProjectStore(role.projectId))
222+
const hookProject = transformToHookProject(project, projectStore)
223+
212224
const rolePayload = {
213225
...role,
214226
permissions: role.permissions.toString(),
227+
project: hookProject,
215228
}
216229
const store = dbToObj(await getAdminPlugin())
217230
return hooks.upsertProjectRole.execute(rolePayload, store)
@@ -220,9 +233,14 @@ const projectRole = {
220233
const role = await getRole(roleId)
221234
if (!role) throw new Error('Role not found')
222235

236+
const project = await getHookProjectInfos(role.projectId)
237+
const projectStore = dbToObj(await getProjectStore(role.projectId))
238+
const hookProject = transformToHookProject(project, projectStore)
239+
223240
const rolePayload = {
224241
...role,
225242
permissions: role.permissions.toString(),
243+
project: hookProject,
226244
}
227245
const store = dbToObj(await getAdminPlugin())
228246
return hooks.deleteProjectRole.execute(rolePayload, store)
Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
import type { ProjectMember, ProjectRole } from '@cpn-console/shared'
1+
import type { ProjectRole } from './hook-project-role.js'
2+
import type { Project } from './hook-project.js'
23
import type { Hook } from './hook.js'
34
import { createHook } from './hook.js'
45

5-
export type ProjectMemberPayload = ProjectMember & {
6+
export interface ProjectMember {
7+
userId: string
8+
email: string
9+
firstName: string
10+
lastName: string
611
roles: ProjectRole[]
7-
project: {
8-
id: string
9-
slug: string
10-
}
12+
project: Project
1113
}
1214

13-
export const upsertProjectMember: Hook<ProjectMemberPayload> = createHook()
14-
export const deleteProjectMember: Hook<ProjectMemberPayload> = createHook()
15+
export const upsertProjectMember: Hook<ProjectMember> = createHook()
16+
export const deleteProjectMember: Hook<ProjectMember> = createHook()
Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
1-
import type { ProjectRole } from '@cpn-console/shared'
1+
import type { Project } from './hook-project.js'
22
import type { Hook } from './hook.js'
33
import { createHook } from './hook.js'
44

5+
export interface ProjectRole {
6+
id: string
7+
name: string
8+
permissions: string
9+
projectId: string
10+
position: number
11+
type?: string
12+
oidcGroup?: string
13+
project: Project
14+
}
15+
516
export const upsertProjectRole: Hook<ProjectRole> = createHook()
617
export const deleteProjectRole: Hook<ProjectRole> = createHook()

plugins/gitlab/src/class.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createHash } from 'node:crypto'
2-
import { PluginApi, type Project, type UniqueRepo } from '@cpn-console/hooks'
2+
import { PluginApi, type Project, type UniqueRepo, type ProjectMember } from '@cpn-console/hooks'
33
import type { AccessTokenScopes, CommitAction, GroupSchema, GroupStatisticsSchema, MemberSchema, ProjectVariableSchema, VariableSchema } from '@gitbeaker/rest'
44
import type { AllRepositoryTreesOptions, CondensedProjectSchema, Gitlab, PaginationRequestOptions, ProjectSchema, RepositoryFileExpandedSchema, RepositoryTreeSchema } from '@gitbeaker/core'
55
import { AccessLevel } from '@gitbeaker/core'
@@ -234,12 +234,12 @@ export class GitlabZoneApi extends GitlabApi {
234234
}
235235

236236
export class GitlabProjectApi extends GitlabApi {
237-
private project: Project | UniqueRepo
237+
private project: Project | UniqueRepo | ProjectMember['project']
238238
private gitlabGroup: GroupSchema & { statistics: GroupStatisticsSchema } | undefined
239239
private specialRepositories: string[] = [infraAppsRepoName, internalMirrorRepoName]
240240
private zoneApi: GitlabZoneApi
241241

242-
constructor(project: Project | UniqueRepo) {
242+
constructor(project: Project | UniqueRepo | ProjectMember['project']) {
243243
super()
244244
this.project = project
245245
this.api = getApi()
@@ -440,6 +440,11 @@ export class GitlabProjectApi extends GitlabApi {
440440
return this.api.GroupMembers.add(group.id, userId, accessLevel)
441441
}
442442

443+
public async editGroupMember(userId: number, accessLevel: AccessLevelAllowed = AccessLevel.DEVELOPER): Promise<MemberSchema> {
444+
const group = await this.getOrCreateProjectGroup()
445+
return this.api.GroupMembers.edit(group.id, userId, accessLevel)
446+
}
447+
443448
public async removeGroupMember(userId: number) {
444449
const group = await this.getOrCreateProjectGroup()
445450
return this.api.GroupMembers.remove(group.id, userId)

0 commit comments

Comments
 (0)