Skip to content

Commit 3a764a7

Browse files
committed
fix(gitlab): review round 1 — dedicated no-default access level for update member, expiration clearing via explicit empty string
1 parent 18d23cb commit 3a764a7

4 files changed

Lines changed: 42 additions & 9 deletions

File tree

apps/docs/content/docs/en/integrations/gitlab.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@ Update a member's access level in a GitLab project or group
859859
| `resourceId` | string | Yes | Project or group ID or path \(e.g. mygroup/myproject\) |
860860
| `userId` | number | Yes | The ID of the member to update |
861861
| `accessLevel` | number | Yes | New access level: 0 \(No access\), 5 \(Minimal\), 10 \(Guest\), 15 \(Planner\), 20 \(Reporter\), 25 \(Security Manager\), 30 \(Developer\), 40 \(Maintainer\), 50 \(Owner\) |
862-
| `expiresAt` | string | No | Access expiration date in YYYY-MM-DD format |
862+
| `expiresAt` | string | No | Access expiration date in YYYY-MM-DD format. Pass an empty string to clear an existing expiration. |
863863
| `memberRoleId` | number | No | Custom member role ID \(GitLab Ultimate only\). Warning: when omitted, GitLab removes any custom role the member currently holds. |
864864

865865
#### Output

apps/sim/blocks/blocks/gitlab.ts

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,13 @@ const USER_ID_OPS = [
4242
'gitlab_delete_user_identity',
4343
]
4444

45-
/** Operations that take a named access-level dropdown (required unless noted). */
45+
/**
46+
* Operations that take the shared access-level dropdown (required unless
47+
* noted). Update Member deliberately has its own dropdown without a default so
48+
* an expiry-only edit cannot silently reset a Maintainer/Owner to Developer.
49+
*/
4650
const ACCESS_LEVEL_OPS = [
4751
'gitlab_add_member',
48-
'gitlab_update_member',
4952
'gitlab_invite_member',
5053
'gitlab_approve_access_request',
5154
'gitlab_add_saml_group_link',
@@ -54,7 +57,6 @@ const ACCESS_LEVEL_OPS = [
5457
/** Operations where the access level is required (approve/invitation update are optional). */
5558
const ACCESS_LEVEL_REQUIRED_OPS = [
5659
'gitlab_add_member',
57-
'gitlab_update_member',
5860
'gitlab_invite_member',
5961
'gitlab_add_saml_group_link',
6062
]
@@ -1033,6 +1035,30 @@ Return ONLY the commit message - no explanations, no extra text.`,
10331035
value: ACCESS_LEVEL_OPS,
10341036
},
10351037
},
1038+
// Access level for Update Member. Required by GitLab's edit-member API, but
1039+
// deliberately has NO default: the user must explicitly pick the level so an
1040+
// expiry-only edit can't silently downgrade a Maintainer/Owner to Developer.
1041+
{
1042+
id: 'memberAccessLevel',
1043+
title: 'Access Level',
1044+
type: 'dropdown',
1045+
options: [
1046+
{ label: 'No access', id: '0' },
1047+
{ label: 'Minimal Access', id: '5' },
1048+
{ label: 'Guest', id: '10' },
1049+
{ label: 'Planner', id: '15' },
1050+
{ label: 'Reporter', id: '20' },
1051+
{ label: 'Security Manager', id: '25' },
1052+
{ label: 'Developer', id: '30' },
1053+
{ label: 'Maintainer', id: '40' },
1054+
{ label: 'Owner', id: '50' },
1055+
],
1056+
required: true,
1057+
condition: {
1058+
field: 'operation',
1059+
value: ['gitlab_update_member'],
1060+
},
1061+
},
10361062
// Optional access level for Update Invitation. Defaults to "Leave unchanged"
10371063
// so updating only the expiration does not silently reset the access level.
10381064
{
@@ -1818,15 +1844,15 @@ Return ONLY the commit message - no explanations, no extra text.`,
18181844
}
18191845

18201846
case 'gitlab_update_member':
1821-
if (!params.resourceId?.trim() || !params.userId || !params.accessLevel) {
1847+
if (!params.resourceId?.trim() || !params.userId || !params.memberAccessLevel) {
18221848
throw new Error('Project / Group ID, User ID, and Access Level are required.')
18231849
}
18241850
return {
18251851
...baseParams,
18261852
resourceType: params.resourceType || 'project',
18271853
resourceId: params.resourceId.trim(),
18281854
userId: Number(params.userId),
1829-
accessLevel: Number(params.accessLevel),
1855+
accessLevel: Number(params.memberAccessLevel),
18301856
expiresAt: params.expiresAt?.trim() || undefined,
18311857
memberRoleId: params.memberRoleId ? Number(params.memberRoleId) : undefined,
18321858
}
@@ -2130,6 +2156,10 @@ Return ONLY the commit message - no explanations, no extra text.`,
21302156
groupId: { type: 'string', description: 'Group ID or URL-encoded path' },
21312157
userId: { type: 'number', description: 'Target user ID' },
21322158
accessLevel: { type: 'number', description: 'GitLab access level (10-50)' },
2159+
memberAccessLevel: {
2160+
type: 'string',
2161+
description: 'Access level for member updates (explicit choice, no default)',
2162+
},
21332163
invitationAccessLevel: {
21342164
type: 'string',
21352165
description: 'Optional new access level for an invitation ("" leaves it unchanged)',

apps/sim/tools/gitlab/update_invitation.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ export const gitlabUpdateInvitationTool: ToolConfig<
7676
const body: Record<string, unknown> = {}
7777

7878
if (params.accessLevel !== undefined) body.access_level = params.accessLevel
79-
if (params.expiresAt) body.expires_at = params.expiresAt
79+
// Send explicit empty string too, which clears an existing expiration.
80+
if (params.expiresAt !== undefined) body.expires_at = params.expiresAt
8081

8182
return body
8283
},

apps/sim/tools/gitlab/update_member.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ export const gitlabUpdateMemberTool: ToolConfig<
5353
type: 'string',
5454
required: false,
5555
visibility: 'user-or-llm',
56-
description: 'Access expiration date in YYYY-MM-DD format',
56+
description:
57+
'Access expiration date in YYYY-MM-DD format. Pass an empty string to clear an existing expiration.',
5758
},
5859
memberRoleId: {
5960
type: 'number',
@@ -79,7 +80,8 @@ export const gitlabUpdateMemberTool: ToolConfig<
7980
access_level: params.accessLevel,
8081
}
8182

82-
if (params.expiresAt) body.expires_at = params.expiresAt
83+
// Send explicit empty string too, which clears an existing expiration.
84+
if (params.expiresAt !== undefined) body.expires_at = params.expiresAt
8385
if (params.memberRoleId !== undefined) body.member_role_id = params.memberRoleId
8486

8587
return body

0 commit comments

Comments
 (0)