/*
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,
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";
httpResp: HttpResponse;
/**
* Parsed response body.
*/
body: BodyT;
}
/**
* unsuccessful operation, see details
*/
export interface OperationFail {
type: "fail";
httpResp: HttpResponse;
/**
* Error case (either HTTP status code or TalerErrorCode)
*/
case: T;
detail: TalerErrorDetail;
}
/**
* unsuccessful operation, see body
*/
export interface OperationAlternative {
type: "fail";
httpResp: HttpResponse;
case: T;
body: B;
}
export interface OperationFailWithBody {
type: "fail";
httpResp: HttpResponse;
case: keyof B;
body: B[OperationFailWithBody["case"]];
}
export async function opSuccess(
resp: HttpResponse,
codec: Codec,
): Promise> {
const body = await readSuccessResponseJsonOrThrow(resp, codec);
return { type: "ok" as const, body, httpResp: resp };
}
/**
* Success case, but instead of the body we're returning a fixed response
* to the client.
*/
export function opFixedSuccess(resp: HttpResponse, body: T): OperationOk {
return { type: "ok" as const, body, httpResp: resp };
}
export function opEmptySuccess(resp: HttpResponse): OperationOk {
return { type: "ok" as const, body: void 0, httpResp: resp };
}
export async function opKnownFailureWithBody(
case_: keyof B,
body: B[typeof case_],
): Promise> {
return { type: "fail", case: case_, body, httpResp: {} as any };
}
export async function opKnownAlternativeFailure(
resp: HttpResponse,
s: T,
codec: Codec,
): Promise> {
const body = await readSuccessResponseJsonOrThrow(resp, codec);
return { type: "fail", case: s, body, httpResp: resp };
}
export async function opKnownHttpFailure(
s: T,
resp: HttpResponse,
): Promise> {
const detail = await readTalerErrorResponse(resp);
return { type: "fail", case: s, detail, httpResp: resp };
}
export async function opKnownTalerFailure(
s: T,
resp: HttpResponse,
): Promise> {
const detail = await readTalerErrorResponse(resp);
return { type: "fail", case: s, detail, httpResp: resp };
}
export function opUnknownFailure(resp: HttpResponse, text: string): never {
throw TalerError.fromDetail(
TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
{
requestUrl: resp.requestUrl,
requestMethod: resp.requestMethod,
httpStatusCode: resp.status,
errorResponse: text,
},
`Unexpected HTTP status ${resp.status} in response`,
);
}
/**
* Convenience function to throw an error if the operation is not a success.
*/
export function narrowOpSuccessOrThrow(
opRes: OperationResult,
): asserts opRes is OperationOk {
const httpResponse = opRes.httpResp;
if (opRes.type !== "ok") {
throw TalerError.fromDetail(
TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
{
requestUrl: httpResponse.requestUrl,
requestMethod: httpResponse.requestMethod,
httpStatusCode: httpResponse.status,
errorResponse:
"detail" in opRes
? opRes.detail
: "body" in opRes
? opRes.body
: undefined,
},
`Unexpected HTTP status ${httpResponse.status} in response`,
);
}
}
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
>;