From a08a6383dac89eaf2b3bb1027331aec2b830d4c5 Mon Sep 17 00:00:00 2001 From: vindard <17693119+vindard@users.noreply.github.com> Date: Tue, 20 Feb 2024 01:11:18 -0400 Subject: [PATCH] refactor(core): consume 'ProcessPendingInvoiceResult' in exhaustive switch --- core/api/src/app/errors.ts | 4 -- .../wallets/decline-single-pending-invoice.ts | 23 ++++--- core/api/src/app/wallets/errors.ts | 6 -- core/api/src/app/wallets/index.types.d.ts | 18 ++--- .../wallets/process-pending-invoice-result.ts | 65 +++++++++---------- .../wallets/update-single-pending-invoice.ts | 30 +++++---- core/api/src/graphql/error-map.ts | 2 - 7 files changed, 68 insertions(+), 80 deletions(-) delete mode 100644 core/api/src/app/wallets/errors.ts diff --git a/core/api/src/app/errors.ts b/core/api/src/app/errors.ts index 1759b49eb24..35207841f82 100644 --- a/core/api/src/app/errors.ts +++ b/core/api/src/app/errors.ts @@ -27,8 +27,6 @@ import * as KratosErrors from "@/services/kratos/errors" import * as BriaEventErrors from "@/services/bria/errors" import * as SvixErrors from "@/services/svix/errors" -import * as WalletErrors from "@/app/wallets/errors" - export const ApplicationErrors = { ...SharedErrors, ...DomainErrors, @@ -58,6 +56,4 @@ export const ApplicationErrors = { ...LedgerFacadeErrors, ...BriaEventErrors, ...SvixErrors, - - ...WalletErrors, } as const diff --git a/core/api/src/app/wallets/decline-single-pending-invoice.ts b/core/api/src/app/wallets/decline-single-pending-invoice.ts index 9e0cf5a0dce..96ae92996a0 100644 --- a/core/api/src/app/wallets/decline-single-pending-invoice.ts +++ b/core/api/src/app/wallets/decline-single-pending-invoice.ts @@ -1,10 +1,9 @@ import { ProcessPendingInvoiceResult, + ProcessPendingInvoiceResultType, ProcessedReason, } from "./process-pending-invoice-result" -import { InvalidInvoiceProcessingStateError } from "./errors" - import { InvoiceNotFoundError } from "@/domain/bitcoin/lightning" import { InvalidNonHodlInvoiceError } from "@/domain/errors" @@ -15,6 +14,10 @@ import { LndService } from "@/services/lnd" import { elapsedSinceTimestamp } from "@/utils" +const assertUnreachable = (x: never): never => { + throw new Error(`This should never compile with ${x}`) +} + export const declineHeldInvoice = wrapAsyncToRunInSpan({ namespace: "app.invoices", fnName: "declineHeldInvoice", @@ -42,8 +45,8 @@ export const declineHeldInvoice = wrapAsyncToRunInSpan({ const walletInvoices = WalletInvoicesRepository() let marked: WalletInvoiceWithOptionalLnInvoice | RepositoryError - switch (true) { - case result.markProcessedAsCanceledOrExpired(): + switch (result.type) { + case ProcessPendingInvoiceResultType.MarkProcessedAsCanceledOrExpired: marked = await walletInvoices.markAsProcessingCompleted(paymentHash) if (marked instanceof Error) { pendingInvoiceLogger.error("Unable to mark invoice as processingCompleted") @@ -51,14 +54,16 @@ export const declineHeldInvoice = wrapAsyncToRunInSpan({ } return true - case result.reason() === ProcessedReason.InvoiceNotPaidYet: - return true + case ProcessPendingInvoiceResultType.Error: + return result.error - case !!result.error(): - return result.error() as ApplicationError + case ProcessPendingInvoiceResultType.MarkProcessedAsPaid: + case ProcessPendingInvoiceResultType.MarkProcessedAsPaidWithError: + case ProcessPendingInvoiceResultType.ReasonInvoiceNotPaidYet: + return true default: - return new InvalidInvoiceProcessingStateError(JSON.stringify(result._state())) + return assertUnreachable(result) } }, }) diff --git a/core/api/src/app/wallets/errors.ts b/core/api/src/app/wallets/errors.ts deleted file mode 100644 index 855f3c5f69f..00000000000 --- a/core/api/src/app/wallets/errors.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { DomainError, ErrorLevel } from "@/domain/shared" - -export class InvoiceProcessingError extends DomainError {} -export class InvalidInvoiceProcessingStateError extends InvoiceProcessingError { - level = ErrorLevel.Critical -} diff --git a/core/api/src/app/wallets/index.types.d.ts b/core/api/src/app/wallets/index.types.d.ts index a30293e65e7..6c3ea40b1be 100644 --- a/core/api/src/app/wallets/index.types.d.ts +++ b/core/api/src/app/wallets/index.types.d.ts @@ -135,29 +135,29 @@ type LnurlPaymentSendArgs = { type ProcessedReason = (typeof import("./process-pending-invoice-result").ProcessedReason)[keyof typeof import("./process-pending-invoice-result").ProcessedReason] -type ProcessPendingInvoiceResultState = +type ProcessPendingInvoiceResultType = + (typeof import("./process-pending-invoice-result").ProcessPendingInvoiceResultType)[keyof typeof import("./process-pending-invoice-result").ProcessPendingInvoiceResultType] + +type ProcessPendingInvoiceResult = | { + type: "markProcessedAsPaid" markProcessedAsPaid: true } | { + type: "markProcessedAsPaidWithError" markProcessedAsPaid: true error: ApplicationError } | { + type: "markProcessedAsCanceledOrExpired" markProcessedAsCanceledOrExpired: true reason: ProcessedReason } | { + type: "reasonInvoiceNotPaidYet" reason: "InvoiceNotPaidYet" } | { + type: "error" error: ApplicationError } - -type ProcessPendingInvoiceResult = { - _state: () => ProcessPendingInvoiceResultState - markProcessedAsCanceledOrExpired: () => boolean - markProcessedAsPaid: () => boolean - reason: () => ProcessedReason | false - error: () => false | ApplicationError -} diff --git a/core/api/src/app/wallets/process-pending-invoice-result.ts b/core/api/src/app/wallets/process-pending-invoice-result.ts index 2eb269bba7d..b10b79150e8 100644 --- a/core/api/src/app/wallets/process-pending-invoice-result.ts +++ b/core/api/src/app/wallets/process-pending-invoice-result.ts @@ -4,42 +4,35 @@ export const ProcessedReason = { InvoiceNotPaidYet: "InvoiceNotPaidYet", } as const -const wrapper = ( - state: ProcessPendingInvoiceResultState, -): ProcessPendingInvoiceResult => { - return { - _state: () => state, - markProcessedAsCanceledOrExpired: () => - "markProcessedAsCanceledOrExpired" in state && - state.markProcessedAsCanceledOrExpired, - markProcessedAsPaid: () => - "markProcessedAsPaid" in state && state.markProcessedAsPaid, - error: () => ("error" in state && state.error ? state.error : false), - reason: () => ("reason" in state && state.reason ? state.reason : false), - } -} +export const ProcessPendingInvoiceResultType = { + MarkProcessedAsPaidWithError: "markProcessedAsPaidWithError", + MarkProcessedAsPaid: "markProcessedAsPaid", + MarkProcessedAsCanceledOrExpired: "markProcessedAsCanceledOrExpired", + ReasonInvoiceNotPaidYet: "reasonInvoiceNotPaidYet", + Error: "error", +} as const export const ProcessPendingInvoiceResult = { - processAsPaid: (): ProcessPendingInvoiceResult => - wrapper({ - markProcessedAsPaid: true, - }), - processAsPaidWithError: (error: ApplicationError): ProcessPendingInvoiceResult => - wrapper({ - markProcessedAsPaid: true, - error, - }), - processAsCanceledOrExpired: (reason: ProcessedReason): ProcessPendingInvoiceResult => - wrapper({ - markProcessedAsCanceledOrExpired: true, - reason, - }), - notPaid: (): ProcessPendingInvoiceResult => - wrapper({ - reason: ProcessedReason.InvoiceNotPaidYet, - }), - err: (error: ApplicationError): ProcessPendingInvoiceResult => - wrapper({ - error, - }), + processAsPaid: (): ProcessPendingInvoiceResult => ({ + type: ProcessPendingInvoiceResultType.MarkProcessedAsPaid, + markProcessedAsPaid: true, + }), + processAsPaidWithError: (error: ApplicationError): ProcessPendingInvoiceResult => ({ + type: ProcessPendingInvoiceResultType.MarkProcessedAsPaidWithError, + markProcessedAsPaid: true, + error, + }), + processAsCanceledOrExpired: (reason: ProcessedReason): ProcessPendingInvoiceResult => ({ + type: ProcessPendingInvoiceResultType.MarkProcessedAsCanceledOrExpired, + markProcessedAsCanceledOrExpired: true, + reason, + }), + notPaid: (): ProcessPendingInvoiceResult => ({ + type: ProcessPendingInvoiceResultType.ReasonInvoiceNotPaidYet, + reason: ProcessedReason.InvoiceNotPaidYet, + }), + err: (error: ApplicationError): ProcessPendingInvoiceResult => ({ + type: ProcessPendingInvoiceResultType.Error, + error, + }), } diff --git a/core/api/src/app/wallets/update-single-pending-invoice.ts b/core/api/src/app/wallets/update-single-pending-invoice.ts index 9001547597d..44a7f7683ed 100644 --- a/core/api/src/app/wallets/update-single-pending-invoice.ts +++ b/core/api/src/app/wallets/update-single-pending-invoice.ts @@ -4,11 +4,10 @@ import { processPendingInvoiceForDecline } from "./decline-single-pending-invoic import { ProcessPendingInvoiceResult, + ProcessPendingInvoiceResultType, ProcessedReason, } from "./process-pending-invoice-result" -import { InvalidInvoiceProcessingStateError } from "./errors" - import { removeDeviceTokens } from "@/app/users/remove-device-tokens" import { getCurrentPriceAsDisplayPriceRatio, usdFromBtcMidPriceFn } from "@/app/prices" @@ -40,6 +39,10 @@ import { NotificationsService } from "@/services/notifications" import { toDisplayBaseAmount } from "@/domain/payments" import { LockServiceError } from "@/domain/lock" +const assertUnreachable = (x: never): never => { + throw new Error(`This should never compile with ${x}`) +} + export const updatePendingInvoice = wrapAsyncToRunInSpan({ namespace: "app.invoices", fnName: "updatePendingInvoice", @@ -68,11 +71,9 @@ export const updatePendingInvoice = wrapAsyncToRunInSpan({ const walletInvoices = WalletInvoicesRepository() let marked: WalletInvoiceWithOptionalLnInvoice | RepositoryError - switch (true) { - case walletInvoiceBeforeProcessing.paid: - return true - - case result.markProcessedAsCanceledOrExpired(): + if (walletInvoiceBeforeProcessing.paid) return true + switch (result.type) { + case ProcessPendingInvoiceResultType.MarkProcessedAsCanceledOrExpired: marked = await walletInvoices.markAsProcessingCompleted(paymentHash) if (marked instanceof Error) { pendingInvoiceLogger.error("Unable to mark invoice as processingCompleted") @@ -80,7 +81,8 @@ export const updatePendingInvoice = wrapAsyncToRunInSpan({ } return true - case result.markProcessedAsPaid(): + case ProcessPendingInvoiceResultType.MarkProcessedAsPaid: + case ProcessPendingInvoiceResultType.MarkProcessedAsPaidWithError: marked = await walletInvoices.markAsPaid(paymentHash) if ( marked instanceof Error && @@ -88,16 +90,16 @@ export const updatePendingInvoice = wrapAsyncToRunInSpan({ ) { return marked } - return result.error() || true + return "error" in result ? result.error : true - case result.reason() === ProcessedReason.InvoiceNotPaidYet: - return true + case ProcessPendingInvoiceResultType.Error: + return result.error - case !!result.error(): - return result.error() as ApplicationError + case ProcessPendingInvoiceResultType.ReasonInvoiceNotPaidYet: + return true default: - return new InvalidInvoiceProcessingStateError(JSON.stringify(result._state())) + return assertUnreachable(result) } }, }) diff --git a/core/api/src/graphql/error-map.ts b/core/api/src/graphql/error-map.ts index f788cd7c91a..216d8be0f5d 100644 --- a/core/api/src/graphql/error-map.ts +++ b/core/api/src/graphql/error-map.ts @@ -707,8 +707,6 @@ export const mapError = (error: ApplicationError): CustomGraphQLError => { case "InvalidErrorCodeForPhoneMetadataError": case "InvalidCountryCodeForPhoneMetadataError": case "MultipleWalletsFoundForAccountIdAndCurrency": - case "InvoiceProcessingError": - case "InvalidInvoiceProcessingStateError": message = `Unexpected error occurred, please try again or contact support if it persists (code: ${ error.name }${error.message ? ": " + error.message : ""})`