/* This file is part of GNU Taler (C) 2023-2024 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ /** * Imports. */ import { HttpResponse, readResponseJsonOrErrorCode, readSuccessResponseJsonOrThrow, readTalerErrorResponse, } from "./http-common.js"; import { Codec, HttpStatusCode, TalerError, TalerErrorCode, TalerErrorDetail, } from "./index.js"; type OperationFailWithBodyOrNever = ErrorEnum extends keyof ErrorMap ? OperationFailWithBody : never; export type OperationResult = | OperationOk | OperationAlternative | OperationFail | OperationFailWithBodyOrNever; export function isOperationOk( c: OperationResult, ): c is OperationOk { return c.type === "ok"; } export function isOperationFail( c: OperationResult, ): c is OperationFail { return c.type === "fail"; } /** * successful operation */ export interface OperationOk { type: "ok"; /** * Parsed response body. */ body: BodyT; } /** * unsuccessful operation, see details */ export interface OperationFail { type: "fail"; /** * Error case (either HTTP status code or TalerErrorCode) */ case: T; detail: TalerErrorDetail; } /** * unsuccessful operation, see body */ export interface OperationAlternative { type: "fail"; case: T; body: B; } export interface OperationFailWithBody { type: "fail"; case: keyof B; body: B[OperationFailWithBody["case"]]; } export async function opSuccessFromHttp( resp: HttpResponse, codec: Codec, ): Promise> { const body = await readSuccessResponseJsonOrThrow(resp, codec); return { type: "ok" as const, body }; } /** * Success case, but instead of the body we're returning a fixed response * to the client. */ export function opFixedSuccess(body: T): OperationOk { return { type: "ok" as const, body }; } export function opEmptySuccess(resp: HttpResponse): OperationOk { return { type: "ok" as const, body: void 0 }; } export async function opKnownFailureWithBody( case_: keyof B, body: B[typeof case_], ): Promise> { return { type: "fail", case: case_, body }; } export async function opKnownAlternativeFailure( resp: HttpResponse, s: T, codec: Codec, ): Promise> { const body = (await readResponseJsonOrErrorCode(resp, codec)).response; return { type: "fail", case: s, body }; } export async function opKnownHttpFailure( s: T, resp: HttpResponse, ): Promise> { const detail = await readTalerErrorResponse(resp); return { type: "fail", case: s, detail }; } export function opKnownTalerFailure( s: T, detail: TalerErrorDetail, ): OperationFail { return { type: "fail", case: s, detail }; } export function opUnknownFailure(resp: HttpResponse, error: TalerErrorDetail): never { throw TalerError.fromDetail( TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR, { requestUrl: resp.requestUrl, requestMethod: resp.requestMethod, httpStatusCode: resp.status, errorResponse: error, }, `Unexpected HTTP status ${resp.status} in response`, ); } /** * Convenience function to throw an error if the operation is not a success. */ export function narrowOpSuccessOrThrow( opName: string, opRes: OperationResult, ): asserts opRes is OperationOk { if (opRes.type !== "ok") { throw TalerError.fromDetail( TalerErrorCode.GENERIC_CLIENT_INTERNAL_ERROR, { operation: opName, error: String(opRes.case), detail: "detail" in opRes ? opRes.detail : undefined, }, `Operation ${opName} failed: ${String(opRes.case)}`, ); } } export type ResultByMethod< TT extends object, p extends keyof TT, > = TT[p] extends (...args: any[]) => infer Ret ? Ret extends Promise ? Result extends OperationResult ? Result : never : never //api always use Promises : never; //error cases just for functions export type FailCasesByMethod = Exclude< ResultByMethod, OperationOk >; export type RedirectResult = { redirectURL: URL }