fix(web): always return error for keyboard stub Cloud API error states

Fixes: #13043
Fixes: KEYMAN-COM-1BN
Fixes: HELP-KEYMAN-COM-DCA
This commit is contained in:
Joshua Horton 2025-01-27 12:59:30 +07:00
parent ac90d3df42
commit 9b47e3f824
3 changed files with 23 additions and 12 deletions

View file

@ -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<EventMap> {
* @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<EventMap> {
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);
}
}
}

View file

@ -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);
});
}

View file

@ -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;