Skip to content

Commit 379bd5a

Browse files
committed
fix(integrations): fail closed on the catalog until the deployment allowlist loads
1 parent 5f7d6c9 commit 379bd5a

3 files changed

Lines changed: 52 additions & 15 deletions

File tree

‎apps/sim/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail.tsx‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,14 @@ interface IntegrationBlockDetailProps {
6868
* Integration detail page, shown only when the viewer's integration policy
6969
* permits the integration. A direct link to a restricted one renders an
7070
* access-required state instead of the connect actions, and the check fails
71-
* closed until the deployment allowlist has loaded.
71+
* closed until both the permission group and the deployment allowlist have
72+
* loaded.
7273
*/
7374
export function IntegrationBlockDetail({ integration, workspaceId }: IntegrationBlockDetailProps) {
7475
const {
7576
isBlockPermitted,
7677
isBlockRequestable,
78+
isPermissionReady,
7779
isIntegrationAvailabilityReady,
7880
integrationAvailabilityError,
7981
refetchIntegrationAvailability,
@@ -90,7 +92,7 @@ export function IntegrationBlockDetail({ integration, workspaceId }: Integration
9092
/>
9193
)
9294
}
93-
if (!isIntegrationAvailabilityReady) {
95+
if (!isPermissionReady || !isIntegrationAvailabilityReady) {
9496
return <IntegrationBlockDetailFallback workspaceId={workspaceId} />
9597
}
9698
if (isBlockPermitted(integration.type)) {

‎apps/sim/app/workspace/[workspaceId]/integrations/components/showcase-with-explore/showcase-with-explore.tsx‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,16 @@ export function ShowcaseWithExplore({ prompt }: ShowcaseWithExploreProps) {
2727
const params = useParams()
2828
const router = useRouter()
2929
const { chatEnabled } = useDeploymentShape()
30-
const { isBlockPermitted } = usePermissionConfig()
30+
const { isBlockPermitted, isIntegrationAvailabilityReady } = usePermissionConfig()
3131
const workspaceId = (params?.workspaceId as string) || ''
3232

3333
return (
3434
<div className='relative'>
35-
<IntegrationsShowcase isBlockPermitted={isBlockPermitted} />
35+
<IntegrationsShowcase
36+
isBlockPermitted={(blockType) =>
37+
isIntegrationAvailabilityReady && isBlockPermitted(blockType)
38+
}
39+
/>
3640
{chatEnabled && (
3741
<Chip
3842
active

‎apps/sim/app/workspace/[workspaceId]/integrations/integrations.tsx‎

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ import {
3232
integrationsParsers,
3333
integrationsUrlKeys,
3434
} from '@/app/workspace/[workspaceId]/integrations/search-params'
35-
import { SettingsEmptyState } from '@/app/workspace/[workspaceId]/settings/components/settings-empty-state'
35+
import {
36+
SettingsEmptyState,
37+
SettingsQueryErrorState,
38+
} from '@/app/workspace/[workspaceId]/settings/components/settings-empty-state'
3639
import { SettingsResourceRow } from '@/app/workspace/[workspaceId]/settings/components/settings-resource-row'
3740
import { PermissionAccessBoundary } from '@/ee/access-requests/components/permission-access-boundary'
3841
import { useWorkspaceCredentials, type WorkspaceCredential } from '@/hooks/queries/credentials'
@@ -150,7 +153,14 @@ function IntegrationsContent() {
150153
const scrollContainerRef = useRef<HTMLDivElement>(null)
151154
const params = useParams()
152155
const workspaceId = (params?.workspaceId as string) || ''
153-
const { integrationAvailability, isBlockPermitted } = usePermissionConfig()
156+
const {
157+
integrationAvailability,
158+
isBlockPermitted,
159+
isIntegrationAvailabilityReady,
160+
isIntegrationAvailabilityFetching,
161+
integrationAvailabilityError,
162+
refetchIntegrationAvailability,
163+
} = usePermissionConfig()
154164

155165
const [{ category: urlCategory, search: urlSearchTerm }, setIntegrationFilters] = useQueryStates(
156166
integrationsParsers,
@@ -207,20 +217,30 @@ function IntegrationsContent() {
207217
})
208218
}, [oauthCredentials])
209219

220+
/**
221+
* The catalog lists nothing until the deployment allowlist has loaded, so a
222+
* pending or failed availability request never shows an integration the
223+
* deployment's `ALLOWED_INTEGRATIONS` excludes.
224+
*/
210225
const permittedFeatured = useMemo(
211-
() => FEATURED_INTEGRATIONS.filter((integration) => isBlockPermitted(integration.type)),
212-
[isBlockPermitted]
226+
() =>
227+
isIntegrationAvailabilityReady
228+
? FEATURED_INTEGRATIONS.filter((integration) => isBlockPermitted(integration.type))
229+
: [],
230+
[isIntegrationAvailabilityReady, isBlockPermitted]
213231
)
214232

215233
const permittedCategorySections = useMemo(
216234
() =>
217-
ALL_CATEGORY_SECTIONS.flatMap((section) => {
218-
const integrations = section.integrations.filter((integration) =>
219-
isBlockPermitted(integration.type)
220-
)
221-
return integrations.length > 0 ? [{ label: section.label, integrations }] : []
222-
}),
223-
[isBlockPermitted]
235+
isIntegrationAvailabilityReady
236+
? ALL_CATEGORY_SECTIONS.flatMap((section) => {
237+
const integrations = section.integrations.filter((integration) =>
238+
isBlockPermitted(integration.type)
239+
)
240+
return integrations.length > 0 ? [{ label: section.label, integrations }] : []
241+
})
242+
: [],
243+
[isIntegrationAvailabilityReady, isBlockPermitted]
224244
)
225245

226246
const setSelectedCategory = useCallback(
@@ -321,6 +341,7 @@ function IntegrationsContent() {
321341
])
322342

323343
const showNoResults =
344+
isIntegrationAvailabilityReady &&
324345
Boolean(urlSearchTerm.trim() || !isAllCategorySelected) &&
325346
filteredCategorySections.length === 0 &&
326347
visibleConnectedItems.length === 0
@@ -380,6 +401,16 @@ function IntegrationsContent() {
380401
</IntegrationSection>
381402
)}
382403

404+
{integrationAvailabilityError && (
405+
<SettingsQueryErrorState
406+
variant='inline'
407+
error={integrationAvailabilityError}
408+
fallback='Unable to load integrations'
409+
isRetrying={isIntegrationAvailabilityFetching}
410+
onRetry={() => void refetchIntegrationAvailability()}
411+
/>
412+
)}
413+
383414
{filteredCategorySections.map((section) => (
384415
<IntegrationSection key={section.label} label={formatIntegrationType(section.label)}>
385416
{section.integrations.map((integration) => {

0 commit comments

Comments
 (0)