From 9b47e3f824731e40aefeb3fe6f8a26f32af0de34 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Mon, 27 Jan 2025 12:59:30 +0700 Subject: [PATCH] fix(web): always return error for keyboard stub Cloud API error states Fixes: #13043 Fixes: KEYMAN-COM-1BN Fixes: HELP-KEYMAN-COM-DCA --- .../package-cache/src/cloud/queryEngine.ts | 18 +++++++++--------- .../package-cache/src/keyboardRequisitioner.ts | 5 +++-- .../engine/package-cache/src/keyboardStub.ts | 12 +++++++++++- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/web/src/engine/package-cache/src/cloud/queryEngine.ts b/web/src/engine/package-cache/src/cloud/queryEngine.ts index 3fe0bcc025..2760691124 100644 --- a/web/src/engine/package-cache/src/cloud/queryEngine.ts +++ b/web/src/engine/package-cache/src/cloud/queryEngine.ts @@ -2,7 +2,7 @@ import EventEmitter from 'eventemitter3'; import { PathConfiguration } from 'keyman/engine/paths'; -import { default as KeyboardStub, ErrorStub, KeyboardAPISpec, mergeAndResolveStubPromises } from '../keyboardStub.js'; +import { default as KeyboardStub, ErrorStub, KeyboardAPISpec } from '../keyboardStub.js'; import { LanguageAPIPropertySpec, ManagedPromise, Version } from '@keymanapp/keyboard-processor'; import CloudRequesterInterface from './requesterInterface.js'; @@ -315,7 +315,7 @@ export default class CloudQueryEngine extends EventEmitter { * @param x keyboard name string or keyboard metadata JSON object * @returns resolved or rejected promise with merged array of stubs. */ - async fetchCloudStubs(cloudList: string[]): Promise<(KeyboardStub|ErrorStub)[]> { + async fetchCloudStubs(cloudList: string[]): Promise<(KeyboardStub)[]> { // // Ensure keymanweb is initialized before continuing to add keyboards // if(!this.keymanweb.initialized) { // await this.deferment; @@ -333,17 +333,17 @@ export default class CloudQueryEngine extends EventEmitter { comma=','; } - let errorStubs: ErrorStub[] = []; // Request keyboard metadata from the Keyman Cloud keyboard metadata server try { - let result: (KeyboardStub|ErrorStub)[]|Error = await this.keymanCloudRequest(cmd,false); - return mergeAndResolveStubPromises(result, errorStubs); + return await this.keymanCloudRequest(cmd,false); } catch(err) { // We don't have keyboard info for this ErrorStub - console.error(err); - let stub: ErrorStub = {error: err}; - errorStubs.push(stub); - return Promise.reject(errorStubs); + if(err instanceof Error) { + console.error(`Error for request command-string ${cmd}: ${err.message}\n ${err.stack}`); + } else { + console.error(err); + } + return Promise.reject(err); } } } \ No newline at end of file diff --git a/web/src/engine/package-cache/src/keyboardRequisitioner.ts b/web/src/engine/package-cache/src/keyboardRequisitioner.ts index 7bed7b12ac..eee8cef1ee 100644 --- a/web/src/engine/package-cache/src/keyboardRequisitioner.ts +++ b/web/src/engine/package-cache/src/keyboardRequisitioner.ts @@ -18,6 +18,7 @@ import { toUnprefixedKeyboardId as unprefixed } from "./index.js"; import { default as CloudRequesterInterface } from "./cloud/requesterInterface.js"; +import { rejectErrorStubs } from "./keyboardStub.js"; class CloudRequestEntry { id: string; @@ -282,7 +283,7 @@ export default class KeyboardRequisitioner { if(cmd == '') { // No command so return errors - return Promise.reject(errorStubs); + return rejectErrorStubs(errorStubs); } return this.cloudQueryEngine.keymanCloudRequest('&keyboardid='+cmd, false).then(async (result) => { @@ -300,7 +301,7 @@ export default class KeyboardRequisitioner { console.error(err); let stub: ErrorStub = {error: err}; errorStubs.push(stub); - return Promise.reject(errorStubs); + return rejectErrorStubs(errorStubs); }); } diff --git a/web/src/engine/package-cache/src/keyboardStub.ts b/web/src/engine/package-cache/src/keyboardStub.ts index e36314bf89..41b4f49454 100644 --- a/web/src/engine/package-cache/src/keyboardStub.ts +++ b/web/src/engine/package-cache/src/keyboardStub.ts @@ -205,12 +205,22 @@ export interface ErrorStub { error: Error; } +export function rejectErrorStubs(errorStubs: ErrorStub[]) { + if(errorStubs.length > 1) { + const msgs = errorStubs.map((entry) => `${entry.error.message}:\n ${entry.error.stack ?? ''}`); + return Promise.reject(new Error(msgs.join('\n\n'))); + } else { + // The messages for errors we'd return here should already include data about their triggering request. + return Promise.reject(errorStubs[0].error); + } +} + export function mergeAndResolveStubPromises(keyboardStubs: (KeyboardStub|ErrorStub)[], errorStubs: ErrorStub[]) : Promise<(KeyboardStub|ErrorStub)[]> { if (errorStubs.length == 0) { return Promise.resolve(keyboardStubs); } if (keyboardStubs.length == 0) { - return Promise.reject(errorStubs); + return rejectErrorStubs(errorStubs); } else { // Merge this with errorStubs let result: (KeyboardStub|ErrorStub)[] = keyboardStubs;