stronger type check to be sure that ErrorDetails is consistent

This commit is contained in:
Sebastian 2023-01-17 15:58:20 -03:00
parent 5be2d128ed
commit eeea3e62a0
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
5 changed files with 84 additions and 29 deletions

View File

@ -32,6 +32,8 @@ import {
TransactionType, TransactionType,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
type empty = Record<string, never>;
export interface DetailsMap { export interface DetailsMap {
[TalerErrorCode.WALLET_PENDING_OPERATION_FAILED]: { [TalerErrorCode.WALLET_PENDING_OPERATION_FAILED]: {
innerError: TalerErrorDetail; innerError: TalerErrorDetail;
@ -44,13 +46,13 @@ export interface DetailsMap {
exchangeProtocolVersion: string; exchangeProtocolVersion: string;
walletProtocolVersion: string; walletProtocolVersion: string;
}; };
[TalerErrorCode.WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK]: {}; [TalerErrorCode.WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK]: empty;
[TalerErrorCode.WALLET_TIPPING_COIN_SIGNATURE_INVALID]: {}; [TalerErrorCode.WALLET_TIPPING_COIN_SIGNATURE_INVALID]: empty;
[TalerErrorCode.WALLET_ORDER_ALREADY_CLAIMED]: { [TalerErrorCode.WALLET_ORDER_ALREADY_CLAIMED]: {
orderId: string; orderId: string;
claimUrl: string; claimUrl: string;
}; };
[TalerErrorCode.WALLET_CONTRACT_TERMS_MALFORMED]: {}; [TalerErrorCode.WALLET_CONTRACT_TERMS_MALFORMED]: empty;
[TalerErrorCode.WALLET_CONTRACT_TERMS_SIGNATURE_INVALID]: { [TalerErrorCode.WALLET_CONTRACT_TERMS_SIGNATURE_INVALID]: {
merchantPub: string; merchantPub: string;
orderId: string; orderId: string;
@ -62,18 +64,46 @@ export interface DetailsMap {
[TalerErrorCode.WALLET_INVALID_TALER_PAY_URI]: { [TalerErrorCode.WALLET_INVALID_TALER_PAY_URI]: {
talerPayUri: string; talerPayUri: string;
}; };
[TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR]: {}; [TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR]: {
[TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION]: {}; requestUrl: string;
[TalerErrorCode.WALLET_BANK_INTEGRATION_PROTOCOL_VERSION_INCOMPATIBLE]: {}; requestMethod: string;
[TalerErrorCode.WALLET_CORE_API_OPERATION_UNKNOWN]: {}; httpStatusCode: number;
[TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED]: {}; errorResponse?: any;
[TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT]: {}; };
[TalerErrorCode.WALLET_NETWORK_ERROR]: {}; [TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION]: {
[TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE]: {}; stack?: string;
[TalerErrorCode.WALLET_EXCHANGE_COIN_SIGNATURE_INVALID]: {}; };
[TalerErrorCode.WALLET_WITHDRAWAL_GROUP_INCOMPLETE]: {}; [TalerErrorCode.WALLET_BANK_INTEGRATION_PROTOCOL_VERSION_INCOMPATIBLE]: {
[TalerErrorCode.WALLET_CORE_NOT_AVAILABLE]: {}; exchangeProtocolVersion: string;
[TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR]: {}; walletProtocolVersion: string;
};
[TalerErrorCode.WALLET_CORE_API_OPERATION_UNKNOWN]: {
operation: string;
};
[TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED]: {
requestUrl: string;
requestMethod: string;
throttleStats: Record<string, unknown>;
};
[TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT]: empty;
[TalerErrorCode.WALLET_NETWORK_ERROR]: {
requestUrl: string;
requestMethod: string;
};
[TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE]: {
requestUrl: string;
requestMethod: string;
httpStatusCode: number;
validationError?: string;
};
[TalerErrorCode.WALLET_EXCHANGE_COIN_SIGNATURE_INVALID]: empty;
[TalerErrorCode.WALLET_WITHDRAWAL_GROUP_INCOMPLETE]: {
errorsPerCoin: Record<number, TalerErrorDetail>;
};
[TalerErrorCode.WALLET_CORE_NOT_AVAILABLE]: empty;
[TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR]: {
httpStatusCode: number;
};
[TalerErrorCode.WALLET_PAY_MERCHANT_SERVER_ERROR]: { [TalerErrorCode.WALLET_PAY_MERCHANT_SERVER_ERROR]: {
requestError: TalerErrorDetail; requestError: TalerErrorDetail;
}; };
@ -84,7 +114,7 @@ export interface DetailsMap {
detail: string; detail: string;
}; };
[TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED]: { [TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED]: {
// FIXME! kycUrl: string;
}; };
[TalerErrorCode.WALLET_DEPOSIT_GROUP_INSUFFICIENT_BALANCE]: { [TalerErrorCode.WALLET_DEPOSIT_GROUP_INSUFFICIENT_BALANCE]: {
insufficientBalanceDetails: PayMerchantInsufficientBalanceDetails; insufficientBalanceDetails: PayMerchantInsufficientBalanceDetails;
@ -94,7 +124,7 @@ export interface DetailsMap {
}; };
} }
type ErrBody<Y> = Y extends keyof DetailsMap ? DetailsMap[Y] : never; type ErrBody<Y> = Y extends keyof DetailsMap ? DetailsMap[Y] : empty;
export function makeErrorDetail<C extends TalerErrorCode>( export function makeErrorDetail<C extends TalerErrorCode>(
code: C, code: C,
@ -133,7 +163,7 @@ function getDefaultHint(code: number): string {
} }
} }
export class TalerProtocolViolationError<T = any> extends Error { export class TalerProtocolViolationError extends Error {
constructor(hint?: string) { constructor(hint?: string) {
let msg: string; let msg: string;
if (hint) { if (hint) {

View File

@ -734,7 +734,7 @@ async function runFirstBackupCycleForProvider(
case OperationAttemptResultType.Error: case OperationAttemptResultType.Error:
throw TalerError.fromDetail( throw TalerError.fromDetail(
TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION, TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION,
resp.errorDetail, resp.errorDetail as any, //FIXME create an error for backup problems
); );
case OperationAttemptResultType.Finished: case OperationAttemptResultType.Finished:
return { return {

View File

@ -68,7 +68,7 @@ export interface HttpRequestOptions {
*/ */
cancellationToken?: CancellationToken; cancellationToken?: CancellationToken;
body?: string | ArrayBuffer | Object; body?: string | ArrayBuffer | Record<string, unknown>;
} }
/** /**
@ -185,6 +185,7 @@ export async function readUnexpectedResponseDetails(
TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR, TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
{ {
requestUrl: httpResponse.requestUrl, requestUrl: httpResponse.requestUrl,
requestMethod: httpResponse.requestMethod,
httpStatusCode: httpResponse.status, httpStatusCode: httpResponse.status,
errorResponse: errJson, errorResponse: errJson,
}, },
@ -211,6 +212,7 @@ export async function readSuccessResponseJsonOrErrorCode<T>(
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE, TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
{ {
requestUrl: httpResponse.requestUrl, requestUrl: httpResponse.requestUrl,
requestMethod: httpResponse.requestMethod,
httpStatusCode: httpResponse.status, httpStatusCode: httpResponse.status,
validationError: e.toString(), validationError: e.toString(),
}, },
@ -223,11 +225,18 @@ export async function readSuccessResponseJsonOrErrorCode<T>(
}; };
} }
type HttpErrorDetails = {
requestUrl: string;
requestMethod: string;
httpStatusCode: number;
};
export function getHttpResponseErrorDetails( export function getHttpResponseErrorDetails(
httpResponse: HttpResponse, httpResponse: HttpResponse,
): Record<string, unknown> { ): HttpErrorDetails {
return { return {
requestUrl: httpResponse.requestUrl, requestUrl: httpResponse.requestUrl,
requestMethod: httpResponse.requestMethod,
httpStatusCode: httpResponse.status, httpStatusCode: httpResponse.status,
}; };
} }
@ -240,6 +249,7 @@ export function throwUnexpectedRequestError(
TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR, TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
{ {
requestUrl: httpResponse.requestUrl, requestUrl: httpResponse.requestUrl,
requestMethod: httpResponse.requestMethod,
httpStatusCode: httpResponse.status, httpStatusCode: httpResponse.status,
errorResponse: talerErrorResponse, errorResponse: talerErrorResponse,
}, },

View File

@ -90,7 +90,8 @@ export class BrowserHttpLib implements HttpRequestLibrary {
TalerError.fromDetail( TalerError.fromDetail(
TalerErrorCode.WALLET_NETWORK_ERROR, TalerErrorCode.WALLET_NETWORK_ERROR,
{ {
requestUrl: requestUrl, requestUrl,
requestMethod,
}, },
"Could not make request", "Could not make request",
), ),
@ -103,7 +104,8 @@ export class BrowserHttpLib implements HttpRequestLibrary {
const exc = TalerError.fromDetail( const exc = TalerError.fromDetail(
TalerErrorCode.WALLET_NETWORK_ERROR, TalerErrorCode.WALLET_NETWORK_ERROR,
{ {
requestUrl: requestUrl, requestUrl,
requestMethod,
}, },
"HTTP request failed (status 0, maybe URI scheme was wrong?)", "HTTP request failed (status 0, maybe URI scheme was wrong?)",
); );
@ -124,7 +126,8 @@ export class BrowserHttpLib implements HttpRequestLibrary {
throw TalerError.fromDetail( throw TalerError.fromDetail(
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE, TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
{ {
requestUrl: requestUrl, requestUrl,
requestMethod,
httpStatusCode: myRequest.status, httpStatusCode: myRequest.status,
}, },
"Invalid JSON from HTTP response", "Invalid JSON from HTTP response",
@ -134,7 +137,8 @@ export class BrowserHttpLib implements HttpRequestLibrary {
throw TalerError.fromDetail( throw TalerError.fromDetail(
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE, TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
{ {
requestUrl: requestUrl, requestUrl,
requestMethod,
httpStatusCode: myRequest.status, httpStatusCode: myRequest.status,
}, },
"Invalid JSON from HTTP response", "Invalid JSON from HTTP response",

View File

@ -104,8 +104,8 @@ export class ServiceWorkerHttpLib implements HttpRequestLibrary {
status: response.status, status: response.status,
requestMethod, requestMethod,
requestUrl, requestUrl,
json: makeJsonHandler(response, requestUrl), json: makeJsonHandler(response, requestUrl, requestMethod),
text: makeTextHandler(response, requestUrl), text: makeTextHandler(response, requestUrl, requestMethod),
bytes: async () => (await response.blob()).arrayBuffer(), bytes: async () => (await response.blob()).arrayBuffer(),
}; };
} catch (e) { } catch (e) {
@ -145,7 +145,11 @@ export class ServiceWorkerHttpLib implements HttpRequestLibrary {
} }
} }
function makeTextHandler(response: Response, requestUrl: string) { function makeTextHandler(
response: Response,
requestUrl: string,
requestMethod: string,
) {
return async function getJsonFromResponse(): Promise<any> { return async function getJsonFromResponse(): Promise<any> {
let respText; let respText;
try { try {
@ -155,6 +159,7 @@ function makeTextHandler(response: Response, requestUrl: string) {
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE, TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
{ {
requestUrl, requestUrl,
requestMethod,
httpStatusCode: response.status, httpStatusCode: response.status,
}, },
"Invalid JSON from HTTP response", "Invalid JSON from HTTP response",
@ -164,7 +169,11 @@ function makeTextHandler(response: Response, requestUrl: string) {
}; };
} }
function makeJsonHandler(response: Response, requestUrl: string) { function makeJsonHandler(
response: Response,
requestUrl: string,
requestMethod: string,
) {
return async function getJsonFromResponse(): Promise<any> { return async function getJsonFromResponse(): Promise<any> {
let responseJson; let responseJson;
try { try {
@ -174,6 +183,7 @@ function makeJsonHandler(response: Response, requestUrl: string) {
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE, TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
{ {
requestUrl, requestUrl,
requestMethod,
httpStatusCode: response.status, httpStatusCode: response.status,
}, },
"Invalid JSON from HTTP response", "Invalid JSON from HTTP response",
@ -184,6 +194,7 @@ function makeJsonHandler(response: Response, requestUrl: string) {
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE, TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
{ {
requestUrl, requestUrl,
requestMethod,
httpStatusCode: response.status, httpStatusCode: response.status,
}, },
"Invalid JSON from HTTP response", "Invalid JSON from HTTP response",