Skip to content

Commit 508e205

Browse files
authored
Merge pull request #2560 from trycompai/main
[comp] Production Deploy
2 parents ab622ec + ba6bbb7 commit 508e205

File tree

4 files changed

+70
-18
lines changed

4 files changed

+70
-18
lines changed

apps/api/src/integration-platform/controllers/connections.controller.ts

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -262,28 +262,30 @@ export class ConnectionsController {
262262
}
263263

264264
/**
265-
* List connections for an organization
265+
* List connections for an organization (excludes soft-deleted/disconnected)
266266
*/
267267
@Get()
268268
@RequirePermission('integration', 'read')
269269
async listConnections(@OrganizationId() organizationId: string) {
270270
const connections =
271271
await this.connectionService.getOrganizationConnections(organizationId);
272272

273-
return connections.map((c) => ({
274-
id: c.id,
275-
providerId: c.providerId,
276-
providerSlug: (c as any).provider?.slug,
277-
providerName: (c as any).provider?.name,
278-
status: c.status,
279-
authStrategy: c.authStrategy,
280-
lastSyncAt: c.lastSyncAt,
281-
nextSyncAt: c.nextSyncAt,
282-
errorMessage: c.errorMessage,
283-
variables: c.variables,
284-
metadata: c.metadata,
285-
createdAt: c.createdAt,
286-
}));
273+
return connections
274+
.filter((c) => c.status !== 'disconnected')
275+
.map((c) => ({
276+
id: c.id,
277+
providerId: c.providerId,
278+
providerSlug: (c as any).provider?.slug,
279+
providerName: (c as any).provider?.name,
280+
status: c.status,
281+
authStrategy: c.authStrategy,
282+
lastSyncAt: c.lastSyncAt,
283+
nextSyncAt: c.nextSyncAt,
284+
errorMessage: c.errorMessage,
285+
variables: c.variables,
286+
metadata: c.metadata,
287+
createdAt: c.createdAt,
288+
}));
287289
}
288290

