Skip to content

Commit 5a7a197

Browse files
committed
feat: event tracking errors
1 parent 5edb414 commit 5a7a197

File tree

6 files changed

+287
-35
lines changed

6 files changed

+287
-35
lines changed

cli/src/commands/demo/command.ts

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
checkExistingOnboarding,
1616
} from './api.js';
1717
import {
18+
captureOnboardingEvent,
1819
checkDockerReadiness,
1920
clearScreen,
2021
getDemoLogPath,
@@ -104,7 +105,17 @@ async function cleanupFederatedGraph(
104105
const deleteResponse = await cleanUpFederatedGraph(client, graphData);
105106

106107
if (deleteResponse.error) {
107-
spinner.fail(`Removing federated graph ${graphData.graph.name} failed.`);
108+
const failText = `Removing federated graph ${graphData.graph.name} failed.`;
109+
spinner.fail(failText);
110+
captureOnboardingEvent({
111+
name: 'onboarding_step_failed',
112+
properties: {
113+
step_name: 'delete_federated_graph',
114+
entry_source: 'wgc',
115+
error_category: 'resource',
116+
error_message: `${failText}\n${deleteResponse.error.message}`,
117+
},
118+
});
108119
console.error(deleteResponse.error.message);
109120

110121
await waitForKeyPress(
@@ -291,7 +302,17 @@ async function handleStep3(
291302
// Delete existing token first (idempotent — no error if missing)
292303
const deleteResult = await deleteRouterToken(tokenParams);
293304
if (deleteResult.error) {
294-
console.error(`Failed to clean up existing router token: ${deleteResult.error.message}`);
305+
const errorText = `Failed to clean up existing router token: ${deleteResult.error.message}`;
306+
console.error(errorText);
307+
captureOnboardingEvent({
308+
name: 'onboarding_step_failed',
309+
properties: {
310+
step_name: 'run_router_send_metrics',
311+
entry_source: 'wgc',
312+
error_category: 'router',
313+
error_message: errorText,
314+
},
315+
});
295316
await waitForKeyPress({ r: retryFn, R: retryFn }, 'Hit [r] to retry. CTRL+C to quit.');
296317
return;
297318
}
@@ -300,7 +321,17 @@ async function handleStep3(
300321
const createResult = await createRouterToken(tokenParams);
301322

302323
if (createResult.error) {
303-
spinner.fail(`Failed to generate router token: ${createResult.error.message}`);
324+
const failText = `Failed to generate router token: ${createResult.error.message}`;
325+
spinner.fail(failText);
326+
captureOnboardingEvent({
327+
name: 'onboarding_step_failed',
328+
properties: {
329+
step_name: 'run_router_send_metrics',
330+
entry_source: 'wgc',
331+
error_category: 'router',
332+
error_message: failText,
333+
},
334+
});
304335
await waitForKeyPress({ r: retryFn, R: retryFn }, 'Hit [r] to retry. CTRL+C to quit.');
305336
return;
306337
}
@@ -340,7 +371,17 @@ async function handleStep3(
340371

341372
firedQueries++;
342373
} catch (err) {
343-
querySpinner.fail(`Sample query failed: ${err instanceof Error ? err.message : String(err)}`);
374+
const failText = `Sample query failed: ${err instanceof Error ? err.message : String(err)}`;
375+
captureOnboardingEvent({
376+
name: 'onboarding_step_failed',
377+
properties: {
378+
step_name: 'run_router_send_metrics',
379+
entry_source: 'wgc',
380+
error_category: 'router',
381+
error_message: failText,
382+
},
383+
});
384+
querySpinner.fail(failText);
344385
}
345386
showQueryPrompt();
346387
}
@@ -360,7 +401,17 @@ async function handleStep3(
360401
});
361402

362403
if (routerResult.error) {
363-
console.error(`\nRouter exited with error: ${routerResult.error.message}`);
404+
const errorText = `Router exited with error: ${routerResult.error.message}`;
405+
console.error(`\n${errorText}`);
406+
captureOnboardingEvent({
407+
name: 'onboarding_step_failed',
408+
properties: {
409+
step_name: 'run_router_send_metrics',
410+
entry_source: 'wgc',
411+
error_category: 'router',
412+
error_message: errorText,
413+
},
414+
});
364415
await waitForKeyPress({ r: retryFn, R: retryFn }, 'Hit [r] to retry. CTRL+C to quit.');
365416
} else {
366417
showQueryPrompt();
@@ -379,14 +430,35 @@ async function handleGetOnboardingResponse(client: BaseCommandOptions['client'],
379430
return onboardingCheck.onboarding;
380431
}
381432
case 'not-allowed': {
382-
program.error('Only organization owners can trigger onboarding.');
433+
const errorText = 'Only organization owners can trigger onboarding.';
434+
captureOnboardingEvent({
435+
name: 'onboarding_step_failed',
436+
properties: {
437+
step_name: 'check_onboarding',
438+
entry_source: 'wgc',
439+
error_category: 'resource',
440+
error_message: errorText,
441+
},
442+
});
443+
program.error(errorText);
383444

384445
break;
385446
}
386447
case 'error': {
387-
console.error('An issue occured while fetching the onboarding status');
448+
const errorText = 'An issue occured while fetching the onboarding status';
449+
console.error(errorText);
388450
console.error(onboardingCheck.error);
389451

452+
captureOnboardingEvent({
453+
name: 'onboarding_step_failed',
454+
properties: {
455+
step_name: 'check_onboarding',
456+
entry_source: 'wgc',
457+
error_category: 'resource',
458+
error_message: `${errorText}\n${onboardingCheck.error}`,
459+
},
460+
});
461+
390462
await waitForKeyPress({ Enter: retryFn }, 'Hit Enter to retry. CTRL+C to quit.');
391463
break;
392464
}
@@ -406,6 +478,15 @@ async function getUserInfo(client: BaseCommandOptions['client']) {
406478

407479
if (error) {
408480
spinner.fail(error.message);
481+
captureOnboardingEvent({
482+
name: 'onboarding_step_failed',
483+
properties: {
484+
step_name: 'init',
485+
entry_source: 'wgc',
486+
error_category: 'resource',
487+
error_message: error.message,
488+
},
489+
});
409490
program.error(error.message);
410491
}
411492

cli/src/commands/demo/util.ts

Lines changed: 101 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,33 @@ export async function prepareSupportingData() {
138138
);
139139
if (!treeResponse.ok) {
140140
spinner.fail('Failed to fetch repository tree.');
141-
program.error(`GitHub API error: ${treeResponse.statusText}`);
141+
const errorText = `GitHub API error: ${treeResponse.statusText}`;
142+
captureOnboardingEvent({
143+
name: 'onboarding_step_failed',
144+
properties: {
145+
step_name: 'init',
146+
entry_source: 'wgc',
147+
error_category: 'support_files',
148+
error_message: errorText,
149+
},
150+
});
151+
program.error(errorText);
142152
}
143153

144154
const parsed = GitHubTreeSchema.safeParse(await treeResponse.json());
145155
if (!parsed.success) {
146156
spinner.fail('Failed to parse repository tree.');
147-
program.error('Unexpected response format from GitHub API. The repository structure may have changed.');
157+
const errorText = 'Unexpected response format from GitHub API. The repository structure may have changed.';
158+
captureOnboardingEvent({
159+
name: 'onboarding_step_failed',
160+
properties: {
161+
step_name: 'init',
162+
entry_source: 'wgc',
163+
error_category: 'support_files',
164+
error_message: errorText,
165+
},
166+
});
167+
program.error(errorText);
148168
}
149169

150170
const files = parsed.data.tree.filter((entry) => entry.type === 'blob' && entry.path.startsWith('plugins/'));
@@ -165,15 +185,29 @@ export async function prepareSupportingData() {
165185

166186
return { path: file.path, error: null };
167187
} catch (err) {
168-
return { path: file.path, error: err instanceof Error ? err.message : String(err) };
188+
return {
189+
path: file.path,
190+
error: err instanceof Error ? err.message : String(err),
191+
};
169192
}
170193
}),
171194
);
172195

173196
const failed = results.filter((r) => r.error !== null);
174197
if (failed.length > 0) {
175-
spinner.fail(`Failed to fetch some files from onboarding repository or store them in ${cosmoDir}.`);
176-
program.error(failed.map((f) => ` ${f.path}: ${f.error}`).join('\n'));
198+
const failText = `Failed to fetch some files from onboarding repository or store them in ${cosmoDir}.`;
199+
const errorText = failed.map((f) => ` ${f.path}: ${f.error}`).join('\n');
200+
captureOnboardingEvent({
201+
name: 'onboarding_step_failed',
202+
properties: {
203+
step_name: 'init',
204+
entry_source: 'wgc',
205+
error_category: 'support_files',
206+
error_message: `${failText}\n${errorText}`,
207+
},
208+
});
209+
spinner.fail(failText);
210+
program.error(errorText);
177211
}
178212

179213
spinner.succeed(`Support files copied to ${pc.bold(cosmoDir)}`);
@@ -227,14 +261,34 @@ export async function checkDockerReadiness(): Promise<void> {
227261
const spinner = demoSpinner('Checking Docker availability…').start();
228262

229263
if (!(await isDockerAvailable())) {
230-
spinner.fail('Docker is not available.');
264+
const failText = 'Docker is not available.';
265+
captureOnboardingEvent({
266+
name: 'onboarding_step_failed',
267+
properties: {
268+
step_name: 'init',
269+
entry_source: 'wgc',
270+
error_category: 'docker_readiness',
271+
error_message: failText,
272+
},
273+
});
274+
spinner.fail(failText);
231275
program.error(
232276
`Docker CLI is not installed or the daemon is not running.\nInstall Docker: ${pc.underline('https://docs.docker.com/get-docker/')}`,
233277
);
234278
}
235279

236280
if (!(await isBuildxAvailable())) {
237-
spinner.fail('Docker Buildx is not available.');
281+
const failText = 'Docker Buildx is not available.';
282+
captureOnboardingEvent({
283+
name: 'onboarding_step_failed',
284+
properties: {
285+
step_name: 'init',
286+
entry_source: 'wgc',
287+
error_category: 'docker_readiness',
288+
error_message: failText,
289+
},
290+
});
291+
spinner.fail(failText);
238292
program.error(
239293
`Docker Buildx plugin is required for multi-platform builds.\nSee: ${pc.underline('https://docs.docker.com/build/install-buildx/')}`,
240294
);
@@ -249,9 +303,20 @@ export async function checkDockerReadiness(): Promise<void> {
249303
try {
250304
await createDockerContainerBuilder(config.dockerBuilderName);
251305
} catch (err) {
252-
spinner.fail(`Failed to create buildx builder "${config.dockerBuilderName}".`);
306+
const failText = `Failed to create buildx builder "${config.dockerBuilderName}".`;
307+
const errorText = err instanceof Error ? err.message : String(err);
308+
spinner.fail(failText);
309+
captureOnboardingEvent({
310+
name: 'onboarding_step_failed',
311+
properties: {
312+
step_name: 'init',
313+
entry_source: 'wgc',
314+
error_category: 'docker_readiness',
315+
error_message: `${failText}\n${errorText}`,
316+
},
317+
});
253318
program.error(
254-
`Could not create a docker-container buildx builder: ${err instanceof Error ? err.message : String(err)}\nYou can create one manually: docker buildx create --use --driver docker-container --name ${config.dockerBuilderName}`,
319+
`Could not create a docker-container buildx builder: ${errorText}\nYou can create one manually: docker buildx create --use --driver docker-container --name ${config.dockerBuilderName}`,
255320
);
256321
}
257322

@@ -517,17 +582,32 @@ export async function publishAllPlugins({
517582
export function captureOnboardingEvent({
518583
name,
519584
properties,
520-
}: {
521-
name: 'onboarding_step_completed';
522-
properties: {
523-
step_name:
524-
| 'init'
525-
| 'check_onboarding'
526-
| 'create_federated_graph'
527-
| 'delete_federated_graph'
528-
| 'run_router_send_metrics';
529-
entry_source: 'wgc';
530-
};
531-
}): void {
585+
}:
586+
| {
587+
name: 'onboarding_step_completed';
588+
properties: {
589+
step_name:
590+
| 'init'
591+
| 'check_onboarding'
592+
| 'create_federated_graph'
593+
| 'delete_federated_graph'
594+
| 'run_router_send_metrics';
595+
entry_source: 'wgc';
596+
};
597+
}
598+
| {
599+
name: 'onboarding_step_failed';
600+
properties: {
601+
step_name:
602+
| 'init'
603+
| 'check_onboarding'
604+
| 'create_federated_graph'
605+
| 'delete_federated_graph'
606+
| 'run_router_send_metrics';
607+
entry_source: 'wgc';
608+
error_category: 'resource' | 'support_files' | 'docker_readiness' | 'router';
609+
error_message: string;
610+
};
611+
}): void {
532612
capture(name, properties);
533613
}

studio/src/components/onboarding/step-1.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,19 @@ export const Step1 = () => {
6262
const { mutate, isPending } = useMutation(createOnboarding, {
6363
onSuccess: (d) => {
6464
if (d.response?.code !== EnumStatusCode.OK) {
65+
const description = d.response?.details ?? 'We had issues with storing your data. Please try again.';
6566
toast({
66-
description: d.response?.details ?? 'We had issues with storing your data. Please try again.',
67+
description,
6768
duration: 3000,
6869
});
70+
captureOnboardingEvent(posthog, {
71+
name: 'onboarding_step_failed',
72+
options: {
73+
step_name: 'welcome',
74+
error_category: 'resource',
75+
error_message: description,
76+
},
77+
});
6978
return;
7079
}
7180

@@ -88,10 +97,19 @@ export const Step1 = () => {
8897
router.push('/onboarding/2');
8998
},
9099
onError: (error) => {
100+
const description = error.details.toString() ?? 'We had issues with storing your data. Please try again.';
91101
toast({
92-
description: error.details.toString() ?? 'We had issues with storing your data. Please try again.',
102+
description,
93103
duration: 3000,
94104
});
105+
captureOnboardingEvent(posthog, {
106+
name: 'onboarding_step_failed',
107+
options: {
108+
step_name: 'welcome',
109+
error_category: 'resource',
110+
error_message: description,
111+
},
112+
});
95113
},
96114
});
97115

studio/src/components/onboarding/step-2.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,19 @@ export const Step2 = () => {
141141

142142
const status = getDemoGraphStatus({ data, isPolling: polling.active, isError });
143143

144+
useEffect(() => {
145+
if (status !== 'fail' && status !== 'error') return;
146+
147+
captureOnboardingEvent(posthog, {
148+
name: 'onboarding_step_failed',
149+
options: {
150+
step_name: 'create_graph',
151+
error_category: 'resource',
152+
error_message: status === 'error' ? 'Failed to fetch federated graph data' : 'Demo federated graph not created',
153+
},
154+
});
155+
}, [status, posthog]);
156+
144157
return (
145158
<OnboardingContainer>
146159
<div className="mt-4 flex w-full flex-col gap-6 text-left">

0 commit comments

Comments
 (0)