Skip to content

Commit d41d8f1

Browse files
committed
fix(gitlab): make update-invitation access level optional
Update Invitation now uses a dedicated dropdown that defaults to 'Leave unchanged', so updating only the expiration no longer silently resets the invitation's access level to Developer. The level is sent only when explicitly chosen.
1 parent 5bf4943 commit d41d8f1

2 files changed

Lines changed: 56 additions & 10 deletions

File tree

apps/sim/blocks/blocks/gitlab.test.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,21 +115,36 @@ describe('GitLabBlock access operations', () => {
115115
expect(untouched?.admin).toBeUndefined()
116116
})
117117

118-
it('exposes the access-level dropdown for update invitation and coerces it', () => {
119-
const accessLevel = block.subBlocks.find((s) => s.id === 'accessLevel')
120-
const condition = accessLevel?.condition
121-
const ops = condition && 'value' in condition ? condition.value : undefined
122-
expect(ops).toContain('gitlab_update_invitation')
118+
it('exposes an optional access-level dropdown for update invitation that defaults to unchanged', () => {
119+
const invAccess = block.subBlocks.find((s) => s.id === 'invitationAccessLevel')
120+
expect(invAccess?.type).toBe('dropdown')
121+
expect(invAccess?.value?.()).toBe('')
122+
const options = typeof invAccess?.options === 'function' ? undefined : invAccess?.options
123+
expect(options?.[0]).toEqual({ label: 'Leave unchanged', id: '' })
123124

124-
const params = block.tools.config.params?.({
125+
// Updating only the expiration must NOT send an access level (no silent reset).
126+
const expiryOnly = block.tools.config.params?.({
125127
accessToken: 'pat',
126128
operation: 'gitlab_update_invitation',
127129
resourceType: 'group',
128130
resourceId: '42',
129131
email: 'a@b.com',
130-
accessLevel: '40',
132+
expiresAt: '2027-01-01',
133+
invitationAccessLevel: '',
134+
})
135+
expect(expiryOnly).toMatchObject({ email: 'a@b.com', expiresAt: '2027-01-01' })
136+
expect(expiryOnly?.accessLevel).toBeUndefined()
137+
138+
// Choosing a level sends the coerced integer.
139+
const withLevel = block.tools.config.params?.({
140+
accessToken: 'pat',
141+
operation: 'gitlab_update_invitation',
142+
resourceType: 'group',
143+
resourceId: '42',
144+
email: 'a@b.com',
145+
invitationAccessLevel: '40',
131146
})
132-
expect(params).toMatchObject({ email: 'a@b.com', accessLevel: 40 })
147+
expect(withLevel).toMatchObject({ email: 'a@b.com', accessLevel: 40 })
133148
})
134149

135150
it('throws when required access fields are missing', () => {

apps/sim/blocks/blocks/gitlab.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ const ACCESS_LEVEL_OPS = [
4747
'gitlab_add_member',
4848
'gitlab_update_member',
4949
'gitlab_invite_member',
50-
'gitlab_update_invitation',
5150
'gitlab_approve_access_request',
5251
'gitlab_add_saml_group_link',
5352
]
@@ -933,6 +932,30 @@ Return ONLY the commit message - no explanations, no extra text.`,
933932
value: ACCESS_LEVEL_OPS,
934933
},
935934
},
935+
// Optional access level for Update Invitation. Defaults to "Leave unchanged"
936+
// so updating only the expiration does not silently reset the access level.
937+
{
938+
id: 'invitationAccessLevel',
939+
title: 'Access Level',
940+
type: 'dropdown',
941+
options: [
942+
{ label: 'Leave unchanged', id: '' },
943+
{ label: 'No access', id: '0' },
944+
{ label: 'Minimal Access', id: '5' },
945+
{ label: 'Guest', id: '10' },
946+
{ label: 'Planner', id: '15' },
947+
{ label: 'Reporter', id: '20' },
948+
{ label: 'Security Manager', id: '25' },
949+
{ label: 'Developer', id: '30' },
950+
{ label: 'Maintainer', id: '40' },
951+
{ label: 'Owner', id: '50' },
952+
],
953+
value: () => '',
954+
condition: {
955+
field: 'operation',
956+
value: ['gitlab_update_invitation'],
957+
},
958+
},
936959
// Access expiration date (first-class time-boxed grants)
937960
{
938961
id: 'expiresAt',
@@ -1699,7 +1722,11 @@ Return ONLY the commit message - no explanations, no extra text.`,
16991722
resourceType: params.resourceType || 'project',
17001723
resourceId: params.resourceId.trim(),
17011724
email: params.email.trim(),
1702-
accessLevel: params.accessLevel ? Number(params.accessLevel) : undefined,
1725+
// Only send access_level when a level is chosen; "Leave unchanged"
1726+
// ('') keeps the invitation's current level instead of resetting it.
1727+
accessLevel: params.invitationAccessLevel
1728+
? Number(params.invitationAccessLevel)
1729+
: undefined,
17031730
expiresAt: params.expiresAt?.trim() || undefined,
17041731
}
17051732

@@ -1919,6 +1946,10 @@ Return ONLY the commit message - no explanations, no extra text.`,
19191946
groupId: { type: 'string', description: 'Group ID or URL-encoded path' },
19201947
userId: { type: 'number', description: 'Target user ID' },
19211948
accessLevel: { type: 'number', description: 'GitLab access level (10-50)' },
1949+
invitationAccessLevel: {
1950+
type: 'string',
1951+
description: 'Optional new access level for an invitation ("" leaves it unchanged)',
1952+
},
19221953
expiresAt: { type: 'string', description: 'Access expiration date (YYYY-MM-DD)' },
19231954
memberRoleId: { type: 'number', description: 'Custom member role ID (Ultimate)' },
19241955
email: { type: 'string', description: 'Email address for invitations' },

0 commit comments

Comments
 (0)