diff --git a/e2e/playwright/point-click.spec.ts b/e2e/playwright/point-click.spec.ts index 76fbc60c1..35ab4fcc5 100644 --- a/e2e/playwright/point-click.spec.ts +++ b/e2e/playwright/point-click.spec.ts @@ -1078,7 +1078,7 @@ sketch002 = startSketchOn('XZ') await page.waitForTimeout(500) await cmdBar.progressCmdBar() await expect( - page.getByText('Unable to sweep with the provided selection') + page.getByText('Unable to sweep with the current selection. Reason:') ).toBeVisible() }) }) @@ -1846,7 +1846,7 @@ sweep001 = sweep({ path = sketch002 }, sketch001) await page.waitForTimeout(500) await cmdBar.progressCmdBar() await expect( - page.getByText('Unable to shell with the provided selection') + page.getByText('Unable to shell with the current selection. Reason:') ).toBeVisible() await page.waitForTimeout(1000) }) diff --git a/src/lang/std/engineConnection.ts b/src/lang/std/engineConnection.ts index 35fbe2b87..9ce37c119 100644 --- a/src/lang/std/engineConnection.ts +++ b/src/lang/std/engineConnection.ts @@ -1999,7 +1999,7 @@ export class EngineCommandManager extends EventTarget { .catch((e) => { // TODO: Previously was never caught, we are not rejecting these pendingCommands but this needs to be handled at some point. /*noop*/ - return null + return e }) } /** diff --git a/src/lib/commandBarConfigs/validators.test.ts b/src/lib/commandBarConfigs/validators.test.ts new file mode 100644 index 000000000..4862ab5a6 --- /dev/null +++ b/src/lib/commandBarConfigs/validators.test.ts @@ -0,0 +1,19 @@ +import { parseEngineErrorMessage } from './validators' + +describe('parseEngineErrorMessage', () => { + it('takes an engine error string and parses its json message', () => { + const engineError = + 'engine error: [{"error_code":"internal_engine","message":"Trajectory curve must be G1 continuous (with continuous tangents)"}]' + const message = parseEngineErrorMessage(engineError) + expect(message).toEqual( + 'Trajectory curve must be G1 continuous (with continuous tangents)' + ) + }) + + it('retuns undefined on strings with different formats', () => { + const s1 = 'engine error: []' + const s2 = 'blabla' + expect(parseEngineErrorMessage(s1)).toBeUndefined() + expect(parseEngineErrorMessage(s2)).toBeUndefined() + }) +}) diff --git a/src/lib/commandBarConfigs/validators.ts b/src/lib/commandBarConfigs/validators.ts index 2d87294aa..f478e584e 100644 --- a/src/lib/commandBarConfigs/validators.ts +++ b/src/lib/commandBarConfigs/validators.ts @@ -3,6 +3,7 @@ import { engineCommandManager } from 'lib/singletons' import { uuidv4 } from 'lib/utils' import { CommandBarContext } from 'machines/commandBarMachine' import { Selections } from 'lib/selections' +import { ApiError_type } from '@kittycad/lib/dist/types/src/models' export const disableDryRunWithRetry = async (numberOfRetries = 3) => { for (let tries = 0; tries < numberOfRetries; tries++) { @@ -46,6 +47,20 @@ function isSelections(selections: unknown): selections is Selections { ) } +export function parseEngineErrorMessage(engineError: string) { + const parts = engineError.split('engine error: ') + if (parts.length < 2) { + return undefined + } + + const errors = JSON.parse(parts[1]) as ApiError_type[] + if (!errors[0]) { + return undefined + } + + return errors[0].message +} + export const revolveAxisValidator = async ({ data, context, @@ -83,7 +98,7 @@ export const revolveAxisValidator = async ({ value: 360, } - const revolveAboutEdgeCommand = async () => { + const command = async () => { return await engineCommandManager.sendSceneCommand({ type: 'modeling_cmd_req', cmd_id: uuidv4(), @@ -96,13 +111,13 @@ export const revolveAxisValidator = async ({ }, }) } - const attemptRevolve = await dryRunWrapper(revolveAboutEdgeCommand) - if (attemptRevolve?.success) { + const result = await dryRunWrapper(command) + if (result?.success) { return true - } else { - // return error message for the toast - return 'Unable to revolve with selected edge' } + + const reason = parseEngineErrorMessage(result) || 'unknown' + return `Unable to revolve with the current selection. Reason: ${reason}` } export const loftValidator = async ({ @@ -128,7 +143,7 @@ export const loftValidator = async ({ return 'Unable to loft, selection contains less than two solid2ds' } - const loftCommand = async () => { + const command = async () => { // TODO: check what to do with these const DEFAULT_V_DEGREE = 2 const DEFAULT_TOLERANCE = 2 @@ -145,13 +160,13 @@ export const loftValidator = async ({ }, }) } - const attempt = await dryRunWrapper(loftCommand) - if (attempt?.success) { + const result = await dryRunWrapper(command) + if (result?.success) { return true - } else { - // return error message for the toast - return 'Unable to loft with selected sketches' } + + const reason = parseEngineErrorMessage(result) || 'unknown' + return `Unable to loft with the current selection. Reason: ${reason}` } export const shellValidator = async ({ @@ -180,7 +195,7 @@ export const shellValidator = async ({ return "Unable to shell, couldn't find the solid" } - const shellCommand = async () => { + const command = async () => { // TODO: figure out something better than an arbitrarily small value const DEFAULT_THICKNESS: Models['LengthUnit_type'] = 1e-9 const DEFAULT_HOLLOW = false @@ -200,12 +215,13 @@ export const shellValidator = async ({ }) } - const attemptShell = await dryRunWrapper(shellCommand) - if (attemptShell?.success) { + const result = await dryRunWrapper(command) + if (result?.success) { return true } - return 'Unable to shell with the provided selection' + const reason = parseEngineErrorMessage(result) || 'unknown' + return `Unable to shell with the current selection. Reason: ${reason}` } export const sweepValidator = async ({ @@ -241,7 +257,7 @@ export const sweepValidator = async ({ } const target = targetArtifact.pathId - const sweepCommand = async () => { + const command = async () => { // TODO: second look on defaults here const DEFAULT_TOLERANCE: Models['LengthUnit_type'] = 1e-7 const DEFAULT_SECTIONAL = false @@ -261,10 +277,11 @@ export const sweepValidator = async ({ }) } - const attemptSweep = await dryRunWrapper(sweepCommand) - if (attemptSweep?.success) { + const result = await dryRunWrapper(command) + if (result?.success) { return true } - return 'Unable to sweep with the provided selection' + const reason = parseEngineErrorMessage(result) || 'unknown' + return `Unable to sweep with the current selection. Reason: ${reason}` }