Skip to content

Commit 6355b9b

Browse files
committed
Merge branch 'main' of https://github.com/trycompai/comp into chas/mbm-policy
2 parents 867af2c + 87ae9df commit 6355b9b

File tree

7 files changed

+345
-79
lines changed

7 files changed

+345
-79
lines changed

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { encrypt } from '@/lib/encryption';
44
import { getIntegrationHandler } from '@comp/integrations';
55
import { db } from '@db';
66
import { revalidatePath } from 'next/cache';
7-
import { headers } from 'next/headers';
7+
import { cookies, headers } from 'next/headers';
88
import { z } from 'zod';
99
import { authActionClient } from '../../../../../actions/safe-action';
1010
import { runTests } from './run-tests';
@@ -100,7 +100,11 @@ export const connectCloudAction = authActionClient
100100
}
101101

102102
// Trigger immediate scan
103-
await runTests();
103+
const runResult = await runTests();
104+
105+
if (runResult.success && runResult.publicAccessToken) {
106+
(await cookies()).set('publicAccessToken', runResult.publicAccessToken);
107+
}
104108

105109
// Revalidate the path
106110
const headersList = await headers();
@@ -110,6 +114,13 @@ export const connectCloudAction = authActionClient
110114

111115
return {
112116
success: true,
117+
trigger: runResult.success
118+
? {
119+
taskId: runResult.taskId ?? undefined,
120+
publicAccessToken: runResult.publicAccessToken ?? undefined,
121+
}
122+
: undefined,
123+
runErrors: runResult.success ? undefined : (runResult.errors ?? undefined),
113124
};
114125
} catch (error) {
115126
console.error('Failed to connect cloud provider:', error);

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

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ interface CloudField {
1818
type?: string;
1919
}
2020

21+
type TriggerInfo = {
22+
taskId?: string;
23+
publicAccessToken?: string;
24+
};
25+
2126
interface CloudConnectionCardProps {
2227
cloudProvider: 'aws' | 'gcp' | 'azure';
2328
name: string;
@@ -27,7 +32,7 @@ interface CloudConnectionCardProps {
2732
guideUrl?: string;
2833
color?: string;
2934
logoUrl?: string;
30-
onSuccess?: () => void;
35+
onSuccess?: (trigger?: TriggerInfo) => void;
3136
}
3237

3338
export function CloudConnectionCard({
@@ -83,7 +88,11 @@ export function CloudConnectionCard({
8388
if (result?.data?.success) {
8489
toast.success(`${name} connected! Running initial scan...`);
8590
setCredentials({});
86-
onSuccess?.();
91+
onSuccess?.(result.data?.trigger);
92+
93+
if (result.data?.runErrors && result.data.runErrors.length > 0) {
94+
toast.error(result.data.runErrors[0] || 'Initial scan reported an issue');
95+
}
8796
} else {
8897
toast.error(result?.data?.error || 'Failed to connect');
8998
}
@@ -99,13 +108,11 @@ export function CloudConnectionCard({
99108
<Card className="rounded-xs">
100109
<CardHeader className="space-y-4">
101110
<div className="flex items-center gap-3">
102-
<div className={`bg-gradient-to-br ${color} flex items-center justify-center rounded-lg p-2`}>
111+
<div
112+
className={`bg-gradient-to-br ${color} flex items-center justify-center rounded-lg p-2`}
113+
>
103114
{logoUrl && (
104-
<img
105-
src={logoUrl}
106-
alt={`${shortName} logo`}
107-
className="h-8 w-8 object-contain"
108-
/>
115+
<img src={logoUrl} alt={`${shortName} logo`} className="h-8 w-8 object-contain" />
109116
)}
110117
</div>
111118
<div>

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,18 @@ const PROVIDER_FIELDS: Record<'aws' | 'gcp' | 'azure', ProviderFieldWithOptions[
117117
],
118118
};
119119

120+
type TriggerInfo = {
121+
taskId?: string;
122+
publicAccessToken?: string;
123+
};
124+
120125
interface EmptyStateProps {
121126
onBack?: () => void;
122127
connectedProviders?: string[];
128+
onConnected?: (trigger?: TriggerInfo) => void;
123129
}
124130

125-
export function EmptyState({ onBack, connectedProviders = [] }: EmptyStateProps = {}) {
131+
export function EmptyState({ onBack, connectedProviders = [], onConnected }: EmptyStateProps = {}) {
126132
const [step, setStep] = useState<Step>('choose');
127133
const [selectedProvider, setSelectedProvider] = useState<CloudProvider>(null);
128134
const [credentials, setCredentials] = useState<Record<string, string>>({});
@@ -224,6 +230,12 @@ export function EmptyState({ onBack, connectedProviders = [] }: EmptyStateProps
224230

225231
if (result?.data?.success) {
226232
setStep('success');
233+
if (result.data?.trigger) {
234+
onConnected?.(result.data.trigger);
235+
}
236+
if (result.data?.runErrors && result.data.runErrors.length > 0) {
237+
toast.error(result.data.runErrors[0] || 'Initial scan reported an issue');
238+
}
227239
// If user already has clouds, automatically return to results after 2 seconds
228240
if (onBack) {
229241
setTimeout(() => {

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

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
import { Button } from '@comp/ui/button';
44
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@comp/ui/select';
55
import { useRealtimeRun } from '@trigger.dev/react-hooks';
6-
import { CheckCircle2, Info, Loader2, RefreshCw, X } from 'lucide-react';
6+
import { AlertTriangle, CheckCircle2, Info, Loader2, RefreshCw, X } from 'lucide-react';
77
import { useEffect, useState } from 'react';
8+
import type { IntegrationRunOutput } from '../types';
89
import { FindingsTable } from './FindingsTable';
910

1011
interface Finding {
@@ -23,6 +24,7 @@ interface ResultsViewProps {
2324
scanAccessToken: string | null;
2425
onRunScan: () => Promise<string | null>;
2526
isScanning: boolean;
27+
runOutput: IntegrationRunOutput | null;
2628
}
2729

2830
const severityOrder = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
@@ -50,6 +52,7 @@ export function ResultsView({
5052
scanAccessToken,
5153
onRunScan,
5254
isScanning,
55+
runOutput,
5356
}: ResultsViewProps) {
5457
// Track scan status with Trigger.dev hooks
5558
const { run } = useRealtimeRun(scanTaskId || '', {
@@ -64,17 +67,28 @@ export function ResultsView({
6467
const [selectedSeverity, setSelectedSeverity] = useState<string>('all');
6568
const [showSuccessBanner, setShowSuccessBanner] = useState(false);
6669
const [showErrorBanner, setShowErrorBanner] = useState(false);
70+
const [showOutputErrorBanner, setShowOutputErrorBanner] = useState(false);
6771

68-
// Show success banner when scan completes, auto-hide after 5 seconds
72+
const runOutputError = runOutput && !runOutput.success ? runOutput : null;
73+
const scanSucceeded = scanCompleted && !runOutputError;
74+
const outputErrorMessages =
75+
runOutputError?.errors && runOutputError.errors.length > 0
76+
? runOutputError.errors
77+
: runOutputError?.failedIntegrations?.map(
78+
(integration) => `${integration.name}: ${integration.error}`,
79+
) ?? [];
80+
81+
// Show success banner when scan completes successfully, auto-hide after 5 seconds
6982
useEffect(() => {
70-
if (scanCompleted) {
83+
if (scanSucceeded) {
7184
setShowSuccessBanner(true);
7285
const timer = setTimeout(() => {
7386
setShowSuccessBanner(false);
7487
}, 5000);
7588
return () => clearTimeout(timer);
7689
}
77-
}, [scanCompleted]);
90+
setShowSuccessBanner(false);
91+
}, [scanSucceeded]);
7892

7993
// Auto-dismiss error banner after 30 seconds
8094
useEffect(() => {
@@ -87,6 +101,16 @@ export function ResultsView({
87101
}
88102
}, [scanFailed]);
89103

104+
// Show output error banner when run completes with errors
105+
useEffect(() => {
106+
if (runOutputError) {
107+
setShowOutputErrorBanner(true);
108+
setShowSuccessBanner(false);
109+
} else {
110+
setShowOutputErrorBanner(false);
111+
}
112+
}, [runOutputError]);
113+
90114
// Get unique statuses and severities
91115
const uniqueStatuses = Array.from(
92116
new Set(findings.map((f) => f.status).filter(Boolean) as string[]),
@@ -146,8 +170,40 @@ export function ResultsView({
146170
</div>
147171
)}
148172

173+
{/* Output error banner when run reports errors but job didn't crash */}
174+
{showOutputErrorBanner && runOutputError && !isScanning && (
175+
<div className="bg-destructive/10 flex items-start gap-3 rounded-lg border border-destructive/20 p-4">
176+
<AlertTriangle className="text-destructive h-5 w-5 flex-shrink-0" />
177+
<div className="flex-1 space-y-1">
178+
<p className="text-destructive text-sm font-medium">Scan completed with errors</p>
179+
<ul className="text-muted-foreground text-xs leading-relaxed">
180+
{outputErrorMessages.slice(0, 5).map((message, index) => (
181+
<li key={`${message}-${index}`}>{message}</li>
182+
))}
183+
{outputErrorMessages.length === 0 && (
184+
<li>Encountered an unknown error while processing integration results.</li>
185+
)}
186+
</ul>
187+
{runOutputError.failedIntegrations && runOutputError.failedIntegrations.length > 0 && (
188+
<p className="text-muted-foreground text-xs">
189+
{runOutputError.failedIntegrations.length} integration
190+
{runOutputError.failedIntegrations.length === 1 ? '' : 's'} returned errors.
191+
</p>
192+
)}
193+
</div>
194+
<Button
195+
variant="ghost"
196+
size="sm"
197+
onClick={() => setShowOutputErrorBanner(false)}
198+
className="text-muted-foreground hover:text-foreground h-auto p-1"
199+
>
200+
<X className="h-4 w-4" />
201+
</Button>
202+
</div>
203+
)}
204+
149205
{/* Propagation delay info banner - only when scan succeeds but returns empty output */}
150-
{scanCompleted && findings.length === 0 && !isScanning && !scanFailed && (
206+
{scanCompleted && findings.length === 0 && !isScanning && !scanFailed && !runOutputError && (
151207
<div className="bg-blue-50 dark:bg-blue-950/20 flex items-center gap-3 rounded-lg border border-blue-200 dark:border-blue-900 p-4">
152208
<Info className="text-blue-600 dark:text-blue-400 h-5 w-5 flex-shrink-0" />
153209
<div className="flex-1">

0 commit comments

Comments
 (0)