Skip to content

Commit 02eca1e

Browse files
committed
fix(search): omit response-only scopes from provider updates
1 parent 65a04f1 commit 02eca1e

5 files changed

Lines changed: 71 additions & 6 deletions

File tree

apps/sim/app/o/[organizationId]/settings/components/integrations/organization-integrations-settings.test.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,6 @@ describe('organization integration invitations', () => {
170170
label: 'Slack',
171171
required: false,
172172
slackBotCredentialId: 'slack-bot',
173-
requiredScopes: ['search:read'],
174173
},
175174
],
176175
},

apps/sim/ee/credential-groups/components/organization-account-providers.test.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,6 @@ describe('organization provider configuration UI', () => {
259259
label: slack.label,
260260
required: slack.required,
261261
slackBotCredentialId: slack.slackBotCredentialId,
262-
requiredScopes: slack.requiredScopes,
263262
},
264263
],
265264
},
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/** @vitest-environment node */
2+
import { describe, expect, it } from 'vitest'
3+
import {
4+
type OrganizationAccountsSettings,
5+
updateOrganizationAccountsContract,
6+
} from '@/lib/api/contracts/organization-accounts'
7+
import { getOrganizationAccountUpdateOptions } from '@/lib/credential-groups/organization-account-options'
8+
import { CREDENTIAL_GROUP_STANDARD_OAUTH_PROVIDER_IDS } from '@/lib/credential-groups/providers'
9+
10+
describe('organization account update options', () => {
11+
it.each([undefined, '12345678-1234-4123-8123-123456789012'])(
12+
'satisfies the update contract with stored Slack scopes and bot %s',
13+
(slackBotCredentialId) => {
14+
const group: NonNullable<OrganizationAccountsSettings['credentialGroup']> = {
15+
id: 'group-1',
16+
workspaceId: null,
17+
organizationId: 'org-1',
18+
name: 'Connected accounts',
19+
description: null,
20+
mcpServers: [],
21+
status: 'active',
22+
createdAt: '2026-01-01T00:00:00Z',
23+
updatedAt: '2026-01-01T00:00:00Z',
24+
options: [
25+
...CREDENTIAL_GROUP_STANDARD_OAUTH_PROVIDER_IDS.map((provider) => ({
26+
id: `${provider}-option`,
27+
provider,
28+
label: provider,
29+
required: false,
30+
status: 'active' as const,
31+
configurationStatus: 'ready' as const,
32+
})),
33+
{
34+
id: 'slack-option',
35+
provider: 'slack',
36+
label: 'Slack',
37+
required: true,
38+
status: 'active',
39+
configurationStatus: 'ready',
40+
slackBotCredentialId,
41+
requiredScopes: ['search:read', 'channels:history'],
42+
},
43+
],
44+
}
45+
46+
const options = getOrganizationAccountUpdateOptions(group)
47+
const result = updateOrganizationAccountsContract.body.parse({ options })
48+
49+
expect(result.options).toEqual(options)
50+
expect(result.options?.map(({ id }) => id)).toEqual(group.options.map(({ id }) => id))
51+
expect(result.options?.at(-1)).toEqual({
52+
id: 'slack-option',
53+
provider: 'slack',
54+
label: 'Slack',
55+
required: true,
56+
slackBotCredentialId,
57+
})
58+
expect(group.options.at(-1)).toHaveProperty('requiredScopes', [
59+
'search:read',
60+
'channels:history',
61+
])
62+
}
63+
)
64+
})

apps/sim/lib/credential-groups/organization-account-options.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type {
33
UpdateOrganizationAccountsBody,
44
} from '@/lib/api/contracts/organization-accounts'
55

6-
/** Preserves option identities and custom Slack scopes while the server refreshes managed OAuth policies. */
6+
/** Keeps option IDs so the server preserves saved scopes when refreshing provider policies. */
77
export function getOrganizationAccountUpdateOptions(
88
group: NonNullable<OrganizationAccountsSettings['credentialGroup']>
99
): NonNullable<UpdateOrganizationAccountsBody['options']> {
@@ -14,7 +14,6 @@ export function getOrganizationAccountUpdateOptions(
1414
...common,
1515
provider: 'slack',
1616
slackBotCredentialId: option.slackBotCredentialId,
17-
requiredScopes: option.requiredScopes,
1817
}
1918
: { ...common, provider: option.provider }
2019
})

apps/sim/lib/credential-groups/service.test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ describe('Credential Group service', () => {
105105
}
106106
)
107107

108-
it('validates provider policy through the active update transaction', async () => {
108+
it('preserves stored scopes while validating provider policy in the update transaction', async () => {
109109
const option = {
110110
id: 'option-1',
111111
provider: 'slack' as const,
@@ -156,8 +156,12 @@ describe('Credential Group service', () => {
156156
})
157157
).resolves.toMatchObject({ id: 'group-1' })
158158

159+
expect(dbChainMockFns.set).toHaveBeenCalledWith(expect.objectContaining({ options: [option] }))
159160
expect(mockGetPolicy).toHaveBeenCalledWith(
160-
expect.objectContaining({ slackBotCredentialId: 'bot-1' }),
161+
expect.objectContaining({
162+
slackBotCredentialId: 'bot-1',
163+
requiredScopes: option.requiredScopes,
164+
}),
161165
{
162166
workspaceId: 'workspace-1',
163167
credentialGroupId: 'group-1',

0 commit comments

Comments
 (0)