Skip to content

Commit 6a0397a

Browse files
authored
Merge pull request #2537 from trycompai/fix/cloud-reconnect-oauth-clear
fix(cloud): clear reconnect warning after successful OAuth reconnect
2 parents 48c5fe5 + 4faab40 commit 6a0397a

7 files changed

Lines changed: 61 additions & 0 deletions

File tree

apps/api/src/cloud-security/cloud-security-query.service.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export interface CloudProvider {
1717
status: string;
1818
createdAt: Date;
1919
updatedAt: Date;
20+
reconnectedAt?: Date;
2021
isLegacy: boolean;
2122
variables: Record<string, unknown> | null;
2223
requiredVariables: string[];
@@ -96,6 +97,12 @@ export class CloudSecurityQueryService {
9697
const newProviders: CloudProvider[] = newConnections.map((conn) => {
9798
const metadata = (conn.metadata || {}) as Record<string, unknown>;
9899
const manifest = getManifest(conn.provider.slug);
100+
const reconnectMarker = metadata.reconnectedAt;
101+
const reconnectedAt =
102+
typeof reconnectMarker === 'string' &&
103+
!Number.isNaN(new Date(reconnectMarker).getTime())
104+
? new Date(reconnectMarker)
105+
: undefined;
99106
return {
100107
id: conn.id,
101108
integrationId: conn.provider.slug,
@@ -109,6 +116,7 @@ export class CloudSecurityQueryService {
109116
status: conn.status,
110117
createdAt: conn.createdAt,
111118
updatedAt: conn.updatedAt,
119+
reconnectedAt,
112120
isLegacy: false,
113121
variables: (conn.variables as Record<string, unknown>) ?? null,
114122
requiredVariables: getRequiredVariables(conn.provider.slug),

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,23 @@ export class OAuthController {
315315

316316
// Store tokens and mark connection as active
317317
await this.credentialVaultService.storeOAuthTokens(connection.id, tokens);
318+
319+
// Mark cloud OAuth reconnect completion so reconnect banners clear after successful OAuth.
320+
if (manifest.category === 'Cloud') {
321+
const metadata =
322+
connection.metadata &&
323+
typeof connection.metadata === 'object' &&
324+
!Array.isArray(connection.metadata)
325+
? (connection.metadata as Record<string, unknown>)
326+
: {};
327+
connection = await this.connectionRepository.update(connection.id, {
328+
metadata: {
329+
...metadata,
330+
reconnectedAt: new Date().toISOString(),
331+
},
332+
});
333+
}
334+
318335
await this.connectionService.activateConnection(connection.id);
319336

320337
// Provider-specific post-OAuth actions

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ export function TestsLayout({ initialFindings, initialProviders, orgId }: TestsL
108108
requiresCloudReconnect({
109109
providerId: provider.integrationId,
110110
createdAt: provider.createdAt,
111+
reconnectedAt: provider.reconnectedAt,
111112
isLegacy: provider.isLegacy,
112113
status: provider.status,
113114
}),
@@ -331,6 +332,7 @@ export function TestsLayout({ initialFindings, initialProviders, orgId }: TestsL
331332
requiresCloudReconnect({
332333
providerId: provider.integrationId,
333334
createdAt: provider.createdAt,
335+
reconnectedAt: provider.reconnectedAt,
334336
isLegacy: provider.isLegacy,
335337
status: provider.status,
336338
})

apps/app/src/app/(app)/[orgId]/cloud-tests/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export interface Provider {
2323
status: string;
2424
createdAt: Date;
2525
updatedAt: Date;
26+
reconnectedAt?: Date | string | null;
2627
isLegacy?: boolean;
2728
variables?: Record<string, unknown> | null;
2829
requiredVariables?: string[];

apps/app/src/app/(app)/[orgId]/integrations/[slug]/components/ProviderDetailView.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,12 @@ export function ProviderDetailView({ provider, initialConnections }: ProviderDet
7171
const isCloudProvider = provider.category === 'Cloud';
7272
const selectedConnectionRequiresReconnect = useMemo(() => {
7373
if (!isCloudProvider || !selectedConnection) return false;
74+
const metadata = (selectedConnection.metadata || {}) as Record<string, unknown>;
7475
return requiresCloudReconnect({
7576
providerId: provider.id,
7677
createdAt: selectedConnection.createdAt,
78+
reconnectedAt:
79+
typeof metadata.reconnectedAt === 'string' ? metadata.reconnectedAt : null,
7780
status: selectedConnection.status,
7881
});
7982
}, [isCloudProvider, provider.id, selectedConnection]);

apps/app/src/lib/cloud-reconnect-policy.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,26 @@ describe('requiresCloudReconnect', () => {
7474
}),
7575
).toBe(true);
7676
});
77+
78+
it('returns false when connection was reconnected after cutoff', () => {
79+
expect(
80+
requiresCloudReconnect({
81+
providerId: 'gcp',
82+
createdAt: '2026-04-12T12:00:00.000Z',
83+
reconnectedAt: '2026-04-13T20:00:00.000Z',
84+
status: 'active',
85+
}),
86+
).toBe(false);
87+
});
88+
89+
it('returns true when reconnect marker is before cutoff', () => {
90+
expect(
91+
requiresCloudReconnect({
92+
providerId: 'azure',
93+
createdAt: '2026-04-10T12:00:00.000Z',
94+
reconnectedAt: '2026-04-13T17:00:00.000Z',
95+
status: 'active',
96+
}),
97+
).toBe(true);
98+
});
7799
});

apps/app/src/lib/cloud-reconnect-policy.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const CLOUD_RECONNECT_CUTOFF_MS = new Date(CLOUD_RECONNECT_CUTOFF_ISO_UTC).getTi
1212
type ReconnectCandidate = {
1313
providerId: string;
1414
createdAt?: Date | string | null;
15+
reconnectedAt?: Date | string | null;
1516
isLegacy?: boolean;
1617
status?: string | null;
1718
};
@@ -26,6 +27,13 @@ export function requiresCloudReconnect(candidate: ReconnectCandidate): boolean {
2627
// Legacy cloud connections come from the old integration table and should be re-added.
2728
if (candidate.isLegacy) return true;
2829

30+
if (candidate.reconnectedAt) {
31+
const reconnectedAt = new Date(candidate.reconnectedAt);
32+
if (!Number.isNaN(reconnectedAt.getTime())) {
33+
return reconnectedAt.getTime() < CLOUD_RECONNECT_CUTOFF_MS;
34+
}
35+
}
36+
2937
if (!candidate.createdAt) return false;
3038

3139
const createdAt = new Date(candidate.createdAt);

0 commit comments

Comments
 (0)