Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,5 @@ export const PUT = defineInternalJsonRoute({
errorPolicy: internalOrchestrationErrorPolicy,
mapInput: ({ params, body }) => ({ organizationId: params.id, ...body }),
useCase: updateOrganizationAccountWorkspaceAccess,
present: ({ revision, workspaceIds }) => ({ revision, workspaceIds }),
present: ({ revision, grants }) => ({ revision, grants }),
})
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('organization settings navigation', () => {

it('uses Sources for administration when Search is available', () => {
expect(organizationSettingsNavigation(true, enterprise, available)).toEqual(
ORGANIZATION_SETTINGS_ITEMS.filter(({ id }) => id !== 'connected-accounts')
ORGANIZATION_SETTINGS_ITEMS
)
expect(
organizationSettingsNavigation(true, enterprise, available).find(
Expand Down
3 changes: 1 addition & 2 deletions apps/sim/app/o/[organizationId]/settings/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ export function organizationSettingsNavigation(
) {
return ORGANIZATION_SETTINGS_ITEMS.filter(
(item) =>
(item.id !== 'connected-accounts' ||
(availability.connectedAccounts && !availability.search)) &&
(item.id !== 'connected-accounts' || availability.connectedAccounts) &&
((item.id !== 'search-mcp' && item.id !== 'search-slack' && item.id !== 'integrations') ||
availability.search) &&
resolveOrganizationSectionAccess({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('unified settings navigation', () => {
{ id: 'organization', label: 'Members', section: 'organization' },
{ id: 'usage', label: 'Insights', section: 'organization' },
{ id: 'secrets', label: 'Secrets', section: 'workspace' },
{ id: 'connected-accounts', label: 'Connected accounts', section: 'organization' },
{ id: 'connected-accounts', label: 'Credential Groups', section: 'organization' },
{ id: 'custom-tools', label: 'Custom tools', section: 'workspace' },
{ id: 'mcp', label: 'MCP tools', section: 'workspace' },
{ id: 'apikeys', label: 'Sim API keys', section: 'workspace' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ describe('workspace SettingsSidebar organization rollout', () => {
expect(workspaceLink('billing')).toHaveTextContent('Subscription')
expect(workspaceLink('usage')).toHaveTextContent('Insights')
expect(workspaceLink('sso')).toHaveTextContent('Single sign-on')
expect(workspaceLink('connected-accounts')).toHaveTextContent('Connected accounts')
expect(workspaceLink('connected-accounts')).toHaveTextContent('Credential Groups')
expect(container.querySelector('a[href^="/o/"]')).toBeNull()
expectWorkspaceLinks()
}
Expand Down Expand Up @@ -240,7 +240,10 @@ describe('workspace SettingsSidebar organization rollout', () => {
expect(links).toHaveLength(1)
expect(links[0]).toHaveAttribute('href', '/o/host-org/settings/members')
expect(links[0]).toHaveTextContent('Organization')
for (const section of ['organization', 'billing', 'usage', 'sso', 'connected-accounts']) {
if (role === 'admin')
expect(workspaceLink('connected-accounts')).toHaveTextContent('Credential Groups')
else expect(workspaceLink('connected-accounts')).toBeNull()
for (const section of ['organization', 'billing', 'usage', 'sso']) {
expect(workspaceLink(section)).toBeNull()
}
expectWorkspaceLinks()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,7 @@ export function SettingsSidebar({
return Boolean(
hostContext.hostOrganizationId &&
isOrgAdminOrOwner &&
hostContext.features?.credentialGroups &&
!hostContext.features?.organizationSearch
hostContext.features?.credentialGroups
)
}
if (
Expand Down
8 changes: 4 additions & 4 deletions apps/sim/components/settings/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -509,11 +509,11 @@ export const SETTINGS_SECTION_REGISTRY: readonly SettingsSectionRegistryEntry[]
},
},
{
label: 'Connected accounts',
label: 'Credential Groups',
Comment thread
TheodoreSpeaks marked this conversation as resolved.
icon: GridOffset,
unified: {
id: 'connected-accounts',
description: 'Manage accounts shared with your organization’s workflows.',
description: 'Manage integrations and workspace access for workflows and Chat.',
group: 'organization',
order: 1,
organizationSection: 'connected-accounts',
Expand Down Expand Up @@ -913,8 +913,8 @@ export const ORGANIZATION_SETTINGS_ITEMS: SettingsNavigationItem<OrganizationSet
if (id === 'connected-accounts') {
return {
id,
label: 'Connected accounts',
description: 'Manage accounts shared with your organization’s workflows.',
label: 'Credential Groups',
description: 'Manage integrations and workspace access for workflows and Chat.',
icon: GridOffset,
group,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ vi.mock('@/ee/credential-groups/components/organization-account-people', () => (
OrganizationAccountPeople: () => null,
}))
vi.mock('@/ee/credential-groups/components/organization-account-workspace-access', () => ({
OrganizationAccountWorkspaceAccess: () => null,
OrganizationAccountWorkspaceAccess: () => <div data-testid='workspace-access'>Workspaces</div>,
}))

import { OrganizationAccountProviders } from '@/ee/credential-groups/components/organization-account-providers'
Expand Down Expand Up @@ -167,6 +167,33 @@ describe('organization provider configuration UI', () => {
})
}

it('keeps workspace allowlists in the Access tab and preserves the integration controls', async () => {
mocks.accounts.mockReturnValue({
data: {
canManage: true,
credentialGroup: { ...group, options: [gmail] },
availableProviders: ['gmail'],
},
})
await act(async () =>
root.render(
<NuqsTestingAdapter hasMemory>
<OrganizationConnectedAccounts organizationId='org-1' />
</NuqsTestingAdapter>
)
)
expect(container.textContent).toContain('Update configurations')
expect(container.querySelector('[data-testid="workspace-access"]')).toBeNull()
await clickButton('Access')
expect(container.querySelector('[data-testid="workspace-access"]')).not.toBeNull()
expect(container.textContent).not.toContain('Update configurations')
expect(container.querySelector('[role="combobox"]')).toBeNull()
await clickButton('Integrations')
expect(container.textContent).toContain('Update configurations')
expect(container.querySelector('[data-testid="workspace-access"]')).toBeNull()
expect(mocks.update).not.toHaveBeenCalled()
})

it('shows only added providers and searches the remaining catalog', async () => {
await render([], [gmail])
expect(container.textContent).toContain('Gmail')
Expand All @@ -175,7 +202,7 @@ describe('organization provider configuration UI', () => {
expect(container.textContent).not.toMatch(/Ready|Setup required/)
expect(container.textContent).not.toContain('Fireflies')
expect(container.querySelector('[role="radio"]')).toBeNull()
await clickButton('Add provider')
await clickButton('Add integration')
expect(document.querySelector('[aria-label="Add Gmail"]')).toBeNull()
const search = document.querySelector('[aria-label="Search providers"]')
await act(async () => {
Expand All @@ -191,7 +218,7 @@ describe('organization provider configuration UI', () => {

it('adds Fireflies directly without an empty configuration modal', async () => {
await render([])
await clickButton('Add provider')
await clickButton('Add integration')
await clickButton('Add Fireflies')
expect(mocks.add).toHaveBeenCalledWith(
{ organizationId: 'org-1', connectorId: 'fireflies' },
Expand All @@ -208,7 +235,7 @@ describe('organization provider configuration UI', () => {

it('adds Gmail directly without opening indexing configuration', async () => {
await render([])
await clickButton('Add provider')
await clickButton('Add integration')
await clickButton('Add Gmail')
expect(mocks.update).toHaveBeenCalledWith(
{
Expand Down Expand Up @@ -341,7 +368,7 @@ describe('organization provider configuration UI', () => {
it('surfaces an add failure in the catalog and does not open configuration', async () => {
mocks.add.mockImplementation(() => {})
await render([])
await clickButton('Add provider')
await clickButton('Add integration')
await clickButton('Add Fireflies')
mocks.addError = new Error('Could not add Fireflies')
await render([])
Expand Down Expand Up @@ -373,7 +400,7 @@ describe('organization provider configuration UI', () => {
it('returns an unfinished Databricks entry to the catalog until its configuration is saved', async () => {
await render([provider])
expect(container.textContent).not.toContain('Databricks')
await clickButton('Add provider')
await clickButton('Add integration')
await clickButton('Add Databricks')
expect(mocks.add).not.toHaveBeenCalled()
expect(mocks.addAsync).not.toHaveBeenCalled()
Expand All @@ -393,7 +420,7 @@ describe('organization provider configuration UI', () => {

it('cancels Databricks setup without adding a provider', async () => {
await render([])
await clickButton('Add provider')
await clickButton('Add integration')
await clickButton('Add Databricks')
expect(mocks.setup).toHaveBeenCalledWith('org-1', false)
expect(document.querySelector('[role="dialog"]')?.textContent).toContain('Add Databricks')
Expand All @@ -413,7 +440,7 @@ describe('organization provider configuration UI', () => {

it('adds Databricks with its complete configuration in one organization-scoped request', async () => {
await render([])
await clickButton('Add provider')
await clickButton('Add integration')
await clickButton('Add Databricks')
expect(mocks.addAsync).not.toHaveBeenCalled()
await fill('Name', ' Analytics ')
Expand All @@ -437,7 +464,7 @@ describe('organization provider configuration UI', () => {
it('keeps Databricks configuration open after validation fails and allows correction', async () => {
mocks.addAsync.mockRejectedValueOnce(new Error('Enter a valid Databricks MCP URL'))
await render([])
await clickButton('Add provider')
await clickButton('Add integration')
await clickButton('Add Databricks')
await fill('MCP URL', 'https://invalid.example.com/mcp')
await fill('OAuth Client ID', 'client-1')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ export function OrganizationAccountProviders({
return (
<div className='flex flex-col gap-7'>
<SettingsSection
label='Providers'
label='Integrations'
action={
<div className='flex flex-wrap gap-2'>
{options.length > 0 && (
Expand All @@ -177,7 +177,7 @@ export function OrganizationAccountProviders({
setCatalogOpen(true)
}}
>
Add provider
Add integration
</Chip>
</div>
}
Expand Down Expand Up @@ -223,7 +223,7 @@ export function OrganizationAccountProviders({
))}
{!rows.length && (
<SettingsEmptyState variant='inline'>
Add a provider to start connecting accounts.
Add an integration to start connecting accounts.
</SettingsEmptyState>
)}
</div>
Expand Down
Loading
Loading