289291
/**
@@ -1178,6 +1180,10 @@ export class ConnectionsController {
11781180
if (Array.isArray(mergedCredentials.regions)) {
11791181
metaUpdates.regions = mergedCredentials.regions;
11801182
}
1183+
// Mark cloud credential updates as reconnections so reconnect banners clear
1184+
if (manifest.category === 'Cloud') {
1185+
metaUpdates.reconnectedAt = new Date().toISOString();
1186+
}
11811187
if (Object.keys(metaUpdates).length > 0) {
11821188
const existingMeta =
11831189
(connection.metadata as Record<string, unknown>) ?? {};

apps/app/src/app/(app)/[orgId]/cloud-tests/components/ProviderTabs.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ interface ProviderTabsProps {
2323
onConnectionTabChange: (providerType: string, connectionId: string) => void;
2424
onRunScan: (connectionId?: string) => Promise<string | null>;
2525
onAddConnection: (providerType: string) => void;
26+
onReconnect: (providerType: string) => void;
2627
onConfigure: (provider: Provider) => void;
2728
needsConfiguration: (provider: Provider) => boolean;
2829
requiresReconnect: (provider: Provider) => boolean;
2930
canRunScan?: boolean;
3031
canAddConnection?: boolean;
32+
isReconnecting?: boolean;
3133
orgId: string;
3234
}
3335

@@ -235,11 +237,13 @@ export function ProviderTabs({
235237
onConnectionTabChange,
236238
onRunScan,
237239
onAddConnection,
240+
onReconnect,
238241
onConfigure,
239242
needsConfiguration,
240243
requiresReconnect,
241244
canRunScan,
242245
canAddConnection,
246+
isReconnecting,
243247
orgId,
244248
}: ProviderTabsProps) {
245249
return (
@@ -328,7 +332,12 @@ export function ProviderTabs({
328332
</p>
329333
</div>
330334
{canAddConnection !== false && (
331-
<Button size="sm" onClick={() => onAddConnection(providerType)}>
335+
<Button
336+
size="sm"
337+
onClick={() => onReconnect(providerType)}
338+
disabled={isReconnecting}
339+
loading={isReconnecting}
340+
>
332341
Reconnect
333342
</Button>
334343
)}

apps/app/src/app/(app)/[orgId]/cloud-tests/components/TestsLayout.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { ConnectIntegrationDialog } from '@/components/integrations/ConnectIntegrationDialog';
44
import { useApi } from '@/hooks/use-api';
5+
import { useIntegrationMutations } from '@/hooks/use-integration-platform';
56
import { usePermissions } from '@/hooks/use-permissions';
67
import { ManageIntegrationDialog } from '@/components/integrations/ManageIntegrationDialog';
78
import { CLOUD_RECONNECT_CUTOFF_LABEL, requiresCloudReconnect } from '@/lib/cloud-reconnect-policy';
@@ -10,6 +11,7 @@ import { Add, Settings } from '@trycompai/design-system/icons';
1011
import { useSearchParams, useRouter } from 'next/navigation';
1112
import { useCallback, useMemo, useState } from 'react';
1213
import { toast } from 'sonner';
14+
import { mutate as globalMutate } from 'swr';
1315
import { isCloudProviderSlug } from '../constants';
1416
import type { Finding, Provider } from '../types';
1517
import { CloudSettingsModal } from './CloudSettingsModal';
@@ -51,9 +53,11 @@ export function TestsLayout({ initialFindings, initialProviders, orgId }: TestsL
5153
const canRunScan = hasPermission('integration', 'update');
5254
const canCreateIntegration = hasPermission('integration', 'create');
5355
const api = useApi();
56+
const { deleteConnection } = useIntegrationMutations();
5457
const [showSettings, setShowSettings] = useState(false);
5558
const [viewingResults, setViewingResults] = useState(true);
5659
const [isScanning, setIsScanning] = useState(false);
60+
const [isReconnecting, setIsReconnecting] = useState(false);
5761
const searchParams = useSearchParams();
5862
const router = useRouter();
5963
const [activeProviderTab, setActiveProviderTabState] = useState<string | null>(
@@ -217,6 +221,37 @@ export function TestsLayout({ initialFindings, initialProviders, orgId }: TestsL
217221
setViewingResults(true);
218222
};
219223

224+
const handleReconnect = async (providerType: string) => {
225+
const providerName = PROVIDER_NAME[providerType] || providerType.toUpperCase();
226+
if (
227+
!confirm(
228+
`This will disconnect all your ${providerName} connections and redirect you to set up a fresh connection. Continue?`,
229+
)
230+
) {
231+
return;
232+
}
233+
234+
setIsReconnecting(true);
235+
try {
236+
const connections = providerGroups[providerType] || [];
237+
await Promise.all(
238+
connections.map((connection) =>
239+
connection.isLegacy
240+
? api.delete(`/v1/cloud-security/legacy/${connection.id}`)
241+
: deleteConnection(connection.id),
242+
),
243+
);
244+
await Promise.all([mutateProviders(), mutateFindings()]);
245+
// Clear integration connections cache so the target page doesn't flash stale data
246+
await globalMutate(['integration-connections', orgId]);
247+
router.push(`/${orgId}/integrations/${providerType}`);
248+
} catch {
249+
toast.error('Failed to disconnect connections. Please try again.');
250+
} finally {
251+
setIsReconnecting(false);
252+
}
253+
};
254+
220255
if (connectedProviders.length === 0 || !viewingResults) {
221256
return (
222257
<EmptyState
@@ -339,6 +374,8 @@ export function TestsLayout({ initialFindings, initialProviders, orgId }: TestsL
339374
}
340375
canRunScan={canRunScan}
341376
canAddConnection={canCreateIntegration}
377+
isReconnecting={isReconnecting}
378+
onReconnect={handleReconnect}
342379
orgId={orgId}
343380
/>
344381

apps/app/src/components/integrations/ConnectIntegrationDialog.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,11 @@ export function ConnectIntegrationDialog({
9595
// since hooks return [] as fallback, not undefined
9696
const isDataLoading = isProvidersLoading || isConnectionsLoading;
9797

98-
// Filter connections for this specific integration
98+
// Filter connections for this specific integration (exclude soft-deleted)
9999
const existingConnections: ExistingConnection[] = useMemo(() => {
100100
if (!allConnections) return [];
101101
return allConnections
102-
.filter((conn) => conn.providerSlug === integrationId)
102+
.filter((conn) => conn.providerSlug === integrationId && conn.status !== 'disconnected')
103103
.map((conn) => {
104104
const metadata = (conn.metadata || {}) as Record<string, unknown>;
105105
return {

0 commit comments

Comments
 (0)